The one-sentence version
On Ethereum, allowing a DeFi application to "use" your tokens requires signing a approve, authorizing it to transfer up to X tokens from your address. To save effort, most applications default to requesting unlimited approvals, and these approvals never expire.
As long as that contract later develops a vulnerability, is upgraded to a malicious version, or the contract you originally authorized was actually a phishing contract, the tokens in your wallet can be transferred at any time without you signing anything else. Therefore, you must do two things: grant only precise amounts when authorizing, and regularly check and revoke unused approvals.
This article has three layers:
- Layer 1: The Intuition: Signing an unlimited, non-expiring power of attorney
- Layer 2: Real-World Use: Real-world incidents, and how to check and revoke
- Layer 3:
approve/transferFrommechanisms, calldata decoding, and precise amounts
Layer 1: The Intuition
1.1 An Unlimited, Non-Expiring Power of Attorney
Suppose you want to exchange 100 USDC on an exchange. The process involves two steps:
- Approval: Telling the USDC contract, "Allow this exchange contract to transfer USDC from my account";
- Exchange: The exchange contract transfers your 100 USDC and gives you another token in return.
The problem lies in step 1. To ensure you don't have to approve it again the next time you exchange, applications often request an unlimited allowance. This is equivalent to signing a power of attorney: "This person is authorized to transfer any amount of USDC from my account at any time, and it is valid forever."
1.2 When Things Go Wrong
| Scenario | Result |
|---|---|
| The authorized contract has a vulnerability | An attacker exploits the vulnerability to transfer all authorized users' funds using the contract's identity |
| The contract is upgradeable and the admin's private key is stolen | The contract is upgraded to a malicious version, leading to the same result as above |
| You originally authorized a phishing contract | The attacker can transfer funds whenever they want |
The terrifying part is: the approval might have been signed years ago, and you have long since forgotten about it.
Layer 2: Real-World Use
- Cross-chain Aggregator Incidents: In 2024, vulnerabilities appeared in the contracts of cross-chain aggregators such as LI.FI and Socket. Attackers exploited unlimited approvals previously granted by users to transfer millions to tens of millions of dollars directly from user wallets. Victims did nothing on the day the incidents occurred.
- Badger DAO (2021): Malicious scripts were injected into the frontend, inducing users to approve the attacker's address, after which funds were transferred in batches, resulting in losses of approximately $120 million.
- The
setApprovalForAllof NFTs: One signature means "I grant you permission to dispose of all NFTs in this collection." A large number of NFT phishing attacks involve inducing users to sign such requests.
How to check and revoke:
- Tools like Etherscan's Token Approval Checker and revoke.cash can list all still-valid approvals on your address;
- Revoking involves signing another transaction to set the allowance to 0 (this requires a small amount of gas);
- Develop the habit: Once you have finished using an uncommon application, revoke its approval.
Caloogy Wallet also provides approval checking and one-click revocation.
Layer 3: Principles and Engineering
3.1 ERC-20 Authorization Mechanism
The ERC-20 token contract maintains an internal table allowance[owner][spender]:
function approve(address spender, uint256 amount) returns (bool); // owner call: set allowance
function transferFrom(address from, address to, uint256 amount); // spender call: transfer tokens from from within allowance
- After
approve, the spender can calltransferFrommultiple times, with the amount deducted from the allowance each time; - "Unlimited allowance" means setting the amount to the maximum value of
uint256, which is . Many token contracts handle this value specially: they do not deduct the allowance during transfers, making it truly infinite.
3.2 Identifying an Authorization from calldata
In a transaction sent by a wallet, the first 4 bytes of the data field are the function selector, which is equal to the first 4 bytes of the Keccak-256 hash of the function signature:
| Function | Selector |
|---|---|
approve(address,uint256) |
0x095ea7b3 |
transfer(address,uint256) |
0xa9059cbb |
An unlimited allowance calldata looks like this:
0x095ea7b3
000000000000000000000000 <spender address 20 bytes>
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
When you see 095ea7b3 followed by a long string of f, it is an unlimited allowance authorization. (The selector has been verified using Python.)
3.3 Engineering Protections
| Measure | Description |
|---|---|
| Default Exact Allowance | When an application requests an unlimited allowance, the wallet changes the allowance to the actual amount required for this transaction (MetaMask already supports manual modification by users). |
| Identify All Authorization-type Calls | approve, increaseAllowance, setApprovalForAll, as well as Permit / Permit2 signatures (see "Blind Signing and What You See Is What You Sign"). |
| Spender Verification | Is the spender a known contract? Is it an EOA (an EOA acting as a spender is almost certainly phishing)? |
| Regular Scanning | List all still-valid authorizations for an address, marking them as "unlimited," "unknown entity," or "long unused." |
| Revocation is Low-risk | Revocation only makes the account more secure; from a risk control perspective, it can be assigned the lowest risk level to reduce user friction during revocation. |
| On-chain Verification | If the authorization list comes from a third-party indexing service, perform a direct on-chain read of the current allowance using allowance(owner, spender) before displaying it. |
3.4 New Directions
- Permit2 Expiration Time: Authorizations can include an expiration time, after which they automatically become invalid;
- Account Abstraction / Smart Contract Wallets: It is possible to make "authorize only what is needed for this transaction" or even "complete authorization and usage simultaneously in a batch transaction" the default behavior (see "EOA vs. Smart Contract Wallets").
Summary
| Layer | Key points |
|---|---|
| Intuition | Unlimited approval = An unlimited, non-expiring power of attorney |
| Risk | If the authorized contract has an issue, your funds can be transferred even if you do nothing |
| Identification | 0x095ea7b3 + a string of f; also setApprovalForAll and Permit signatures |
| Protection | Default to exact amounts; regular checks; revoke once used |