为何调用transferFrom时我的ERC20合约会回滚?
你的问题核心在于对OpenZeppelin ERC20合约的transferFrom函数行为理解有误,同时代码存在竞态条件问题:
关键问题点
OpenZeppelin ERC20的
transferFrom不返回布尔值
从OpenZeppelin v4.x版本开始,适配Solidity 0.8+的ERC20实现中,transferFrom、transfer等函数不再返回bool类型结果。当转账失败时(比如授权额度不足、余额不足),函数会直接触发revert抛出错误,而非返回false。你代码中试图用bool success接收返回值的写法是错误的,这会导致编译警告或运行时异常,最终触发你自定义的"Transfer failed"错误。竞态条件风险
你先调用token.allowance(from, msg.sender)检查额度,再执行transferFrom的操作存在竞态窗口:两次调用之间,from地址可能已经修改了给msg.sender的授权额度(比如调用approve减少额度),导致transferFrom执行时实际额度不足,即使之前的检查结果是充足的。
修改后的代码
直接移除多余的额度检查和返回值判断,让transferFrom自身处理所有校验逻辑:
function spend(address from, address to, uint256 amount) public { token.transferFrom(from, to, amount); }
补充说明
如果需要自定义错误提示,可以利用Solidity 0.8+的try/catch捕获transferFrom抛出的异常,但通常不需要——OpenZeppelin的ERC20已经内置了清晰的错误类型(比如ERC20InsufficientAllowance、ERC20InsufficientBalance),这些错误会直接暴露给调用者,便于排查问题。
内容的提问来源于stack exchange,提问作者Fauzan Hidayatulloh

