DEV Community

Cover image for Your old token approvals are a loaded gun: how ERC-20 approve, Permit2 and setApprovalForAll get you drained
Bryan
Bryan

Posted on

Your old token approvals are a loaded gun: how ERC-20 approve, Permit2 and setApprovalForAll get you drained

Most people think a wallet gets drained when someone steals the seed phrase. In 2026 the more common story is quieter: an approval you signed months ago gets used against you.

  • January 2026: an aggregator partner (SwapNet) was exploited and about $16.8M left users' wallets through approvals they had granted earlier. No new signatures.
  • The same month, a single wallet lost about $13.3M through an approval granted on January 1.
  • October 29, 2026: Binance lost about $6.3B (a different vector — exchange hot wallets), which tells you how active attackers are right now.

Here's how approvals actually work, why they're dangerous, and the free scanner I built to clean them up across 6 EVM chains.

What an approval really is

ERC-20 tokens don't let contracts pull your funds by default. A dApp first asks you to call:

function approve(address spender, uint256 amount) external returns (bool);
Enter fullscreen mode Exit fullscreen mode

After that, spender can call transferFrom(you, anyone, amount) whenever it wants. Two details make this risky:

  1. Amount is almost always type(uint256).max — "unlimited", so the UI doesn't have to ask again.
  2. There is no expiry. The allowance stays until you set it back to 0.

NFTs are worse: setApprovalForAll(operator, true) gives one contract every token in a collection.

Permit and Permit2

permit (EIP-2612) lets you grant an allowance with an off-chain signature — no transaction, no gas, and no scary-looking wallet popup. Drainer sites love it.

Uniswap's Permit2 adds a layer: you approve the Permit2 contract once per token, and apps get spending rights through Permit2 allowances. Cleaning up means checking both the token → Permit2 approval and the Permit2 → app allowances.

Why "I stopped using that dApp" doesn't help

The allowance lives in the token contract, not in the dApp. When a router, aggregator or bridge contract gets exploited later, the attacker inherits every allowance users ever gave it. That's exactly what happened in the cases above.

How to clean up

Revoking = sending approve(spender, 0) (or setApprovalForAll(operator, false)). It costs normal gas: cents on BNB Chain, Base, Arbitrum, Optimism and Polygon.

The annoying part is finding the approvals: they're spread across chains and buried in years of logs. That's why I built Revokly:

  • scans Ethereum, BNB Chain, Base, Arbitrum, Optimism and Polygon in one pass;
  • shows each spender, token and amount, with unlimited ones flagged;
  • revokes in one click from MetaMask, Rabby, Trust Wallet or any WalletConnect wallet;
  • runs in the browser — no signup, keys never leave your wallet, and it only ever asks you to sign approve(spender, 0).

It's free. Feedback very welcome in the comments.

A 60-second habit

  • After every new dApp, airdrop claim or mint → re-scan.
  • When a protocol you used is hacked → revoke immediately, on every chain you used it on.
  • Savings on a hardware wallet; a small separate hot wallet for experiments.

👉 Check your wallet on Revokly · Wallet drained? What to do

Top comments (0)