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

含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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 01:54:55