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(forge): change startPrank to overwrite existing prank instead of erroring #4826

Merged
merged 16 commits into from
May 11, 2023

Conversation

4meta5
Copy link
Contributor

@4meta5 4meta5 commented Apr 26, 2023

Closes #4779

Motivation

There is user demand for a startPrank or changePrank cheatcode that allows optimistically setting a new prank such that any existing prank is overwritten and there is no error if the existing prank is not set.

Solution

startPrank is changed to allow overwriting existing pranks instead of erroring. It is not able to overwrite existing pranks unless the existing prank has already been applied at least once.

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

codewise lgtm,

wdyt @mds1

@mds1
Copy link
Collaborator

mds1 commented Apr 26, 2023

Should we just change the behavior of startPrank to work this way instead of introducing a new cheat? It feels intuitive that startPrank would end any active pranks in order to start a new one. Then we'd also want a PR to forge-std that updates changePrank to just revert explaining this breaking change. cc @PaulRBerg

Also it looks like this only adds startChangePrank(address,address), but we also need startChangePrank(address) (or, if we just change startPrank, make sure to change behavior in both

Also one edge case to make sure we handle is:

vm.startPrank(msgSender, txOrigin);
// --- do stuff ---

// now we change prank, but need to make sure to reset txOrigin
vm.startPrank(msgSender); 

@PaulRBerg
Copy link
Contributor

Should we just change the behavior of startPrank to work this way

Works for me

…rank instead of erroring as per review suggestion
@4meta5 4meta5 changed the title feat(forge): cheat that starts or changes the prank feat(forge): change startPrank to overwrite existing prank instead of erroring Apr 27, 2023
Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

lgtm,

pending @mds1 and lint

@mds1
Copy link
Collaborator

mds1 commented Apr 28, 2023

Looks good! Just two questions:

  1. What if I call vm.prank immediately followed by vm.prank (same question if either/both of those are replaced with startPrank)? I think we should make sure that reverts, because the first prank was never used
  2. I don't see new tests for startPrank, just tests for prank, so just want to confirm the above question, which was:

Also one edge case to make sure we handle is:

vm.startPrank(msgSender, txOrigin);
// --- do stuff ---

// now we change prank, but need to make sure to reset txOrigin
vm.startPrank(msgSender); 

@4meta5
Copy link
Contributor Author

4meta5 commented Apr 28, 2023

Both suggestions are reasonable.

  1. What if I call vm.prank immediately followed by vm.prank (same question if either/both of those are replaced with startPrank)? I think we should make sure that reverts, because the first prank was never used

Agreed, implemented in 15ac502

Will add tests for this as well

  1. I don't see new tests for startPrank, just tests for prank, so just want to confirm the above question, which was:

Also one edge case to make sure we handle is:

vm.startPrank(msgSender, txOrigin);
// --- do stuff ---

// now we change prank, but need to make sure to reset txOrigin
vm.startPrank(msgSender); 

Will add tests for startPrank as well

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

lgtm, this seems to work!

just rebased because we landed a big pr on master

Will add tests for startPrank as well

this is the only thing missing, right?

@4meta5
Copy link
Contributor Author

4meta5 commented Apr 29, 2023

cc @mds1 How do we use a prank in the tests? Specifically, I want to invoke Cheatcodes::call such that the prank is applied here https://github.com/foundry-rs/foundry/pull/4826/files#diff-3a6637039aa2043110b6bf828cf9c802d7621bdd472a536ea43f56421026eb64R597-R616 ? This would require executing a call that that uses the prank. I don't see any example of this in Prank.t.sol.

I do expect the added tests to fail now because we added an error for calls to prank when an existing prank has been added and not yet applied (suggestion 1). So until we add the call between successive startPranks, they will fail.

@Evalir
Copy link
Member

Evalir commented May 10, 2023

hey hey @4meta5 thanks for the work! I've pushed some commits to your branch to get it over the line, hope you don't mind:

  • rebased to make sure it's up to par with master
  • added the startPrank tests and tweaked an error being returned to use one of our internal utils.

cc @mattsse / @mds1 should be g2g bar CI (github is having problems)

@mds1
Copy link
Collaborator

mds1 commented May 10, 2023

@Evalir just confirming if you or @4meta5 handled these items too / have test cases for them? #4826 (comment)

@Evalir
Copy link
Member

Evalir commented May 10, 2023

Ah whoops, seems I stashed & didn't upload that edge case: It is handled, but just pushed the missing test

Comment on lines +173 to +193
function testStartPrank0AfterPrank1(address sender, address origin) public {
// Perform the prank
address oldOrigin = tx.origin;
Victim victim = new Victim();
cheats.startPrank(sender, origin);
victim.assertCallerAndOrigin(
sender, "msg.sender was not set during prank", origin, "tx.origin was not set during prank"
);

// Overwrite the prank
cheats.startPrank(sender);
victim.assertCallerAndOrigin(
sender, "msg.sender was not set during prank", oldOrigin, "tx.origin invariant failed"
);

cheats.stopPrank();
// Ensure we cleaned up correctly after stopping the prank
victim.assertCallerAndOrigin(
address(this), "msg.sender was not cleaned up", oldOrigin, "tx.origin invariant failed"
);
}
Copy link
Member

Choose a reason for hiding this comment

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

Just to confirm: this should be the edge case you're talking about @mds1 right?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yep that's it, thanks!

Copy link
Member

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

sweet,

tysm @4meta5

@mattsse mattsse merged commit c1dbafd into foundry-rs:master May 11, 2023
@PaulRBerg
Copy link
Contributor

This is awesome! I'm glad that this was implemented.

Am I correct that now I can replace all of our uses of changePrank with vm.startPrank?

@mds1
Copy link
Collaborator

mds1 commented May 11, 2023

Yep you should be able to!

We should also get a PR in to forge-std that logs a deprecation warning when using changePrank, and open a forge-std issue to track deleting it eventually. cc @Evalir if you're able to do that

@urataps
Copy link

urataps commented Aug 31, 2023

Cool feature, but should contract deployment count also as if state was applied?

Currently I have some code that fails with this:

vm.startPrank(owner);
// deploy some contracts

vm.startPrank(stranger)
// check some stuff

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat(forge): cheat that starts or changes the prank
6 participants