如何测试带动态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
相关产品推荐
相关产品推荐

