@@ -136,24 +136,28 @@ Hence, checking if a `DSTest.assert*` has failed amounts to reading as a boolean
136
136
module FOUNDRY-SUCCESS
137
137
imports EVM
138
138
139
- syntax Bool ::= "foundry_success" "(" StatusCode "," Int ")" [function, klabel(foundry_success), symbol]
140
- // --------------------------------------------------------------------------------------------------------
141
- rule foundry_success(EVMC_SUCCESS, 0) => true
142
- rule foundry_success(_, _) => false [owise]
139
+ syntax Bool ::= "foundry_success" "(" StatusCode "," Int "," Bool " )" [function, klabel(foundry_success), symbol]
140
+ // -----------------------------------------------------------------------------------------------------------------
141
+ rule foundry_success(EVMC_SUCCESS, 0, false ) => true
142
+ rule foundry_success(_, _, _) => false [owise]
143
143
144
144
endmodule
145
145
```
146
146
147
147
### Foundry Cheat Codes
148
148
149
149
The configuration of the Foundry Cheat Codes is defined as follwing:
150
- 1 . The ` <prank> ` subconfiguration stores values which are used during the execution of any kind of ` prank ` cheatcode :
150
+ 1 . The ` <prank> ` subconfiguration stores values which are used during the execution of any kind of ` prank ` cheat code :
151
151
- ` <prevCaller> ` keeps the current address of the contract that initiated the prank.
152
152
- ` <prevOrigin> ` keeps the current address of the ` tx.origin ` value.
153
153
- ` <newCaller> ` and ` <newOrigin> ` are addresses to be assigned after the prank call to ` msg.sender ` and ` tx.origin ` .
154
154
- ` <active> ` signals if a prank is active or not.
155
155
- ` <depth> ` records the current call depth at which the prank was invoked.
156
156
- ` <singleCall> ` tells whether the prank stops by itself after the next call or when a ` stopPrank ` cheat code is invoked.
157
+ 2 . The ` <expected> ` subconfiguration stores values used for the ` expectRevert ` cheat code.
158
+ - ` <expectedRevert> ` flags if the next call is expected to revert or not.
159
+ - ` <expectedDepth> ` records the depth at which the call is expected to revert.
160
+ - ` <expectedBytes> ` keeps the expected revert message as a ByteArray.
157
161
158
162
``` k
159
163
module FOUNDRY-CHEAT-CODES
@@ -172,6 +176,11 @@ module FOUNDRY-CHEAT-CODES
172
176
<depth> 0 </depth>
173
177
<singleCall> false </singleCall>
174
178
</prank>
179
+ <expected>
180
+ <expectedRevert> false </expectedRevert>
181
+ <expectedBytes> .ByteArray </expectedBytes>
182
+ <expectedDepth> 0 </expectedDepth>
183
+ </expected>
175
184
</cheatcodes>
176
185
```
177
186
@@ -469,6 +478,67 @@ This rule then takes from the function call data the account using `#asWord(#ran
469
478
requires SELECTOR ==Int selector ( "store(address,bytes32,bytes32)" )
470
479
```
471
480
481
+ #### expectRevert - expect the next call to revert.
482
+
483
+ ```
484
+ function expectRevert() external;
485
+ function expectRevert(bytes4 msg) external;
486
+ function expectRevert(bytes calldata msg) external;
487
+ ```
488
+
489
+ Ignore all cheat code calls which take place while ` expectRevert ` is active.
490
+
491
+ ``` {.k .bytes}
492
+ rule [foundry.call.ignoreCalls]:
493
+ <k> #call_foundry _ _ => . ... </k>
494
+ <expected>
495
+ <expectedRevert> true </expectedRevert>
496
+ ...
497
+ </expected>
498
+ [priority(35)]
499
+ ```
500
+
501
+ Catch reverts.
502
+ If the current call depth is equal with the ` expectedDepth ` and the ` expectedBytes ` match the ` <output> ` cell, then replace the ` EVMC_REVERT ` status code with ` EVMC_SUCCESS ` .
503
+
504
+ ``` {.k .bytes}
505
+ rule <statusCode> EVMC_REVERT => EVMC_SUCCESS </statusCode>
506
+ <k> #halt ~> #return _RETSTART _RETWIDTH ... </k>
507
+ <output> OUT </output>
508
+ <callDepth> CD </callDepth>
509
+ <expected>
510
+ <expectedRevert> true => false </expectedRevert>
511
+ <expectedDepth> CD </expectedDepth>
512
+ <expectedBytes> EXPECTED </expectedBytes>
513
+ </expected>
514
+ requires (EXPECTED =/=K .ByteArray andBool EXPECTED ==K #range(OUT, 4, #sizeByteArray(OUT) -Int 4))
515
+ orBool EXPECTED ==K .ByteArray
516
+ [priority(40)]
517
+ ```
518
+
519
+ Change the status code from ` EVMC_SUCCESS ` to ` EVMC_REVERT ` if a revert is expected but the call succeded.
520
+
521
+ ``` {.k .bytes}
522
+ rule <statusCode> EVMC_SUCCESS => EVMC_REVERT </statusCode>
523
+ <k> #halt ~> #return _RETSTART _RETWIDTH ... </k>
524
+ <callDepth> CD </callDepth>
525
+ <expected>
526
+ <expectedRevert> true => false </expectedRevert>
527
+ <expectedDepth> CD </expectedDepth>
528
+ ...
529
+ </expected>
530
+ [priority(40)]
531
+ ```
532
+
533
+ If the ` expectRevert() ` selector is matched, call the ` #setExpectRevert ` production to initialize the ` <expected> ` subconfiguration.
534
+
535
+ ``` {.k .bytes}
536
+ rule [foundry.call.expectRevert]:
537
+ <k> #call_foundry SELECTOR ARGS => #setExpectRevert ARGS ... </k>
538
+ requires SELECTOR ==Int selector ( "expectRevert()" )
539
+ orBool SELECTOR ==Int selector ( "expectRevert(bytes)" )
540
+ ```
541
+
472
542
Pranks
473
543
------
474
544
@@ -652,6 +722,20 @@ Utils
652
722
</account>
653
723
```
654
724
725
+ - ` #setExpectRevert ` sets the ` <expected> ` subconfiguration with the current call depth and the expected message from ` expectRevert ` .
726
+
727
+ ``` k
728
+ syntax KItem ::= "#setExpectRevert" ByteArray [klabel(foundry_setExpectedRevert)]
729
+ // ---------------------------------------------------------------------------------
730
+ rule <k> #setExpectRevert EXPECTED => . ... </k>
731
+ <callDepth> CD </callDepth>
732
+ <expected>
733
+ <expectedRevert> false => true </expectedRevert>
734
+ <expectedDepth> _ => CD +Int 1 </expectedDepth>
735
+ <expectedBytes> _ => EXPECTED </expectedBytes>
736
+ </expected>
737
+ ```
738
+
655
739
- ` #setPrank NEWCALLER NEWORIGIN ` will set the ` <prank/> ` subconfiguration for the given accounts.
656
740
657
741
``` k
@@ -694,7 +778,6 @@ If the production is matched when no prank is active, it will be ignored.
694
778
<prevOrigin> OG </prevOrigin>
695
779
...
696
780
</prank>
697
-
698
781
```
699
782
700
783
- ` #clearPrank ` will clear the prank subconfiguration.
@@ -731,6 +814,8 @@ If the production is matched when no prank is active, it will be ignored.
731
814
rule ( selector ( "load(address,bytes32)" ) => 1719639408 )
732
815
rule ( selector ( "store(address,bytes32,bytes32)" ) => 1892290747 )
733
816
rule ( selector ( "setNonce(address,uint64)" ) => 4175530839 )
817
+ rule ( selector ( "expectRevert()" ) => 4102309908 )
818
+ rule ( selector ( "expectRevert(bytes)" ) => 4069379763 )
734
819
rule ( selector ( "startPrank(address)" ) => 105151830 )
735
820
rule ( selector ( "startPrank(address,address)" ) => 1169514616 )
736
821
rule ( selector ( "stopPrank()" ) => 2428830011 )
@@ -757,13 +842,8 @@ If the production is matched when no prank is active, it will be ignored.
757
842
rule selector ( "envString(string,string)" ) => 347089865
758
843
rule selector ( "envBytes(string,string)" ) => 3720504603
759
844
rule selector ( "prank(address)" ) => 3395723175
760
- rule selector ( "startPrank(address)" ) => 105151830
761
845
rule selector ( "prank(address,address)" ) => 1206193358
762
- rule selector ( "startPrank(address,address)" ) => 1169514616
763
- rule selector ( "stopPrank()" ) => 2428830011
764
- rule selector ( "expectRevert(bytes)" ) => 4069379763
765
846
rule selector ( "expectRevert(bytes4)" ) => 3273568480
766
- rule selector ( "expectRevert()" ) => 4102309908
767
847
rule selector ( "record()" ) => 644673801
768
848
rule selector ( "accesses(address)" ) => 1706857601
769
849
rule selector ( "expectEmit(bool,bool,bool,bool)" ) => 1226622914
0 commit comments