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

如何测试带动态calldata数组参数的Solidity合约函数?

如何测试接收动态calldata数组的Solidity函数?

目标合约函数

function updateDeps(uint16 parent, uint16[] calldata deps) public

你尝试的测试代码(调用失败)

contract TestContract is MyContract {
    uint16[] deps;
    function testUpdateDeps() {
        deps.push(uint16(2));
        deps.push(uint16(3));
        deps.push(uint16(4));

        (bool success, bytes memory result) = address(this).delegatecall(abi.encodeWithSignature("updateDeps(uint16, uint16[])", 1, deps));
        Assert.ok(success, "should succeed");
    }
}

问题核心

你使用delegatecall完全没必要,而且这个调用方式会继承调用上下文,容易引发存储布局不匹配的问题,这也是调用失败的主要原因。另外,Solidity允许内部调用时用memory数组直接传递给calldata参数,编译器会自动处理数据转换,不需要额外的低级别调用。

正确测试方案

方案1:直接调用函数(最简洁可靠)

因为测试合约继承了MyContract,直接调用updateDeps即可,用memory数组传参,编译器会自动适配calldata参数:

contract TestContract is MyContract {
    function testUpdateDeps() public {
        // 创建memory数组并初始化
        uint16[] memory deps = new uint16[](3);
        deps[0] = 2;
        deps[1] = 3;
        deps[2] = 4;

        // 直接调用目标函数
        updateDeps(1, deps);
        
        // 可添加断言验证状态,例如检查依赖关系是否正确更新
        // 示例:假设合约有存储依赖的变量,可断言对应值是否符合预期
    }
}

方案2:模拟外部调用(用call替代delegatecall)

如果需要模拟外部账户调用合约的场景,用call而非delegatecall,同时用函数选择器替代签名字符串,避免拼写错误:

contract TestContract is MyContract {
    function testUpdateDeps() public {
        uint16[] memory deps = new uint16[](3);
        deps[0] = 2;
        deps[1] = 3;
        deps[2] = 4;

        // 使用call执行外部调用,用selector确保编码正确
        (bool success, ) = address(this).call(abi.encodeWithSelector(this.updateDeps.selector, 1, deps));
        Assert.ok(success, "调用成功");
    }
}

关键注意点

  • calldata只是参数的位置限定,内部调用时memory数组可以直接传递,编译器会自动处理数据复制。
  • delegatecall用于继承上下文的代码执行,不适合普通函数测试,容易引发存储冲突。
  • 用函数选择器(selector)比encodeWithSignature更可靠,避免因签名字符串写错导致的编码错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 01:45:36