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
相关产品推荐
相关产品推荐

