Skip to content
Last updated: February 06, 2026

SCWE-103: ERC20 Approval Double-Spend (Allowance Race)

Stable Version v1.0

This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

Changing an ERC20 allowance from value X to Y with a single approve call allows a spender to front-run and spend X before the change, then spend Y after, effectively double-spending. Integrations that do not use increaseAllowance/decreaseAllowance are exposed.

Remediation

  • Follow the allowance reset pattern: set allowance to 0, then set the new value.
  • Prefer increaseAllowance/decreaseAllowance or EIP-2612 permit with nonces.
  • For critical flows, pull tokens via transferFrom after verifying allowance updates.

Examples

Vulnerable

pragma solidity ^0.8.0;
interface IERC20 { function approve(address,uint256) external returns (bool); }

contract DApp {
    IERC20 public token;

    function changeSpender(address spender, uint256 newAmount) external {
        token.approve(spender, newAmount); // can be front-run
    }
}

Fixed

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract DApp {
    using SafeERC20 for IERC20;
    IERC20 public token;

    function changeSpender(address spender, uint256 newAmount) external {
        token.safeApprove(spender, 0);      // reset first; SafeERC20 handles non-standard tokens (e.g. USDT)
        token.safeApprove(spender, newAmount);
    }
}