Over $50 million was drained from DeFi users in the first three months of 2026 alone — not through flashy flash loans or oracle manipulation, but through something far more mundane: stale token approvals.
The SwapNet exploit ($13.5M), Aperture Finance hack ($3.67M), and dozens of smaller incidents share a common thread. Attackers didn't need to break cryptography or find zero-days. They exploited permissions users had already granted — sometimes months or years earlier.
This article is a practitioner's guide to token approval security: what goes wrong, why the standard patterns are dangerous, and how to build approval-safe protocols and habits in 2026.
The Anatomy of an Approval Attack
Every ERC-20 approve() call creates a standing permission: "Contract X can move up to Y of my tokens." When you approve type(uint256).max, you're saying "Contract X can take everything, forever."
Here's the attack flow that drained SwapNet users:
1. User swaps on Matcha Meta → routed through SwapNet
2. SwapNet's router requests unlimited approval for USDC
3. User approves (standard UX, most users click through)
4. Months pass. User forgets about the approval.
5. Attacker finds arbitrary-call vulnerability in SwapNet's 0x87395540()
6. Attacker calls transferFrom(victim, attacker, victim_balance)
7. Approved tokens vanish. No phishing needed. No signature required.
The critical insight: the vulnerability was in the protocol, but the damage was enabled by the approval. If users had granted one-time or bounded approvals, the attack surface would have been near zero.
Why MAX_UINT256 Approvals Persist
Despite years of warnings, unlimited approvals remain the default across DeFi. Here's why:
1. Gas Optimization Theater
// The "gas-saving" argument
// "Why pay for approve() every transaction when you can approve once?"
token.approve(router, type(uint256).max); // One tx, ~46k gas
// vs.
token.approve(router, exactAmount); // Every swap, ~46k gas each
On Ethereum mainnet at 30 gwei, each approval costs ~$2-4. For frequent traders, unlimited approval saves maybe $50/year. But it creates an indefinite liability against their entire token balance.
2. UX Friction Aversion
Wallets and dApps optimize for conversion. Every additional transaction is a dropout point. So the industry standardized on "approve once, use forever" — prioritizing convenience over security.
3. The "Audited Protocol" Fallacy
Users assume audited protocols are safe forever. But:
- Audits are point-in-time snapshots
- Upgradeable proxies can change logic post-audit
- Closed-source contracts (like SwapNet) can't even be publicly audited
- Team key compromises can alter contract behavior
The 2026 Approval Security Stack
Layer 1: Protocol-Level — Permit2 and Bounded Approvals
Uniswap's Permit2 contract fundamentally changes the approval model:
// Old pattern: approve each protocol individually
USDC.approve(uniswapRouter, MAX);
USDC.approve(sushiRouter, MAX);
USDC.approve(oneInchRouter, MAX);
// 3 unlimited approvals = 3 attack surfaces
// Permit2 pattern: one approval to Permit2, then signed permits
USDC.approve(permit2Address, MAX); // Only 1 approval needed
// Each swap uses a signed, time-bounded permit
permit2.permitTransferFrom(
ISignatureTransfer.PermitTransferFrom({
permitted: ISignatureTransfer.TokenPermissions({
token: address(USDC),
amount: swapAmount // Exact amount, not MAX
}),
nonce: uniqueNonce,
deadline: block.timestamp + 30 minutes // Expires quickly
}),
transferDetails,
msg.sender,
signature
);
Why this matters: Even if a protocol using Permit2 gets exploited, the attacker can only use permits that haven't expired. No more infinite standing approvals to drain.
Layer 2: Contract Design — The Approval-Minimizing Pattern
If you're building a protocol, design for minimal approval surface:
// BAD: Require users to pre-approve your contract
function swap(address token, uint256 amount) external {
IERC20(token).transferFrom(msg.sender, address(this), amount);
// ... swap logic
}
// GOOD: Pull exact amounts via Permit2 with deadline
function swapWithPermit(
address token,
uint256 amount,
uint256 deadline,
bytes calldata signature
) external {
require(block.timestamp <= deadline, "Permit expired");
permit2.permitTransferFrom(
/* signed permit for exact amount with deadline */,
ISignatureTransfer.SignatureTransferDetails({
to: address(this),
requestedAmount: amount
}),
msg.sender,
signature
);
// ... swap logic
}
For Solana programs, the equivalent is using delegated token accounts with explicit limits:
// Solana: Approve with explicit amount and optional delegate authority
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Approve};
pub fn approve_bounded(ctx: Context<ApproveCtx>, amount: u64) -> Result<()> {
// Only approve the exact amount needed for this operation
token::approve(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Approve {
to: ctx.accounts.token_account.to_account_info(),
delegate: ctx.accounts.delegate.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
},
),
amount, // NOT u64::MAX
)?;
Ok(())
}
// After operation completes, revoke the delegation
pub fn revoke_after_use(ctx: Context<RevokeCtx>) -> Result<()> {
token::revoke(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
token::Revoke {
source: ctx.accounts.token_account.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
},
),
)?;
Ok(())
}
Layer 3: User Habits — The Approval Hygiene Checklist
For individual users, implement these practices:
Monthly Approval Audit:
- Visit revoke.cash or app.unrekt.net
- Connect each wallet you use
- Sort approvals by "value at risk" (unlimited approvals × token balance)
- Revoke any approval where:
- You haven't used the protocol in 30+ days
- The approved contract is unverified/closed-source
- The approval is unlimited and the protocol supports bounded approvals
Per-Transaction Discipline:
- Enable "One-Time Approvals" on aggregators that support it (1inch, Matcha)
- Use wallet extensions like Pocket Universe or Fire that simulate and warn about approval transactions
- When approving, manually edit the amount in MetaMask (click the pencil icon on the approval screen)
Emergency Response:
If a protocol you've approved gets hacked:
1. DON'T PANIC — but move fast
2. Go to revoke.cash immediately
3. Revoke the affected contract's approvals
4. Check if the exploiter has already used your approval
5. If funds are still there, move them to a fresh wallet
The Approval Attack Surface: By the Numbers (Q1 2026)
| Incident | Loss | Root Cause | Approval Factor |
|---|---|---|---|
| SwapNet/Matcha | $13.5M | Arbitrary call in closed-source contract | Unlimited USDC approvals |
| Aperture Finance | $3.67M | Arbitrary call, no input validation | Unlimited approvals for "Instant Liquidity" |
| Various phishing | ~$30M+ | Wallet drainer scripts, permit signatures | Users signing malicious permit2 messages |
| Venus Protocol | $3.7M | Oracle manipulation + donation attack | N/A (lending protocol, different vector) |
Notice the pattern: the largest approval-based losses come from closed-source contracts. Open-source contracts get audited by the community; closed-source ones are black boxes where you're trusting the team's competence entirely.
Solana-Specific Approval Risks
Solana's token model is different from ERC-20, but the approval risks are analogous:
Token Delegate Authority
SPL tokens use delegate instead of approve. A delegated account can transfer up to the approved amount. The risk: some Solana dApps request delegation of your full token balance.
Token-2022 Transfer Hooks
The newer Token-2022 standard introduces transfer hooks — arbitrary code that executes on every transfer. A malicious or compromised transfer hook could:
- Silently redirect tokens during legitimate transfers
- Log all transfer destinations for phishing targeting
- Implement time-delayed drains that activate weeks after approval
CPI-Based Approval Chains
When a Solana program makes a Cross-Program Invocation (CPI) to the token program with your signing authority, it can approve delegates on your behalf. Always verify that programs you interact with don't grant unexpected delegations in their CPI chains.
Building an Approval-Safe Protocol: Checklist for Developers
□ Use Permit2 or equivalent bounded-approval mechanism
□ Never request unlimited approvals in your frontend
□ If using upgradeable proxies, implement approval revocation in upgrade hooks
□ Time-bound all delegated permissions (24h max for routine operations)
□ Emit clear events on approval changes for monitoring tools
□ Open-source your token-handling contracts (even if core logic is proprietary)
□ Document your approval model in user-facing documentation
□ Implement a one-click "revoke all" function in your dApp
□ Add approval amount to transaction simulation previews
□ Conduct approval-focused audit (separate from general security audit)
The Path Forward: ERC-7715 and Native Approval Expiry
The Ethereum community is working on ERC-7715 (Grant Permissions), which would introduce native expiry for token approvals at the protocol level. Instead of approvals persisting forever until explicitly revoked, they would automatically expire after a specified duration.
Combined with account abstraction (ERC-4337) enabling batch revocations and session keys, the approval landscape is shifting. But these standards are months from widespread adoption. In the meantime, the security practices outlined above are your best defense.
Key Takeaways
- Unlimited approvals are technical debt — they accumulate silently and explode when a protocol gets compromised
- Closed-source contracts with approvals are the highest-risk combination — you can't verify what your approval enables
- Permit2 is the current best practice — use it if you're building, prefer protocols that use it if you're trading
- Monthly approval audits are non-negotiable — 10 minutes on revoke.cash can save your portfolio
- The SwapNet exploit was not novel — approval-based drains have been happening since 2020. The only novel part was the scale.
The DeFi ecosystem has spent billions on smart contract auditing while leaving the approval model fundamentally broken. Until ERC-7715 and similar standards achieve adoption, your security is only as good as your approval hygiene.
Building something in DeFi? I audit smart contracts with a focus on approval surfaces and access control. Find me on dev.to or Hashnode.
Tags: #security #defi #web3 #solidity #solana #smartcontracts
Top comments (0)