How to Rescue Tokens From a Compromised Wallet With a Sweeper Bot (Flashbots Guide)

Your seed phrase leaked. Maybe it was a fake airdrop site, maybe a malicious browser extension, maybe a screenshot sitting in your cloud photos. The ETH is gone, but there are still tokens in the wallet. Maybe an ERC-20 position, some NFTs, a staked balance or an airdrop you haven't claimed yet.

So you do the obvious thing. You send a bit of ETH to the wallet to pay for gas, so you can move the tokens out. And the ETH disappears within seconds, before your transfer even gets a chance.

That's a sweeper bot. The good news is you can beat it, and this article explains exactly how.

What a sweeper bot actually does

A sweeper bot is a script the attacker runs against your address. Since they have your private key, they can sign transactions from your wallet just like you can. The bot watches the address and, the moment any ETH arrives, it signs a transaction sending that ETH to the attacker's wallet. It usually pays a high priority fee so it gets included before anything you try to do.

MetaMask's support center has a good explainer on sweeper bots, and the core problem is simple: every Ethereum transaction needs ETH for gas, and the attacker takes any ETH that lands in the wallet. Your tokens are sitting there, but you can't move them because you can't keep gas in the account long enough to use it.

Attackers don't always take the tokens right away. They may be locked, vesting or illiquid, so the attacker leaves the sweeper running and waits for you to feed it ETH.

The EIP-7702 twist

Since Ethereum's Pectra upgrade, there's a newer version of this attack. EIP-7702 lets a normal wallet (an EOA) delegate its execution to smart contract code. Attackers with a leaked key started using that to attach sweeper contracts directly to victims' wallets. Wintermute's research team found that a single copy-pasted sweeper contract, which they nicknamed "CrimeEnjoyor", made up the majority of early EIP-7702 delegations (CoinDesk, The Block).

Why does that matter for a rescue? A classic sweeper is an off-chain bot that needs to see your ETH and react. A 7702 sweeper is code that runs inside your account. ETH sent to the address can be forwarded automatically by the delegated code in the same transaction. So if your wallet has been delegated, the delegation has to be cleared first, before you fund anything. We'll come back to this.

Why you can't win a gas war against a sweeper

The instinct is to outbid the bot: send ETH and the token transfer at the same time with a huge gas price. It almost never works.

When you send ETH to the compromised wallet through the public mempool, that transaction is visible before it's included. The sweeper sees it coming. It can pre-sign a transaction using the next nonce of your wallet and broadcast it with a higher fee. And because the attacker has the same key you have, there's no transaction you can sign that they can't also sign, or replace.

You're playing a speed game against a bot whose only job is to win that exact game. You will lose, and every attempt costs you the ETH you sent.

The fix: an atomic Flashbots bundle

The way out is to stop sending transactions one at a time through the public mempool, and instead send them as a bundle directly to block builders.

A bundle is an ordered list of signed transactions that a builder either includes all together, in that exact order, in the same block, or not at all. Flashbots describes this in its Quick Start documentation: searchers send bundles privately to builders, builders simulate them to make sure they don't revert, and then they're delivered to validators through mev-boost relays without ever appearing in the public mempool.

That gives you two things the sweeper can't deal with:

  1. Privacy. The funding transaction is never broadcast publicly, so the sweeper bot doesn't see the ETH coming.
  2. Atomicity. The funding and the token transfers execute back to back in the same block. There's no gap between "ETH arrives" and "ETH gets spent" for the bot to jump into.

This is exactly the pattern used by Flashbots' own reference project, searcher-sponsored-tx, which MetaMask also points victims toward.

How the bundle is structured

You need three wallets:

  • The compromised wallet (the "executor"). It holds the tokens. You need its private key, because it has to sign the transfers.
  • A clean sponsor wallet. Freshly created, never connected to the compromised device or seed. It holds the ETH that pays for everything.
  • A safe recipient wallet. Also fresh. This is where the rescued tokens go.

The bundle then looks like this:

  1. Sponsor sends just enough ETH to the compromised wallet to pay for the next transactions.
  2. Compromised wallet transfers token A to the safe wallet.
  3. Compromised wallet transfers token B (or the NFT, or calls unstake then transfer) to the safe wallet.
  4. Optionally, a final transaction that pays the builder for including the bundle.

Because all of it lands in one block or not at all, the sweeper has no window. If any step would fail, the builder simply drops the bundle and you don't pay for a failed rescue.

If the wallet has an EIP-7702 delegation

If the attacker delegated your account to a sweeper contract, step 1 isn't safe on its own, because the delegated code can forward the incoming ETH immediately. The open-source eth-rescue CLI handles this by adding a transaction at the very start of the bundle: a sponsor-paid type-4 transaction carrying an authorization, signed with the compromised key, that clears the delegation. Only then does it fund the wallet and run the rescue.

