
Date: June 20, 2026
Chain: Ethereum
Loss: about $7.5M in WETH, USDC and USDT
Class: token approvals granted to untrusted contracts and never checked or revoked
For three years, jaredfromsubway.eth was the bot every Ethereum trader learned to fear. It watched the public mempool, found trades it could squeeze, and placed its own orders on both sides of them, a move known as a sandwich attack. By most counts it ran roughly 70% of the sandwich attacks on Ethereum, and its gross revenue reportedly topped $34M to $40M in its best three-month stretches.
On June 20, 2026, someone sandwiched it. An attacker built a fake market designed to look exactly like the kind of opportunity the bot was built to chase, let the bot win small amounts of real money in it for about 21 hours, and then took about $7.5M in a single transaction at 18:49:11 UTC. The bot never made a losing trade, and it lost anyway, because it assumed a permission handed out during a trade stopped mattering once the trade was done.
The original bot went live on February 27, 2023, under a name that joked about the former Subway spokesman and about the sandwich attacks it specialized in. The contract that got drained, labelled "jaredfromsubway: MEV Bot 2" on Etherscan at 0x1f2F10D1C40777AE1Da742455c65828FF36Df387, was the operator's second version. The explorer shows it was deployed at 03:02:59 UTC on August 6, 2024, by the wallet 0xD668Af7363FF42B2Ab46acd27511bB811a7Ce7e3, and by June 2026 it was still one of the most gas-hungry addresses on Ethereum, scanning every block for something to front-run.
A bot like this holds its own trading capital, WETH and stablecoins sitting in the contract so it can act inside a single block without waiting for anyone. To trade, it approves other contracts, such as token wrappers and routers, to move some of that capital, then calls them in sequence and checks that it ended with more than it started. It doesn't stop to ask who deployed a token or who owns a pool. It simulates a route, sees a profit, and executes, and during the attack CertiK saw it running hundreds of swaps in a single transaction. That speed is what made it so effective against other traders, and it's what the attacker decided to use against it.
MEV Bot 2 has no verified source code. Neither Etherscan nor Blockscout shows anything for it beyond bytecode, which is normal for a trading bot that doesn't want competitors reading its strategy, so this post describes its logic from CertiK's and Blockaid's analyses and from what the bot did on-chain, and quotes no code.
CertiK found that the attacker deployed four helper contracts, one to create fake tokens, one to create fake pools, one to run swaps, and a coordinator at 0xb84db016324e8F2BFdD8DD9c260338AEE0A8DF52 that tracked status and later ran the extraction. Through the token factory, the attacker deployed 131 fake token contracts presented as restaked versions of WETH and USDC, each minted one for one against the real asset through a function called wrapTo. Blockaid counts 66 of them in the family that mattered, all from the same factory with identical code and names like fWETH, fUSDC and fUSDT, paired with a fake counter token, fCAP, in Uniswap V2 style pools whose prices were arranged to look like an arbitrage.
The trap was in how wrapTo behaved. According to CertiK's reconstruction, as long as a storage slot it calls owner_a_0_19 was unset, wrapTo used transferFrom to pull the bot's real token, which spent the approval the bot had just granted. Once the attacker set that slot to point at the coordinator, wrapTo checked a status value, and if the status had been set in the current block, it skipped the transferFrom and still minted the wrapped token. The bot's approval stayed on the books, unspent, and the coordinator recorded it. Blockaid adds that each bait token carried an owner-only withdraw function, selector 0x51cff8d9, that could later call transferFrom on the bot up to whatever approval was left.
At block 25354425, at 21:49:59 UTC on June 19, the bot traded through the first fake pool. CertiK's numbers show it approving and wrapping about 0.00739 WETH, swapping out about 0.00686 fCAP for about 0.00857 WETH, and walking away with a real profit of about 0.00235 WETH. Because the coordinator wasn't set yet, the approval was spent normally, and nothing in the bot's records looked unusual.
Over the following hours the attacker repeated the pattern at larger sizes, letting the bot win each time. Blockaid tracked the standing approvals per contract climbing from about 0.007 WETH to 0.6, then 8.6, then 10.1, and finally about 92.16 WETH, with 423 approval events across 42 transactions. On one early 0.6 WETH leg, Blockaid shows the bot putting in about 0.602 WETH and getting back about 0.603, a profit of roughly 0.00112 WETH.
Then the attacker set the coordinator through a separate transaction and switched the trap on. From block 25360428, at 17:55:23 UTC on June 20, CertiK found the attacker calling markStatusAndTip with a bribe attached, so its own status update landed in the same block just ahead of the bot's trades. CertiK says only about a dozen of those status updates were needed. In every block where the status matched, the bot's wrapTo calls skipped the transfer, the bot still booked its fractional profit, and its approval to the bait contract stayed open. The last and largest bait leg ran about six blocks before the drain, with six approvals of about 92.16 WETH each.
Granting an approval is a normal part of trading through a contract. When the bot routed a trade, it allowed a wrapper to move its tokens so the swap could happen. An approval stays live until it's used up or revoked, though, and according to CertiK the bot only checked one thing at the end of each trade, whether it had made money, and never verified that the approval had been consumed or revoked what was left. Etherscan's transaction insight put the whole flaw in one line: the bot doesn't check for lingering USDC approvals afterward.
Every trade passed the bot's test, because every trade really was profitable. Meanwhile, the permissions behind those trades kept piling up in contracts the attacker controlled, and no check the bot ran could see them, because they lived in the token contracts' allowance tables, outside the bot's own balance. An approval works like a spare key you hand a contractor for one job, and the job ending doesn't take the key back.
At 18:49:11 UTC on June 20, in block 25360696, the address 0x5aF38735B215b00aa7C9f93fEd7ee415CeCB36e1 called the coordinator at 0xb84d with function selector 0xc269a509. The coordinator switched on the bait contracts all at once, and CertiK found that 60 of the 66 held standing approvals from the bot. Each one used transferFrom to pull the bot's real assets to the attacker wallet at 0x3e37f4A10d771Ba9dE44b6d301410b1BEdeA65d0.
The transaction's token-transfer log on Blockscout shows exactly 50 transfers, all from the bot to 0x3e37, in identical slices. Sixteen moved 92.16 WETH each, twenty moved 143,528.66 USDC each, thirteen moved 149,488.05 USDT each and one moved 92,415.48 USDT, for totals of 1,474.58 WETH, 2,870,573.13 USDC and 2,035,760.16 USDT. The whole sweep used about 1.38 million gas.
Two minutes and 48 seconds later, at 18:51:59 UTC, the attacker wallet sent all 2,870,573.13 USDC through CoW Protocol's settlement contract and received 1,661.75 WETH back, in the transaction 0x4d37...58f9. CertiK puts the full take, with the stablecoins converted, at 4,424 ETH, about $7.5M. The bot made no decision at the moment of the drain. It had already said yes to every one of those transfers, trade by trade, while it believed it was winning.
The operator answered on X with "Well played" and offered a 50% white hat bounty if the ETH came back within 48 hours. According to CryptoPotato, the offers escalated from a $1M reward plus $50,000 for anyone who could help locate the attacker, to a $3M time-sensitive bounty with full confidentiality, to an on-chain message asking for 2,150 ETH, about half the haul, with a warning that the operator would pursue all available legal and law-enforcement remedies after the deadline.
The attacker never answered publicly. CertiK lists the proceeds spreading to five exploiter addresses, with 3,000 ETH deposited to Tornado Cash through 0x139bE50D7c0829E0db6E9A444454517e24AbeE7a. At 23:12:23 UTC on June 22, the wallet 0x7B4F24a4522bc2AEE9D1686FFB7d6c9dAC514e20 wrapped 1,422 ETH, and three and a half minutes later it sold that WETH through CoW Protocol for 2,445,816 DAI. Blockscout shows that DAI moving out of the wallet in several transfers between August 6 and September 23, and the same wallet making a run of 100 ETH deposits into Tornado Cash's router between September 5 and September 23. As of October 20, 2026, it held about 628,216 DAI.
The operator said a self-described white-hat group had made contact, and the bot's owner later set its WETH approval on MEV Bot 2 to zero. We found no public report of any return of funds as of October 20, 2026.
A contract like this one has no fixed list of counterparties. It can trade against any token and any pool anyone deploys, so the number of routes it can take is effectively unlimited, and no team or reviewer can walk through all of them by hand. Olympix reads a contract the way the attacker did, as states, transitions and entry points, and the state the attacker cared about was the table of allowances the bot left standing for other contracts. Olympix would read the bot's own source in the team's repository, which is where it runs, so the missing public source doesn't change the analysis.
To keep the search from drowning in routes, the engine reads signals, patterns in the code that mark one path as worth attacking over another, and Olympix's static analysis surfaces the one here: an approve call to an address supplied by the route, a contract the bot didn't deploy and has never verified, on a path whose only post-condition compares balances before and after and never reads the allowance again. From that signal the engine narrows from every route, to the feasible ones that can grant an approval to an outside address, to the ones that can end with that approval still above zero.
That allowance table is where BugPoCer starts. Its method, Counterexample-Guided Invariant Refutation, begins with the whole contract and the rules it quietly depends on, and for this bot the rule fits in one sentence: once a route finishes, no outside contract should still be able to move the bot's tokens. BugPoCer then drives the bot through routes, including routes through tokens and pools it has never seen, and checks the allowance table after each one until the rule fails.
The rule fails on exactly the route the attacker built. The counterexample would contain a wrapper token whose wrapTo mints without pulling the approved amount, a pool priced so the route shows a small profit, the bot's approve call to that wrapper, the swap sequence that returns more WETH than went in, and the resulting state, a nonzero allowance from the bot to the wrapper after the route ends. It would state the minimal conditions, that the counterparty is attacker-controlled and that the bot's only check is profit, and it would ship with a generated Forge test that replays the profitable route, then has the wrapper call transferFrom on the bot in a later transaction and asserts that the bot's balance drops. That test is the June 20 drain, run in development instead of on mainnet.
The bot's own checks never had a chance here, because each one ran inside a single trade and measured the right outcome for that trade. The danger lived in what carried over between trades, which only shows up when you look at the contract's state over time. Olympix has flagged this same class of leftover allowance in other codebases, and since the engine runs automatically in development, at every merge and before release, it would have surfaced at the merge that introduced the routing logic, before the contract was deployed in August 2024, and again on every change after it.
JaredFromSubway spent three years profiting from traders who sent transactions without thinking about who was watching. It lost $7.5M to the same habit, trusting every route that paid and never asking what it left behind. Any contract that hands out approvals to counterparties it doesn't control carries the same open position until each approval is shown to be spent or zeroed. If your contracts approve tokens to addresses you didn't write, see how Olympix checks what those approvals leave behind and book a technical walkthrough.
Protocol: JaredFromSubway MEV Bot (jaredfromsubway: MEV Bot 2)
Chain: Ethereum
Date: June 20, 2026, 18:49:11 UTC
Loss: 1,474.58 WETH, 2,870,573 USDC and 2,035,760 USDT in the drain tx, 4,424 ETH after swaps, about $7.5M (CertiK)
Victim contract (MEV Bot 2, deployed August 6, 2024): 0x1f2F...f387
MEV Bot 2 deployer: 0xD668...E7e3
Operator (jaredfromsubway.eth): 0xae2F...aE13
Drain tx sender: 0x5aF3...36e1
Coordinator / sweep contract: 0xb84d...DF52
Attacker wallet (received the drain): 0x3e37...65d0
Exploiter address 2: 0x74Dc...0e65
Exploiter address 3: 0x71d4...4e97
Exploiter address 4: 0xd8C1...D845
Exploiter address 5: 0xe3Da...5f17
Tornado Cash depositor (per CertiK): 0x139b...eE7a
DAI holding wallet: 0x7B4F...4e20
First bait interaction (June 19, 21:49:59 UTC): Block 25354425
First status manipulation (June 20, 17:55:23 UTC): Block 25360428
Drain block: Block 25360696
Drain tx: 0x2be8...3e65
USDC-to-WETH swap tx (18:51:59 UTC): 0x4d37...58f9
CertiK: JaredFromSubway MEV Bot Incident Analysis
CryptoPotato: Jaredfromsubway Hacker Ignores 50% Bounty, Routes Funds to Tornado Cash
Crypto Economy: JaredFromSubway Offers Bounty After MEV Exploit