For educational purposes only; not investment advice. Investing may result in loss.
Direct answer
Some ERC-20 implementations reject approve(spender, newAmount) when the existing allowance and newAmount are both non-zero. For those tokens, first submit approve(spender, 0), wait for it to confirm, and only then submit the new non-zero approval.
This restriction is not required of every ERC-20 token. ERC-20 defines approve as replacing the current allowance and recommends zero-first behavior in client interfaces to mitigate an approval-change race, while also saying token contracts should not enforce it for compatibility. Some deployed tokens nevertheless do enforce it. Zero-first is therefore both a compatibility procedure and a useful checkpoint, not a guarantee that the old allowance cannot be spent before the zeroing transaction confirms.
Completing this review does not prove an asset, transaction, or system is safe.
How it works
- Verify the chain, token contract, owner, spender and intended amount. Read
allowance(owner, spender)from the token contract rather than relying only on a wallet label. - If the allowance is already
0, submit the desired approval once. If it is non-zero, a direct non-zero replacement may succeed on a standard implementation or revert on a zero-first token. - For a zero-first flow, submit
approve(spender, 0)and wait for a successful receipt. Then read the same owner-spender allowance again and confirm it is0. - Recheck the token balance, spender and purpose. Only then submit
approve(spender, newAmount)and wait for confirmation before treating the new allowance as active. - Verify the final allowance and review any intervening
TransferandApprovalevents. A successful transaction receipt proves execution, while the current contract state shows the permission that remains.
OpenZeppelin’s SafeERC20.forceApprove implements a compatibility fallback for contracts: it tries the desired value, and if that call fails, it tries 0 followed by the desired value. This helper changes the calling contract’s own allowance; it does not automatically fix a user’s wallet approval or remove the need to verify transaction ordering and final state.
Example
An owner has an allowance of 1000 tokens for a spender and wants to reduce it to 100. On a zero-first token, a direct approve(spender, 100) reverts, so the on-chain allowance remains 1000; a reverted call does not partially update it.
The owner instead submits approve(spender, 0). Before that transaction confirms, the spender uses 400, leaving 600; the confirmed zeroing transaction then replaces the remainder with 0. After checking the lower token balance, the owner can decide whether to grant the new 100. If granted, the spender has already used 400 and can later use up to 100 more. Zero-first exposed the intervening spend before the new permission was granted, but it did not undo that spend.
Risks
- The old allowance remains usable until the zeroing transaction executes. A spender may race a pending revocation or reduction.
- Broadcasting the zeroing and replacement transactions without waiting for the first confirmation removes the intended checkpoint and can hide an intervening spend.
- A wrong chain, token address or spender address can create or revoke a different permission from the one intended. Token symbols are not unique identifiers.
- The two-step flow costs two transactions when both are needed, and either can fail, be replaced or remain pending. Do not infer state from a submitted signature alone.
- Unlimited approvals and upgradeable or compromised spenders can expose future deposits. Use the smallest practical amount and verify the remaining allowance after use.
- Contract integrations must handle non-standard return values and approval behavior deliberately. A compatibility wrapper cannot make an untrusted spender safe.
Common misconceptions
- Every ERC-20 requires zero-first. The standard recommends the client-side sequence but says token contracts should not enforce it; only some implementations reject non-zero-to-non-zero changes.
- Zero-first completely fixes the approval race. The spender can still use the old allowance before the zeroing transaction confirms.
- A reverted replacement cleared the old allowance. A revert rolls back the attempted state change, so the prior allowance normally remains.
- Two transactions sent together are equivalent to waiting. The safety checkpoint comes from confirming and inspecting the zero state before deciding on the replacement.
- Disconnecting a website revokes its approval. Wallet connection state is separate from the token contract’s on-chain allowance.
Related topics
- ERC-20 approval race condition
- Wallet approval
- ERC-2612 Permit nonce and deadline
- Permit2 signature risk
Sources
- ERC-20: Token Standard - Ethereum Improvement Proposals (accessed: 2026-08-21)
- ERC20 | OpenZeppelin Docs - OpenZeppelin (accessed: 2026-08-21)
- SafeERC20.sol - OpenZeppelin (accessed: 2026-08-21)