-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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): Add expectEmit
cheatcode
#329
Conversation
Could the |
No. There can only be 4 topics (3 user defined, topic 0 is always the event signature) max emitted by an event as defined by the available evm opcodes. Anything marked as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested a few permutations locally and everything passed/failed as I'd expect, so functionality-wise this looks good to me.
Agreed this function signature makes sense and is the simplest approach. I can see how it's a little confusing in the case where your event doesn't have 4 topics and some inputs are therefore unused, but I think that's ok.
One nuisance of this approach is you have to re-declare your events in the test file as shown in the example here, but a workaround is to define all your contract's events in an interface and just inherit from that in your test contract
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
Writing a mini code walkthrough below for anyone following along, or if you want to add it as a doc in the code:
- User calls the
vm.expectEmit(...) cheatcode
. This pushes anExpectedEmit
to theexpected_emits
vector on the state, withlog = None
andfound = false
. The expecteddepth
isd+1
because it's expected to appear in the subcall and not in the current call. - User calls
emit Transfer(...)
(or whatever event they want to use). This triggers aevm.log
call on SputnikEVM which hits theif
branch in L1188 and populates the FIRST of theexpected_emits
wherelog = None
with the raw log that was emitted. (This is important because there may be >1 expected emits in a call) - Then the user makes the actual subcall, where an event gets emitted. By that time, there are no
expected_emit
withlog = None
, which means we hit theelse
branch which looks for the firstexpected_emit
withfound = false
, and proceeds to check that the actual emitted log topics / data match the ones in the foundexpected_emit
.
CAVEAT / LIMITATION:
This does not support matching on the data (unindexed topics) if there's >1 params, because it checks the entire concatenated data, vs giving granularity over specific event params which were unindexed. We are merging this as-is, but if people end up using it, we may need to get more granular.
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This is awesome! I've done a bunch of work on data decoding > 1 param for events and functions calle, if that's a large need I can try to contribute that utility. Would just need some time to get better at Rust lol. |
Closes #310
This works by grabbing the next emitted event. You specify if you want to match against topics 1-3 and the data in the call. You can expect multiple events by repeatedly doing
vm.expectEvent
then emitting an event prior to a call. After the call, if the expected events are found, it continues execution. Otherwise, if the expected logs aren't found, it reverts.