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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 05:53:21