@@ -79,6 +79,119 @@ fn recursive_call_info_iter(res: &TransactionExecutionInfo) -> impl Iterator<Ite
7979 . flat_map ( |call_info| call_info. iter ( ) ) // flatmap over the roots' recursive inner call infos
8080}
8181
82+ /// Formats the revert error string by removing redundant VM tracebacks.
83+ ///
84+ /// The blockifier generates verbose error messages with VM tracebacks at every level
85+ /// of the call stack. This function filters them to show the traceback only once,
86+ /// positioned after the last regular contract call (CallContract) entry point frame
87+ /// and before any library call (LibraryCall) frames or the final error.
88+ ///
89+ /// This makes error messages more concise while preserving the most relevant debugging information.
90+ fn format_revert_error ( error_string : & str ) -> String {
91+ // Check if the original string ends with a newline
92+ let ends_with_newline = error_string. ends_with ( '\n' ) ;
93+
94+ let lines: Vec < & str > = error_string. lines ( ) . collect ( ) ;
95+ let mut output_lines = Vec :: new ( ) ;
96+ let mut i = 0 ;
97+
98+ while i < lines. len ( ) {
99+ let line = lines[ i] ;
100+
101+ // Check if this is the start of a VM exception frame
102+ // VM exception frames start with "Error at pc=..."
103+ if line. trim ( ) . starts_with ( "Error at pc=" ) {
104+ // Look ahead to see if there's another "Error in the called contract" entry
105+ // or "Error in a library call" coming up
106+ let mut has_nested_call_contract = false ;
107+ let mut j = i + 1 ;
108+
109+ // Scan forward to find the next entry point frame
110+ while j < lines. len ( ) {
111+ let next_line = lines[ j] . trim ( ) ;
112+
113+ // Found the next entry point - check if it's a CallContract or LibraryCall
114+ if next_line. starts_with ( "Error in the called contract" ) {
115+ has_nested_call_contract = true ;
116+ break ;
117+ } else if next_line. starts_with ( "Error in a library call" )
118+ || next_line. starts_with ( "Execution failed" )
119+ || next_line. starts_with ( "Entry point" ) {
120+ // Next entry is a library call or final error - don't skip this traceback
121+ has_nested_call_contract = false ;
122+ break ;
123+ }
124+
125+ // Keep scanning through the traceback lines
126+ if next_line. starts_with ( "Unknown location" )
127+ || next_line. starts_with ( "Cairo traceback" )
128+ || next_line. is_empty ( ) {
129+ j += 1 ;
130+ } else {
131+ // Reached a numbered entry like "1:" - check if it contains the patterns
132+ if let Some ( colon_pos) = next_line. find ( ':' ) {
133+ let after_colon = & next_line[ colon_pos + 1 ..] . trim ( ) ;
134+ if after_colon. starts_with ( "Error in the called contract" ) {
135+ has_nested_call_contract = true ;
136+ break ;
137+ } else if after_colon. starts_with ( "Error in a library call" ) {
138+ has_nested_call_contract = false ;
139+ break ;
140+ }
141+ }
142+ j += 1 ;
143+ }
144+ }
145+
146+ // Skip the VM traceback if there's a nested CallContract
147+ if has_nested_call_contract {
148+ // Skip this "Error at pc=..." line and all traceback lines until the next entry
149+ while i < lines. len ( ) {
150+ let current_line = lines[ i] . trim ( ) ;
151+ i += 1 ;
152+
153+ // Stop when we hit the next numbered entry or end
154+ if i < lines. len ( ) {
155+ let next_line = lines[ i] . trim ( ) ;
156+ if let Some ( first_char) = next_line. chars ( ) . next ( ) {
157+ if first_char. is_ascii_digit ( ) && next_line. contains ( ':' ) {
158+ break ;
159+ }
160+ }
161+ }
162+
163+ // Also stop at the end or at lines that look like new entries
164+ if current_line. is_empty ( ) && i < lines. len ( ) {
165+ let peek = lines[ i] . trim ( ) ;
166+ if let Some ( first_char) = peek. chars ( ) . next ( ) {
167+ if first_char. is_ascii_digit ( ) || peek. starts_with ( "Execution failed" ) {
168+ break ;
169+ }
170+ }
171+ }
172+ }
173+ } else {
174+ // Keep this traceback - it's before a library call or final error
175+ output_lines. push ( line) ;
176+ i += 1 ;
177+ }
178+ } else {
179+ // Not a VM traceback line - keep it
180+ output_lines. push ( line) ;
181+ i += 1 ;
182+ }
183+ }
184+
185+ let mut result = output_lines. join ( "\n " ) ;
186+
187+ // Preserve the trailing newline if the original had one
188+ if ends_with_newline {
189+ result. push ( '\n' ) ;
190+ }
191+
192+ result
193+ }
194+
82195pub fn from_blockifier_execution_info ( res : & TransactionExecutionInfo , tx : & Transaction ) -> TransactionReceipt {
83196 let price_unit = match blockifier_tx_fee_type ( tx) {
84197 FeeType :: Eth => PriceUnit :: Wei ,
@@ -196,7 +309,8 @@ pub fn from_blockifier_execution_info(res: &TransactionExecutionInfo, tx: &Trans
196309 } ;
197310
198311 let execution_result = if let Some ( reason) = & res. revert_error {
199- ExecutionResult :: Reverted { reason : reason. to_string ( ) }
312+ let formatted_reason = format_revert_error ( & reason. to_string ( ) ) ;
313+ ExecutionResult :: Reverted { reason : formatted_reason }
200314 } else {
201315 ExecutionResult :: Succeeded
202316 } ;
0 commit comments