You can check whether an address has a delegation by looking at its code on a block explorer: a normal EOA has no code, while a delegated account shows the delegation designator pointing at a contract. The ethereum.org EIP-7702 guidelines explain how delegation works and why you should never sign a delegation to a contract you don't recognize.

Step-by-step: running a rescue

Here's the process in plain terms. I'm keeping it conceptual on purpose. If you're writing the code yourself, start from the Flashbots reference repo or eth-rescue rather than from scratch.

1. Stop feeding the sweeper

Don't send another wei of ETH to the compromised address. Every failed attempt is a donation to the attacker.

2. Map out exactly what you need to move

List every asset still in the wallet: ERC-20 balances, NFTs, LP tokens, staked or vesting positions, and claimable airdrops. For each one, figure out the exact calls needed. A plain ERC-20 is one transfer. A staked position may need unstake or withdraw, then transfer. An airdrop may need claim, then transfer. All of those can go in the same bundle, in order.

Also check the attacker hasn't already set approvals that let them pull tokens out with transferFrom. If they have, they can take those tokens at any time without needing gas in your wallet, and speed matters even more.

3. Create the clean wallets on a clean machine

If the device that leaked your key is still compromised, anything you generate on it may leak too. Create the sponsor and recipient wallets somewhere you trust.

4. Calculate gas precisely

Since EIP-1559, every transaction pays the block's base fee plus a priority fee, and the base fee has to be paid by the account sending that transaction. That means the sponsor's first transaction must send the compromised wallet enough ETH to cover the base fee and priority fee for every transaction it will send in the bundle, with a small buffer. Send too little and the bundle reverts. Send too much and the leftover may get swept after the block.

The builder can also be paid directly with a transfer to the block's coinbase address from the sponsor, which is a clean way to make your bundle attractive without leaving extra ETH in the compromised wallet.

5. Simulate before you send

Flashbots supports eth_callBundle, which simulates the signed bundle against a given block and returns gas used and payments for each transaction. The Flashbots troubleshooting guide recommends always simulating, logging the results and keeping every bundle you submit. If the simulation reverts, the bundle will never land, so fix it before going live. Rehearsing on the Sepolia testnet first is even better.

6. Submit for the next block, and keep resubmitting

A bundle targets one specific block number. If it isn't included in that block, it's gone, and you have to send it again for the next one. Rescue scripts loop: rebuild with fresh fees and nonces, simulate, submit for block N+1, repeat until it lands or you decide to stop.

Some blocks won't include your bundle for reasons that have nothing to do with you, such as the validator for that slot not running mev-boost. That's normal. Keep going.

7. Watch the nonce

The attacker has your key, so they can also send transactions from your wallet. If they get a transaction included with the same nonce your bundle uses, your bundle becomes invalid. Rebuilding the bundle every block with the current nonce handles this.

What can still go wrong

Flashbots bundles are the best tool available here, but they aren't magic. The eth-rescue README is honest about it: a clean simulation doesn't guarantee inclusion, an attacker with the same key can move the nonce or the assets first, and builders can decline a bundle.

The most common failure modes:

  • The attacker gets there first. If the tokens are liquid and the attacker is watching, they may sweep the tokens themselves once they notice. Move fast.
  • The bundle doesn't pay enough. If a builder can earn more from other transactions, your bundle gets left out. Raising the priority fee or coinbase payment fixes this.
  • The asset is locked. If a vesting contract or staking period hasn't ended, no bundle can move it yet. You'll need to prepare the bundle and submit it at the block it unlocks, ideally before the attacker does.

Do it yourself, get help, or use a bot

You have three realistic options.

Flashbots Whitehat. Flashbots runs a whitehat hotline. Per their docs, they can only help if the remaining assets exceed $1,000, and you should only submit requests through whitehat.flashbots.net. MetaMask's guide notes the recovery team takes a cut of the recovered assets.

Do it yourself. If you're comfortable with TypeScript and signing raw transactions, the Flashbots reference code or eth-rescue will get you there. Test on Sepolia first.

Use a purpose-built rescue bot. That's why we built the MEVDAO Token Rescue Bot. It's built for exactly this case: a compromised wallet with a sweeper attached, where you need to get tokens out through Flashbots and private builders. It's included in the MEVDAO subscription along with the sniper bot and sniping group, and you can try it for 3 days free.

One warning, whichever route you choose: wallet-drain victims get targeted by fake "recovery services" in DMs and replies. Anyone who reaches out to you unprompted offering to recover your funds is almost certainly running a second scam. Only work with tools and people you sought out yourself.

Once you're safe

When the tokens are in the new wallet, treat the old one as permanently burned. Never send anything to it again. Then find out how the key leaked, because if the cause is still on your machine (a malicious extension, a remote access tool, a seed stored in plain text), the new wallet is at risk too.

If you want more practical guides like this one, on MEV, Flashbots and building bots, join the free MEV Secrets email list below. And if you want a custom bot built, you can message me on Telegram.

← All articles