A bug in Anvil's Optimism deposit transaction parser turns a single JSON-RPC call into a process abort, the same class of bug behind grinch, a remote Anvil crash challenge I designed for NNS CTF.

The bug
OP Stack deposit transactions (type 0x7e) carry an optional mint field, a uint256 denominated in wei, representing value bridged from L1 to L2. The type is U256 because that is what the spec says. Anvil's internal representation, however, mirrors op-alloy's TxDeposit, where mint is stored as u128.
Somewhere between the JSON-RPC payload and the in-memory struct, you have to convert and cast the integer. In crates/primitives/src/transaction/optimism.rs, inside get_deposit_tx_parts, that conversion looked like this:
let mint = other
.get_deserialized::<U256>("mint")
.transpose()
.ok()
.flatten()
.unwrap_or_else(|| {
missing.push("mint");
Default::default()
})
.map(|value| value.to::<u128>());The culprit is .to::<u128>(). In ruint, Uint::to::<T>() is a cast that panics on overflow with no no syntactic clue at the call site, just a note in the docs. Sibling methods try_to, wrapping_to, saturating_to, checked_to exist precisely because a 256-bit value generally does not fit in 128 bits.
Sending a 0x7e tx with an oversized mint field:
{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"type": "0x7e",
"from": "0x...",
"to": "0x...",
"mint": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"value":"0x0",
"gas": "0x5208",
"input":"0x"
}],
"id": 1
}aborts the process:
Uint conversion error: Overflow(256, 340282366920938463463374607431768211455, 340282366920938463463374607431768211455)
u128::MAX printed twice, the conversion library's way of reporting the overflow
Root cause
The parser assumed every U256 field had already been bounded by an honest caller valid for synced chain data, where mint is constrained by a real L1 bridge contract. It is not valid for an RPC endpoint accepting arbitrary JSON.
The deposit parser was written for a chain ingest pipeline. It ended up downstream of eth_sendTransaction.
The fix
Swap the panicking conversion for the saturating one. The overflowing mint is silently clamped to u128::MAX.
ruint::Uint::to::<T>() and its safe siblings differ by one word, and the only thing telling them apart at a call site is the reader's memory of the docs.
Generally anywhere U256 decoded from JSON-RPC flows into .to::<u64>() or .to::<u128>(), it will panic.
After patching this one, I spent just a little more time looking for the similar pattern with rg. There were five more in crates/anvil/src/eth/api.rs. That is its own post.