Calculus running on fumes - by Hernán Erasmo
Devil's in the Opcodes
SubscribeSign in
Calculus running on fumes<br>The faint sound of a money printer - Part 3 of 5
Hernán Erasmo<br>Aug 18, 2026
Share
Just in case you’re new to the series, the subject is an unverified arbitrage bot on Arbitrum, one of eight contracts the same operator deployed over about two months, and this part takes apart the instructions its transactions carry.<br>A few things worth knowing before going in: the contract hunts for price discrepancies between trading venues. It has to decide in a fraction of a cent’s worth of computation whether one is worth chasing, and the machine it runs on (the EVM) has integer arithmetic and nothing else: no decimals, no floating point, no math library.<br>Earlier parts cover how the bot was found, how it behaves and how its instructions are encoded, but this one reads on its own.
Part 2 ended at a point where we had decoded the whole calldata grammar, every field of every hop, and I called it the menu on the wall: which pools to touch, which way to trade them, how much edge to demand. Then I said the menu was not the meal. Nothing in the calldata actually decides anything. It never says whether a loop turns a profit, how large a trade to push through it, or whether this candidate deserves a single unit of gas. Those are questions the contract answers on its own, thousands of times per burst, and I closed the part promising the kitchen was where the real work happened.<br>This is the kitchen.<br>I owe you two things from earlier parts, both of which get paid here.<br>In Part 2, I named a header field profitScale. I said I had a pretty good idea of what its purpose was, but admittedly could not fully decipher and promised we would meet it again here.
In Part 1, I claimed this whole strategy only works because the bot can say no to any given opportunity almost for free, but never showed you the machinery that makes saying no actually cheap.
Both are answered at the same place, a single internal routine the disassembly puts at offset 0x67e, the function every probe transaction actually runs. It is the longest and strangest thing in the contract and rather than read it top to bottom, we are going to do what we did with the calldata in Part 2, pull on one loose thread at a time and let the shape come apart in our hands.<br>The following flowchart shows what we know so far, the knowledge we’ve been able to gather from the previous parts of this series. The goal for today is to find out exactly what happens inside the route interpreter at 0x67e and a glimpse beyond.
The cheapest word in the contract: NO
I used cast run --debug to step into each instruction from a given probe transaction (one of many discussed in Part 1), and watched where the calldata pieces went. On a route that ends up not moving any tokens, the contract:<br>Reads two or three pool prices.
Multiplies a few numbers together.
Compares the result against something, and ends
No state changes, no swaps or token transfers. However, let’s take a mental note of this because it becomes relevant later: the transaction does not end with a REVERT, the “something went wrong, undo it all” exit you would expect from a failed attempt. It just stops as if it had succeeded .<br>So the overwhelmingly common path through this function is short: read a few prices, do a little arithmetic, stop. There’s something in there acting as a filter, and to understand the filter we have to start from the beginning, with reading the prices.<br>Turns out that is one of the busiest part of the whole cheap path, because reading a price is only simple if you always deal with a single kind of pool. This contract supports many.<br>Step 1 of 3: read the prices of every pool
Before the contract can decide anything, it needs to know what each pool in the route is charging right this instant. The catch is that “a pool” is not one thing. Back in Part 2 we found the poolType byte tucked into every hop, the single byte that labels a pool as Uniswap V2 style, V3 or Algebra style, or V4. That byte has to exist because these pool designs store their price in completely different shapes, and you cannot read one the way you read another. So the contract forks three ways on that byte, and each branch knows how to talk to exactly one family of pool. Let’s go over them one by one, starting from the simplest.<br>Uniswap V2
A Uniswap V2 pool is basically a jar holding two piles of tokens , say some USDC and some WETH. The pool’s whole design keeps those two piles balanced in value, so the price of one token in terms of the other falls straight out of the pile sizes. If the jar holds 4,000 USDC and 1 WETH, then one WETH is worth about 4,000 USDC, and that is the price.<br>To read it, the contract makes a single call, getReserves(), which hands back the two pile sizes, and divides one by the other. One call, one division. The only decision left is which way up to divide, “WETH per USDC” or “USDC per WETH”, and that is...