Skip to main content
Sigvex

Arbitrary ERC-20 Transfer Remediation

Add access control to functions that move a contract's tokens so callers cannot redirect the balance to themselves.

Arbitrary ERC-20 Transfer Remediation

Overview

A contract that holds ERC-20 tokens and exposes a transfer function whose recipient and amount come from calldata, with no authorization, can be drained by anyone. An attacker simply calls it with their own address and the contract’s full balance. The fix is to require authorization before any token leaves the contract.

Related Detector: Arbitrary ERC-20 Transfer

Before (Vulnerable)

// Anyone can transfer tokens out
function sendTokens(address token, address to, uint256 amount) external {
    IERC20(token).transfer(to, amount);
}

After (Fixed)

// Only owner can transfer
function sendTokens(address token, address to, uint256 amount) external onlyOwner {
    IERC20(token).transfer(to, amount);
}

The onlyOwner modifier reverts for any caller other than the authorized account, so the recipient and amount can no longer be set by an attacker. Use a role check instead of a single owner when several accounts must be able to move funds.

Alternative Mitigations

  • Use OpenZeppelin AccessControl with a dedicated role for treasury movements rather than a single owner.
  • Constrain the destination to a fixed allowlist of addresses when the set of valid recipients is known.
  • Route withdrawals through a pull pattern where each account can only claim its own recorded balance.

Common Mistakes

  • Adding a check on the token address but leaving the recipient and amount fully caller-controlled.
  • Using tx.origin instead of msg.sender for the authorization check, which is phishable.
  • Ignoring the boolean return value of transfer, so a failed transfer is treated as success; use SafeERC20.

References