Skip to content

ERC-20 approval race condition

An ERC-20 spender may use an old allowance before a replacement approval is confirmed, then use the new allowance. Learn how the race works and how to change or revoke approvals more safely.

Updated

For educational purposes only; not investment advice. Investing may result in loss.

Direct answer

The ERC-20 approval race condition can occur when an owner replaces one non-zero allowance with another by calling approve(spender, newAmount). A spender can see the pending change, spend the old allowance first with transferFrom, and then spend from the replacement allowance after it is confirmed.

The ERC-20 specification therefore recommends that client interfaces first set the allowance to 0 before setting a new value for the same spender. Each transaction must be confirmed in order. Where a token supports them, atomic increaseAllowance or decreaseAllowance calls avoid replacing a non-zero allowance directly.

ERC-20 approval race condition
0 / 5
0 items reviewed; 5 items still unresolved

Completing this review does not prove an asset, transaction, or system is safe.

How it works

ERC-20 defines approve as an overwrite: a successful approve(spender, amount) sets the spender’s allowance to amount. Separately, transferFrom(owner, recipient, amount) lets that spender move the owner’s tokens and normally reduces the remaining allowance. Pending transactions do not reserve an execution order, so a spender may submit a transfer that executes before the owner’s approval change.

The risky transition is N -> M, where both N > 0 and M > 0. If the spender consumes N before the replacement approval executes, the later approval installs a fresh allowance of M. The maximum spent across that sequence can therefore be N + M, subject to the owner’s token balance and the token’s implementation.

Example

Alice has approved a protocol to spend 100 tokens. She submits approve(protocol, 50), intending to lower the remaining allowance to 50. Before that transaction confirms, the protocol’s spender submits transferFrom(Alice, recipient, 100) and gets it executed first. Alice’s approval then sets the allowance to 50, which the spender can use in another transfer. The two transfers total 150 tokens.

A safer replacement flow is approve(protocol, 0), wait for confirmation, inspect the resulting allowance and balance, and only then submit approve(protocol, 50) if the new approval is still appropriate. If the spender uses the old allowance before the zeroing transaction confirms, Alice can see the changed balance and stop before granting the new 50.

Risks

  • Setting the allowance to 0 does not revoke spending that has already executed or prevent use of the old allowance before the zeroing transaction confirms.
  • Sending the zeroing and replacement approvals together, without waiting for the first confirmation, reintroduces ordering risk.
  • increaseAllowance and decreaseAllowance are not part of the base ERC-20 standard; use them only when the verified token contract supports them.
  • An unlimited allowance can expose the owner’s full token balance while it remains active. Verify the chain, token contract, spender, and amount before signing.
  • Some tokens have non-standard approval behavior. Read the wallet simulation and transaction calldata, and confirm the on-chain allowance after each step.

Common misconceptions

  • “The latest approval transaction replaces the old one immediately.” It changes state only when executed on-chain.
  • “Lowering an allowance caps total future spending at the new amount.” A spender may use the old allowance before the change executes.
  • “Zero-first guarantees that no more tokens can leave.” The old allowance remains usable until the zeroing transaction confirms.
  • “Every ERC-20 has increaseAllowance and decreaseAllowance.” They are optional extensions, not ERC-20 requirements.
  • “Revoking the website connection revokes token approval.” Wallet connection state and the token contract’s on-chain allowance are separate.

Sources

Navigation

Search the wiki...