Closed 0xBrian closed 4 months ago
Thanks for the report! Could you let us know exactly the error you are getting? Is is the last assertion that fails or is there some other error message?
It's just the assertion that doesn't pass, no error message. I think test1
and test2
should either both pass, or both fail. Thanks.
Hey @0xBrian, I investigated the issue. Thanks for the reproducible example, it was really helpful!
I actually tried fetching the source and luckily the contract was verified on the explorer so I could take a look at how it worked, and noticed that the DIGEST
is computed using a few items from the contract state, but also the msg.sender
.
Unfortunately, the answer to your issue is rather bland: prank
simply doesn't set the msg.sender
for nested calls!
So, in test2
, the msg.sender
would end up being the address where Blah
was deployed, and not MINER
, as it would be in the test1
.
I hope this helps!
OK, thank you for investigating. We figured it was something like that. But does it work this way in normal Foundry, or is it something related to the changes required for ZKSync? To me it seems like this behavior would prevent anyone from writing realistic tests.
Yes, that's intended foundry behaviour, we can also see this in the test suite here.
What we do require for ZKSync is that any contract running inside zkVM isn't able to use cheatcodes (in this case Blah
), but you can work around that by disabling zkVM before the call to the external contract, run your cheatcodes there, and re-enable zkVM afterwards.
For example:
vm.zkVm(false);
blah.doit(...);
and inside doit
:
vm.zkVm(true); //here or after prank is fine either way
// the important part is that `doit` isn't run inside zkVM
vm.prank(MINER);
...
As far as realistic test, I'm not sure I understand what you mean. If instead of Blah
you had another contract, on chain, which handled the calls to MINING_CONTRACT_ADDRESS
somehow, shouldn't you be calling tha contract instead then? And if instead it was some other kind of program off chain, wouldn't that basically be Test1
?
You can always create other functions in the Test1
contract to group functionality, and as long as it's not prefixed with test
(or a few others like invariant
) then it won't be executed as a standalone test.
I'll go ahead and close the issue since we clarified it's intended behaviour, but if you have any more questions you can just comment here on the issue and I'll still reply :)
Component
Forge
Have you ensured that all of these are up to date?
What version of Foundry are you on?
forge 0.0.2 (0989cdc 2024-06-20T00:20:02.912238865Z)
What command(s) is the bug in?
forge test --zksync -vvvv --use 0.8.20
Operating System
Linux
Describe the bug
Above is
test/Whatever.sol
in a stockforge init
project, except that I added the following tofoundry.toml
:I'm testing with
forge test --zksync -vvvv --use 0.8.20
.This code is about replaying this transaction: https://era.zksync.network/tx/0x14f963c64f1800aa2d8b938556ba662daecf55801869d96903398639d7a888a8
That tx was submitted in block
36_852_529
by0xFaf20E5cA7E39d43A3aabc450602b4147c3aA62E
. Without getting into the weeds about what the destination contract does, after the call tomultiMint_SameAddress
, there was an observable change, an additional entry in the map calledusedCombinations
. So after that call,usedCombinations(0x000000000000a8d299d120846e9c48444fbe330d9407a8c005848c491b2556f5)
becametrue
. It's still true, which we can see at https://era.zksync.network/address/0x366d17adb24a7654dbe82e79f85f9cb03c03cd0d#readContract#F47 .Test1 / test1
represents my first attempt to replay the tx. It works like I expect. I go back to a block or two before the tx and do exactly what it did. Before the call, the entry is not in the map, and after the call, the entry is in the map. OK.Test2 / test2
is a contrived attempt to show what happens when I start taking simplistic caveman code and splitting it up into sensical parts -- it stops working. It seems to stop being able to see the changes in the contract I'm testing. Can you help me understand whytest1
works andtest2
doesn't? Thanks.