Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cheatcodes): Add vm.mockCalls to mock different return data for multiple calls #9024

Merged
merged 13 commits into from
Oct 7, 2024

Conversation

Tudmotu
Copy link
Contributor

@Tudmotu Tudmotu commented Oct 3, 2024

Closes #7467

Motivation

Today, we can mock the return value of a function call by using mockCall. Sometimes, it is useful to mock the same call twice with different return values.

Consider the following function:

function failsafeArb (IERC20 token, IERC20 arbToken) public {
    uint initialBalance = token.balanceOf(address(this));
    doArbitrage(token, arbToken);
    uint finalBalance = token.balanceOf(address(this));
    require(finalBalance > initialBalance, 'arbitrage failed');
}

Both invocations of token.balanceOf() have the same calldata. This means we cannot use two different mockCall to test this behavior.

With mockCalls we would test it like so:

bytes[] retdata = new bytes[](2);
retdata[0] = abi.encode(100 ether);
retdata[1] = abi.encode(99 ether);
vm.mockCalls(
    address(token),
    abi.encodeWithSelector(
        IERC20.balanceOf.selector,
        address(testContract)
    ),
    retdata
);

vm.expectRevert('arbitrage failed');
testContract.failsafeArb(token, arbToken);

Solution

I changed Cheatcodes::mocked_calls to have a type of HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>, note the VecDeque<...>.

I then went in inspector.rs and modified the part which handles the mocked calls, to pop the front mock return data as long as the stack has more than one element. This kept backward-compatibility in such a way which minimized the amount of necessary changes.

Cheatcodes checklist

The checklist from the cheatcodes development docs:

  • Add its Solidity definition(s) in cheatcodes/spec/src/vm.rs. Ensure that all structs and functions are documented, and that all function parameters are named. This will initially fail to compile because of the automatically generated match { ... } expression. This is expected, and will be fixed in the next step
  • Implement the cheatcode in cheatcodes in its category's respective module. Follow the existing implementations as a guide.
  • If a struct, enum, error, or event was added to Vm, update spec::Cheatcodes::new
  • Update the JSON interface by running cargo cheats twice. This is expected to fail the first time that this is run after adding a new cheatcode; see JSON interface
  • Write an integration test for the cheatcode in testdata/cheats/

@Tudmotu
Copy link
Contributor Author

Tudmotu commented Oct 4, 2024

Opened draft PRs for Foundry Book and Forge-Std.

@zerosnacks
Copy link
Member

Thanks for your PR! Implementation looks good, small note in foundry-rs/forge-std#615

Copy link
Collaborator

@grandizzy grandizzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, just a small comment. pending other reviews

crates/cheatcodes/src/evm/mock.rs Outdated Show resolved Hide resolved
Copy link
Member

@zerosnacks zerosnacks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! LGTM - pending other reviewers

Copy link
Collaborator

@grandizzy grandizzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, lgtm

crates/cheatcodes/src/evm/mock.rs Outdated Show resolved Hide resolved
crates/cheatcodes/src/evm/mock.rs Outdated Show resolved Hide resolved
Copy link
Member

@DaniPopes DaniPopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@DaniPopes DaniPopes enabled auto-merge (squash) October 7, 2024 13:59
@DaniPopes DaniPopes merged commit d7d9b40 into foundry-rs:master Oct 7, 2024
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

feat(cheatcodes): vm.mockCalls() for mocking multiple calls
4 participants