含isContract与tx.origin校验的Solidity合约在Foundry测试中报错,如何通过?
问题:Foundry测试触发"Proxy contract not allowed"错误
我在Solidity合约中实现了如下modifier:
modifier notContract() { require(!_isContract(msg.sender), "Contract not allowed"); require(msg.sender == tx.origin, "Proxy contract not allowed"); _; }
随后在Foundry中编写测试用例:
function testStartRound() public { vm.startPrank(owner); head_or_tail.startRound(0); vm.stopPrank(); }
执行测试时出现错误:
[10569] CounterTest::testStartRound() ├─ [0] VM::startPrank(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045) │ └─ ← () ├─ [513] HeadOrTail::startRound(0) │ └─ ← "Proxy contract not allowed" └─ ← "Proxy contract not allowed"
请问如何让测试无错误通过?
解决方案
问题核心是msg.sender == tx.origin的判断:使用vm.startPrank(owner)时,msg.sender会被设置为owner,但tx.origin仍然是测试合约本身(测试合约是交易发起源头),导致两者不相等触发错误。
你可以通过以下两种方式解决:
方式一:用vm.startBroadcast替代vm.startPrank
vm.startBroadcast(owner)会模拟外部EOA直接发起交易,此时msg.sender和tx.origin都会被设置为owner,完全满足modifier的校验条件:
function testStartRound() public { vm.startBroadcast(owner); head_or_tail.startRound(0); vm.stopBroadcast(); }
方式二:调整modifier的校验逻辑
msg.sender == tx.origin的判断本身存在安全风险(易被钓鱼攻击利用),且会限制合约间的正常调用。如果业务逻辑不需要严格禁止代理调用,可以移除该判断,或者仅在非测试环境启用:
modifier notContract() { require(!_isContract(msg.sender), "Contract not allowed"); // 仅在非测试环境校验tx.origin #if !TEST require(msg.sender == tx.origin, "Proxy contract not allowed"); #endif _; }
使用该方式需在编译时添加--define TEST=true参数,或在Foundry配置文件中设置对应编译选项。
补充:确认_isContract实现正确性
确保你的_isContract函数没有误将EOA识别为合约,标准实现如下:
function _isContract(address account) internal view returns (bool) { uint256 size; assembly { size := extcodesize(account) } return size > 0; }
内容的提问来源于stack exchange,提问作者Svyatoslav
相关产品推荐
相关产品推荐

