The approval you granted on a Tuesday six months ago is still spending — and it didn't ask your permission. You closed the tab after the trade, the tab after that, and life moved on. Then the protocol got hacked, or someone cloned its UI, and the old unlimited allowance became a wire transfer. You didn't sign anything new. One transferFrom call and the money is gone.
A token approval is a standing permission recorded on the blockchain. approve(spender, amount) hands a spender the right to call transferFrom on your behalf for up to that amount — and nothing in the ERC-20 standard expires it. An unlimited approval, the one your wallet offered as "approve once, never see this prompt again", sets the allowance to 2^256-1. It can't be spent down, so it sits there until you set it back to zero. Every DeFi user has dozens of these on-chain: the wallet used for a sandwich trade, the bridge used once, the gauge you claimed a reward from. Each is a permanent standing authorization until a second on-chain transaction revokes it.
Why "dormant" is a lie — and why it keeps draining wallets
The word "dormant" is reassuring, and it's wrong. The spender doesn't need to be malicious when you granted it: a legitimate protocol can get hacked — admin key phished, compromised update pushed, upgradeable proxy swapped for a drain function. The attacker inherits every standing allowance you hold, one transferFrom call each. Or the approval can belong to a contract you never used: a phishing page clones a real connect flow, you sign, the page disappears, and the drain happens on the bad actor's schedule, months later. Permit and Permit2 do the same damage without an approve transaction — the signature is the approval. The exposure isn't your balance; it's the count of live approvals you've granted. A wallet with thirty approvals has thirty standing invitations to be drained.
How the exploitation happens after the fact
The pattern is boring and reliable. Grant: a swap, a mint, a bridge, a claim — you approve unlimited because the UI frames it as convenience, and the approval is recorded on-chain. The app goes quiet or changes its name. Then the compromise: admin key phished, malicious upgrade, injected script. Finally, the sweep: one transaction calls transferFrom against every wallet holding that token, and the attacker pays the gas. The victim never sees a new approval prompt or a balance change at signing time. The 458-day case makes the shape concrete: an unlimited approval sat untouched for over a year, then a drainer used it after the protocol behind it was compromised. The approval didn't age — it waited.
AI agents multiply this: a coding agent that touches multiple protocols accumulates approvals the way a tool user collects subscriptions — each tool gets unlimited, and nobody revokes. The same shape, scaled.
What to do about it — the retire cadence
Revocation is a transaction: it costs gas and doesn't recover funds already taken. Treat it as maintenance.
- Revoke after every protocol you're done with. The step-by-step is at /how/revoke-token-approvals — one tool, one confirmation, one habit.
- Audit quarterly. Use a revocation tool or the token's approval page on a block explorer: find allowances that exceed your normal trade size, and approvals to apps you can't name, and revoke them.
- When a protocol you approve is compromised or renounced, revoke everything you hold with it the same day. A standing approval to a renounced contract is a wire to a dead address.
- Prefer exact-amount approvals over unlimited where the interface allows it. A fixed allowance is spent down, not drained.
- Read the spender before you sign "approve". A long hex blob you can't parse means stop.
The approval you granted six months ago is still spending. Your other approvals are waiting too. The fix is a cadence: revoke when done, audit twice a year, and treat every drain as a question before you sign, not an event you chase afterward.