You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ERC20代币transferFrom函数触发ERC20InsufficientAllowance错误求助

自定义ERC20代币transferFrom触发ERC20InsufficientAllowance错误排查

问题背景

在Remix开发自定义ERC20代币,使用三个账户:

  • 合约所有者:0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
  • 授权转账账户:0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2(已由合约所有者授权)
  • 接收账户:0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db

调用transferFrom时触发ERC20InsufficientAllowance错误,但approve和getAllowance功能均正常,能正确显示授权额度。

相关合约代码

授权与授权查询逻辑

function approve(address spender, uint256 value) public checkPaused returns (bool) {
    if (spender == address(0)) {
        revert ERC20InvalidSender(address(0));
    }
    
    _approve(msg.sender, spender, value);

    return true;
}

function _approve(address owner, address spender, uint256 value) internal {
    _allowances[owner][spender] = value;

    emit Approve(owner, spender, value);
}

function getAllowance(address owner, address spender) public view returns (uint256) {
    return _allowances[owner][spender];
}

transferFrom及相关内部函数

function transferFrom(address from, address to, uint256 value) public checkPaused returns (bool) {
    if (from == address(0) || to == address(0)) {
        revert ERC20InvalidSender(address(0));
    }

    if (_balances[from] < value) {
        revert ERC20InsufficientBalance(msg.sender, _balances[from], value);
    }

    if (_allowances[from][to] < value) {
        revert ERC20InsufficientAllowance(to, _allowances[from][to], value); // 错误触发位置
    }

    _spendAllowance(from, to, value);

    _transfer(from, to, value);

    return true;
}

function _transfer(address from, address to, uint256 value) internal {
    _balances[from] -= value;
    _balances[to] += value;

    emit Transfer(from, to, value);
}

function _spendAllowance(address owner, address spender, uint256 value) internal {
    _allowances[owner][spender] -= value;

    emit SpendAllowance(owner, spender, _allowances[owner][spender] - value);
}

错误信息

[vm]from: 0xAb8...35cb2to: ShivaToken.transferFrom(address,address,uint256) 0x80d...4BB9Bvalue: 0 weidata: 0x23b...003e8logs: 0hash: 0xd05...7a5d7
status  0x0 Transaction mined but execution failed
transaction hash    0xd0561c9e4f2195c3ab545a29e02f747ad54e28aae092ddd92a6923d49107a5d7
block hash  0x57c90613d4e65424402b0f96f97d671783bf024333cf48315f0b7b9e66082dda
block number    586
from    0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
to  ShivaToken.transferFrom(address,address,uint256) 0x80d527dF27B22d91E1AC56868e7AA5e7Ebf4BB9B
gas 3000000 gas
transaction cost    30318 gas 
execution cost  8366 gas 
input   0x23b...003e8
output  0xfb8f41b20000000000000000000000004b20993bc481177ec7e8f571cecae8a9e22c02db000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8
decoded input   {
    "address from": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
    "address to": "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db",
    "uint256 value": "1000"
}
decoded output  {
    "0": "bool: true"
}
logs    []
raw logs    []
transact to ShivaToken.transferFrom errored: Error occurred: revert.

revert
    The transaction has been reverted to the initial state.
Error provided by the contract:
ERC20InsufficientAllowance
Parameters:
{
 "spender": {
  "value": "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db",
  "documentation": "Address that may be allowed to operate on tokens without being their owner."
 },
 "allowance": {
  "value": "0",
  "documentation": "Amount of tokens a `spender` is allowed to operate with."
 },
 "needed": {
  "value": "1000",
  "documentation": "Minimum amount required to perform a transfer."
 }
}
If the transaction failed for not having enough gas, try increasing the gas limit gently.

问题原因与修复方案

核心问题

transferFrom函数的授权校验逻辑完全错误:

  • 实际业务逻辑是:合约所有者授权给0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2(调用transferFrom的账户),所以需要校验_allowances[from][msg.sender](所有者给当前调用者的授权额度)
  • 原代码错误地校验了_allowances[from][to],也就是所有者给接收账户的授权额度,而接收账户从未被授权,所以额度为0,触发错误。

修复后的transferFrom函数

function transferFrom(address from, address to, uint256 value) public checkPaused returns (bool) {
    if (from == address(0) || to == address(0)) {
        revert ERC20InvalidSender(address(0));
    }

    if (_balances[from] < value) {
        revert ERC20InsufficientBalance(msg.sender, _balances[from], value);
    }

    uint256 currentAllowance = _allowances[from][msg.sender];
    if (currentAllowance < value) {
        revert ERC20InsufficientAllowance(msg.sender, currentAllowance, value);
    }

    _spendAllowance(from, msg.sender, value);

    _transfer(from, to, value);

    return true;
}

额外修复:_spendAllowance事件参数错误

原_spendAllowance中的emit参数逻辑错误,扣除额度后再减value会得到负数,修正为使用扣除前的额度计算剩余值:

function _spendAllowance(address owner, address spender, uint256 value) internal {
    uint256 currentAllowance = _allowances[owner][spender];
    _allowances[owner][spender] = currentAllowance - value;

    emit SpendAllowance(owner, spender, currentAllowance - value);
}

内容的提问来源于stack exchange,提问作者shiva13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:19:57