如何在Foundry测试中Mock智能合约的嵌套调用函数?
Foundry中Mock Uniswap V2 Pair内部嵌套调用的getReserves()问题
问题场景
在Foundry测试中,通过vm.mockCall直接Mock getReserves()函数时,外部调用能获取预期值,但调用Pair合约的swap()函数时,其内部嵌套调用的getReserves()却读取了链上真实储备值,而非Mock结果:
有效Mock代码(直接调用getReserves()时生效)
vm.mockCall( poolAddress, abi.encodeWithSelector(IUniswapV2Pair.getReserves.selector), abi.encode(3000000000000000000, 9000000000000000000000) );
测试swap函数的代码(内部getReserves()未使用Mock值)
IUniswapV2Pair(poolAddress).swap( 0, amountOut, address(this), new bytes(0) );
问题原因
vm.mockCall仅能拦截外部对合约函数的调用,而Uniswap V2 Pair合约的swap()内部调用的getReserves()是读取合约自身的存储变量(_reserve0、_reserve1),属于合约内部逻辑,不会触发外部调用拦截,因此Mock不生效。
解决方案
方案1:直接修改Pair合约的存储值
通过vm.store手动设置Pair合约存储中储备值对应的slot,让内部读取的存储值符合预期:
Uniswap V2 Pair的存储布局中,储备值和最后更新时间存储在slot 0,结构为:
uint112 reserve0(占前112位)uint112 reserve1(占中间112位)uint32 blockTimestampLast(占最后32位)
代码示例:
// 定义预期的储备值 uint256 expectedReserve0 = 3000000000000000000; uint256 expectedReserve1 = 9000000000000000000000; uint32 expectedTimestamp = uint32(block.timestamp); // 编码slot 0的完整值 uint256 slot0Value = (expectedReserve0 << 216) | (expectedReserve1 << 104) | expectedTimestamp; // 写入Pair合约的slot 0 vm.store(poolAddress, bytes32(uint256(0)), bytes32(slot0Value));
这样swap()内部调用getReserves()时,读取的就是我们设置的存储值。
方案2:部署本地Mock Pair合约
不使用链上真实的Pair合约,而是在测试中部署一个自定义的Mock Pair合约,重写getReserves()和swap()逻辑,完全控制内部行为:
contract MockUniswapV2Pair is IUniswapV2Pair { uint112 public reserve0; uint112 public reserve1; function setReserves(uint112 _reserve0, uint112 _reserve1) external { reserve0 = _reserve0; reserve1 = _reserve1; } function getReserves() external view returns (uint112, uint112, uint32) { return (reserve0, reserve1, uint32(block.timestamp)); } // 实现swap函数的逻辑,按需自定义 function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external { // 这里可以使用我们设置的reserve0/reserve1进行计算 // 省略其他逻辑,或模拟真实swap的行为 } } // 在测试中部署并使用 function testSwapWithMockedReserves() public { MockUniswapV2Pair mockPair = new MockUniswapV2Pair(); mockPair.setReserves(3000000000000000000, 9000000000000000000000); // 调用mockPair的swap函数,内部getReserves会返回设置的值 mockPair.swap(0, amountOut, address(this), new bytes(0)); }
内容的提问来源于stack exchange,提问作者AnastasiaShishkova
相关产品推荐
相关产品推荐

