foundry-rs / foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
https://getfoundry.sh
Apache License 2.0
8.25k stars 1.73k forks source link

createForkStepAccount: create a fork with an address replaced #4761

Open joaquinlpereyra opened 1 year ago

joaquinlpereyra commented 1 year ago

Component

Forge

Describe the feature you would like

As an auditor, the contract-under-test deployment is not under my control. Nor are all of the contracts-that-interact with my contract-under-test. I do have access to their source code.

One common idea when testing is to try to test different configurations which are set on contract initialization. One of the simplest way I can think to simplify these tests is to create a new instance of the contract-under-test (either via new() or using minimal proxies depending on the context) and then perform my tests.

The problem is that when you create a new instance of the contract, you of course get a new address. This makes all contracts that interact with your contract-under-test interact with the old instance of the test, which is not desired.

As such, I would like to have the possibility to fork to a new state where the AccountInfo of the address originalAddr is replaced by the AccountInfo of the address newAddr. In this way, all contracts that point to originalAddr would still work and I would be able to programatically test different configurations of the contracts under test.

This cannot be done by forking from an state where the contract does not exist, as it is possible that contracts that interact with my contract under test do not exist at that point either.

This can also not be done by etch, as etch only modifies the code and not the storage.

This can not be done by StdStorage, as it requires specific keys. I want to wipe all of the storage.

One of the most radical workarounds I thought of was using etch to replace the contract code with a single selfdestruct() call; but this does not work as the actual deletion is reserved for the end of the transaction... and when testing we are always inside the same transaction; so this does not work either.

Additional context

No response

joaquinlpereyra commented 1 year ago

I have edited for clarity, as constructors() are annoying in this case. The idea would be to be able to set an already-existing address to an arbitrary new account.

grandizzy commented 2 weeks ago

@joaquinlpereyra we have now vm.cloneAccount cheatcode which seems to be what you're looking for (clones everything form source, including storage, balance, code) (https://github.com/foundry-rs/foundry/issues/4893) see a test here https://github.com/foundry-rs/foundry/blob/15fdb2a19ee2a038f7e72523c6a0b0c3cdc6c3e4/testdata/default/cheats/CloneAccount.t.sol#L23-L45

please check if that works for your case too. thank you!