11use crate :: executors:: {
2- EarlyExit , Executor ,
2+ EarlyExit , EvmError , Executor , RawCallResult ,
33 invariant:: { call_after_invariant_function, call_invariant_function, execute_tx} ,
44} ;
55use alloy_primitives:: { Address , Bytes , I256 , U256 } ;
66use foundry_config:: InvariantConfig ;
7- use foundry_evm_core:: { FoundryBlock , constants:: MAGIC_ASSUME , evm:: FoundryEvmNetwork } ;
7+ use foundry_evm_core:: {
8+ FoundryBlock , constants:: MAGIC_ASSUME , decode:: RevertDecoder , evm:: FoundryEvmNetwork ,
9+ } ;
810use foundry_evm_fuzz:: { BasicTxDetails , invariant:: InvariantContract } ;
911use indicatif:: ProgressBar ;
1012use proptest:: bits:: { BitSetLike , VarBitSet } ;
@@ -122,13 +124,16 @@ pub(crate) fn shrink_sequence<FEN: FoundryEvmNetwork>(
122124 shrinker. current ( ) . collect ( ) ,
123125 target_address,
124126 calldata. clone ( ) ,
125- config. fail_on_revert ,
126- invariant_contract. call_after_invariant ,
127+ CheckSequenceOptions {
128+ fail_on_revert : config. fail_on_revert ,
129+ call_after_invariant : invariant_contract. call_after_invariant ,
130+ rd : None ,
131+ } ,
127132 ) {
128133 // If candidate sequence still fails, shrink until shortest possible.
129- Ok ( ( false , _) ) if shrinker. included_calls . count ( ) == 1 => break ,
134+ Ok ( ( false , _, _ ) ) if shrinker. included_calls . count ( ) == 1 => break ,
130135 // Restore last removed call as it caused sequence to pass invariant.
131- Ok ( ( true , _) ) => shrinker. included_calls . set ( call_idx) ,
136+ Ok ( ( true , _, _ ) ) => shrinker. included_calls . set ( call_idx) ,
132137 _ => { }
133138 }
134139
@@ -154,9 +159,8 @@ pub fn check_sequence<FEN: FoundryEvmNetwork>(
154159 sequence : Vec < usize > ,
155160 test_address : Address ,
156161 calldata : Bytes ,
157- fail_on_revert : bool ,
158- call_after_invariant : bool ,
159- ) -> eyre:: Result < ( bool , bool ) > {
162+ options : CheckSequenceOptions < ' _ > ,
163+ ) -> eyre:: Result < ( bool , bool , Option < String > ) > {
160164 // Apply the call sequence.
161165 for call_index in sequence {
162166 let tx = & calls[ call_index] ;
@@ -165,22 +169,51 @@ pub fn check_sequence<FEN: FoundryEvmNetwork>(
165169 // Ignore calls reverted with `MAGIC_ASSUME`. This is needed to handle failed scenarios that
166170 // are replayed with a modified version of test driver (that use new `vm.assume`
167171 // cheatcodes).
168- if call_result. reverted && fail_on_revert && call_result. result . as_ref ( ) != MAGIC_ASSUME {
172+ if call_result. reverted
173+ && options. fail_on_revert
174+ && call_result. result . as_ref ( ) != MAGIC_ASSUME
175+ {
169176 // Candidate sequence fails test.
170177 // We don't have to apply remaining calls to check sequence.
171- return Ok ( ( false , false ) ) ;
178+ return Ok ( ( false , false , call_failure_reason ( call_result , options . rd ) ) ) ;
172179 }
173180 }
174181
175182 // Check the invariant for call sequence.
176- let ( _, mut success) = call_invariant_function ( & executor, test_address, calldata) ?;
183+ let ( invariant_result, mut success) =
184+ call_invariant_function ( & executor, test_address, calldata) ?;
185+ if !success {
186+ return Ok ( ( false , true , call_failure_reason ( invariant_result, options. rd ) ) ) ;
187+ }
188+
177189 // Check after invariant result if invariant is success and `afterInvariant` function is
178190 // declared.
179- if success && call_after_invariant {
180- ( _, success) = call_after_invariant_function ( & executor, test_address) ?;
191+ if success && options. call_after_invariant {
192+ let ( after_invariant_result, after_invariant_success) =
193+ call_after_invariant_function ( & executor, test_address) ?;
194+ success = after_invariant_success;
195+ if !success {
196+ return Ok ( ( false , true , call_failure_reason ( after_invariant_result, options. rd ) ) ) ;
197+ }
181198 }
182199
183- Ok ( ( success, true ) )
200+ Ok ( ( success, true , None ) )
201+ }
202+
203+ pub struct CheckSequenceOptions < ' a > {
204+ pub fail_on_revert : bool ,
205+ pub call_after_invariant : bool ,
206+ pub rd : Option < & ' a RevertDecoder > ,
207+ }
208+
209+ fn call_failure_reason < FEN : FoundryEvmNetwork > (
210+ call_result : RawCallResult < FEN > ,
211+ rd : Option < & RevertDecoder > ,
212+ ) -> Option < String > {
213+ match call_result. into_evm_error ( rd) {
214+ EvmError :: Execution ( err) => Some ( err. reason ) ,
215+ _ => None ,
216+ }
184217}
185218
186219/// Shrinks a call sequence to the shortest sequence that still produces the target optimization
0 commit comments