-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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: snapshot and revert cheatcodes #1162
Comments
Yes I mentioned this. You can do this without cheatcodes via a revert though. |
Yea, think it's not the best pattern, but certainly could be that it serves ppl's needs well enough for the moment. Out of curiosity, what's the use case you have? |
This is pretty hard to do, and if it's solveable without cheatcodes, I'm not sure it's a good idea for us to implement it given the complexity. It might be better to publish a package that has helpers for this instead. cc @gakonst on thoughts on this In terms of your two first examples as well, is there any reason you would not expose information like that in a view function? And for the third, I'm not sure I completely understand - can you elaborate? |
Fair, tbh if we can solve it with a helper function then agreed that meets the need and feels better
Yea, some projects choose not to add a second path for view-only. I know uni v3 requires you to do the revert to get the price (zefram tweet), whereas balancer has a parallel view path
yea, was thinking: fork mainnet -> execute maker gov proposal e.g. (which changes parameters) -> take some action (pay back dai debt) and read the resulting state -> revert back and use this value for some check. In fairness, this example is more contrived than the others and I haven't run into it personally |
+1 for this, missing too when migrated from brownie I am actually using the following workaround based on It leverages an ancillary contract where you have to write (test) block statements where you revert Fully solidity no cheat codes pragma solidity ^0.8.0;
import {DSTest} from "ds-test/test.sol";
contract SimpleContext {
uint256 public number;
function setAndRevert(uint256 num) external {
number = num;
revert("ouch");
}
}
contract SharedSetup is DSTest {
SimpleContext context;
function setUp() public {
context = new SimpleContext();
}
function testSnapshot() external {
uint256 preNum = context.number();
uint256 num = 5;
try context.setAndRevert(num) {} catch {
assertEq(context.number(), preNum);
}
}
} |
Here is a use case: https://github.com/makerdao/dss-cron/blob/add-oracle-mom/src/ClipperMomJob.sol#L73 Basically when you have to run something to see if it will succeed or not and you don't want it to actually execute in the tests. In the example above |
This is extremely hard. I'm not sure how we would revert mid-execution and then start a new execution from a specific point. No real way to do that in REVM right now, so we'd have to figure out a way to add that upstream as well. I'm leaning heavily towards keeping this as a community utility contract instead of adding the cheatcodes. We'd need to:
We have a way to snapshot the memory and the stack, no real way to snapshot all the addresses and storage, and no real way to resume execution at some program counter It would add a lot of complexity both in REVM and Foundry, so again, really hesitant - I understand the use case, but since this can be solved using try/catch, I don't see a good reason to add it given the complexity. It would also not work well with DSTest, which could lead to a lot of confusion. For example, if you make a DSTest assertion that fails, and then call |
Also cc @rakita if there is a non-complicated way to implement this with how REVM currently is - maybe you have some ideas :) |
Thanks for linking this existing issue @onbjerg.
|
Are you sure? They should - does the reason actually match? |
@onbjerg Yes, test passes fine even though the assertions in Edit: Looks like this is because the failure is getting stored to the HEVM address, which will also be reverted. Can probably revert with the value at that slot instead and check if it's true. Edit 2: Reverting with HEVM storage slot and checking that the value isn't true seems to work nicely, actually! |
Just saw this. :) I see, this seems tricky to do and covers all edge cases, for account+storage you could 'just' clone SubRoutine that is where storage changes are, but you will need to be aware of where snapshot is called, it is hard to jump from one contract subroutine to another, selfdestruct is one more thing you need to be aware. Maybe we can do it in another way to leverage EVM revert mechanism, do something as breakpoint system:
|
@jameswenzel I think you want |
It would be amazing to be able to access the state diff since the last snapshot. The UX that I'm thinking of:
Then the state diff could be looked at line by line to ensure that the upgrade is exactly what is desired |
@tynes you can already do that with the |
Component
Forge
Describe the feature you would like
Cheatcodes that allow you to take a snapshot, then revert the executer's state back to the snapshot. One possible API is:
which would be similar to Ganache's
evm_snapshot
/evm_revert
The use case is to do some state changing operation(s), derive a value, then revert the changes (similar to using
callStatic
using ethers). It feels like the tricky part might be to revert state while preserving values assigned in the test contract before the reversion, but it's been a while since I looked through foundry's codebase ~@hexonaut I think you mentioned this in chat a while ago as well?
Additional context
Example uses include:
I know some people have achieved the same effect by passing a value in as a revert message and catching it, a bit like uni's quoter v2 does, but this feature would enable a cleaner pattern
The text was updated successfully, but these errors were encountered: