@@ -2,6 +2,8 @@ use crate::error::ExecutionError;
22use crate :: juno_state_reader:: JunoStateReader ;
33use blockifier:: execution:: contract_class:: TrackedResource ;
44use blockifier:: state:: state_api:: { StateReader , StateResult , UpdatableState } ;
5+ use blockifier:: transaction:: account_transaction:: ExecutionFlags ;
6+ use blockifier:: transaction:: objects:: HasRelatedFeeType ;
57use blockifier:: transaction:: transaction_execution:: Transaction ;
68use blockifier:: transaction:: transactions:: ExecutableTransaction ;
79use blockifier:: {
@@ -12,8 +14,14 @@ use blockifier::{
1214use starknet_api:: core:: ClassHash ;
1315use starknet_api:: executable_transaction:: AccountTransaction ;
1416use starknet_api:: execution_resources:: GasAmount ;
15- use starknet_api:: transaction:: fields:: { GasVectorComputationMode , ValidResourceBounds } ;
16- use starknet_api:: transaction:: { DeclareTransaction , DeployAccountTransaction , InvokeTransaction } ;
17+ use starknet_api:: transaction:: fields:: {
18+ AllResourceBounds , GasVectorComputationMode , ValidResourceBounds ,
19+ } ;
20+ use starknet_api:: transaction:: {
21+ DeclareTransaction , DeclareTransactionV3 , DeployAccountTransaction , DeployAccountTransactionV3 ,
22+ InvokeTransaction , InvokeTransactionV3 ,
23+ } ;
24+ use anyhow:: { Error , anyhow} ;
1725
1826pub fn process_transaction (
1927 txn : & mut Transaction ,
@@ -140,16 +148,12 @@ fn execute_transaction_with_binary_search<S>(
140148where
141149 S : UpdatableState ,
142150{
143- let initial_gas_limit = extract_l2_gas_limit ( transaction) ?;
144- let mut original_transaction = transaction. clone ( ) ;
151+ let initial_resource_bounds = extract_resource_bounds ( transaction) ?;
152+ let initial_gas_limit = initial_resource_bounds. l2_gas . max_amount ;
153+ let max_l2_gas_limit = calculate_max_l2_gas_covered ( transaction, block_context, state) ?;
145154
146155 // Simulate transaction execution with maximum possible gas to get actual gas usage.
147- set_l2_gas_limit ( transaction, GasAmount :: MAX ) ?;
148- // TODO: Consider getting the upper bound from the balance and not changing the execution flags
149- if let Transaction :: Account ( account_transaction) = transaction {
150- account_transaction. execution_flags . charge_fee = false ;
151- account_transaction. execution_flags . validate = false ;
152- }
156+ set_l2_gas_limit ( transaction, max_l2_gas_limit) ?;
153157
154158 let simulation_result =
155159 match simulate_execution ( transaction, state, block_context, error_on_revert) {
@@ -212,31 +216,35 @@ where
212216 Err ( SimulationError :: ExecutionError ( error) ) => return Err ( error) ,
213217 }
214218 } ;
215-
216- ( current_l2_gas_limit, tx_info, tx_state)
217- }
218- Err ( SimulationError :: ExecutionError ( error) ) => return Err ( error) ,
219- } ;
220- tx_state. abort ( ) ;
219+ ( current_l2_gas_limit, tx_info, tx_state)
220+ }
221+ Err ( SimulationError :: ExecutionError ( error) ) => return Err ( error) ,
222+ } ;
221223
222224 // If the computed gas limit exceeds the initial limit, revert the transaction.
223225 // The L2 gas limit is set to zero to prevent the transaction execution from succeeding
224226 // in the case where the user defined gas limit is less than the required gas limit
225- if l2_gas_limit > initial_gas_limit {
226- set_l2_gas_limit ( & mut original_transaction, GasAmount ( 0 ) ) ?;
227- return execute_transaction ( & original_transaction, state, block_context, error_on_revert) ;
227+ if get_execution_flags ( transaction) . charge_fee && l2_gas_limit > initial_gas_limit {
228+ tx_state. abort ( ) ;
229+ set_l2_gas_limit ( transaction, initial_gas_limit) ?;
230+ return execute_transaction ( & transaction, state, block_context, error_on_revert) ;
228231 }
229232
230- set_l2_gas_limit ( & mut original_transaction, initial_gas_limit) ?;
231- let mut exec_info =
232- execute_transaction ( & original_transaction, state, block_context, error_on_revert) ?;
233+ let mut exec_info = execute_transaction ( & transaction, state, block_context, error_on_revert) ?;
233234
234235 // Execute the transaction with the determined gas limit and update the estimate.
235236 exec_info. receipt . gas . l2_gas = l2_gas_limit;
236237
237238 Ok ( exec_info)
238239}
239240
241+ fn get_execution_flags ( tx : & Transaction ) -> ExecutionFlags {
242+ match tx {
243+ Transaction :: Account ( account_transaction) => account_transaction. execution_flags . clone ( ) ,
244+ Transaction :: L1Handler ( _) => Default :: default ( ) ,
245+ }
246+ }
247+
240248fn calculate_midpoint ( a : GasAmount , b : GasAmount ) -> GasAmount {
241249 let GasAmount ( a) = a;
242250 let GasAmount ( b) = b;
@@ -286,7 +294,7 @@ where
286294fn set_l2_gas_limit (
287295 transaction : & mut Transaction ,
288296 gas_limit : GasAmount ,
289- ) -> Result < ( ) , anyhow :: Error > {
297+ ) -> Result < ( ) , Error > {
290298 if let Transaction :: Account ( ref mut account_transaction) = transaction {
291299 match & mut account_transaction. tx {
292300 AccountTransaction :: Declare ( ref mut tx) => {
@@ -321,42 +329,95 @@ fn set_l2_gas_limit(
321329 }
322330 }
323331 }
324- Err ( anyhow:: anyhow !( "Failed to set L2 gas limit" ) )
332+ Err ( anyhow ! ( "Failed to set L2 gas limit" ) )
325333}
326334
327- fn extract_l2_gas_limit ( transaction : & Transaction ) -> Result < GasAmount , anyhow:: Error > {
328- if let Transaction :: Account ( account_transaction) = transaction {
329- match & account_transaction. tx {
330- AccountTransaction :: Declare ( tx) => {
331- if let DeclareTransaction :: V3 ( tx) = & tx. tx {
332- if let ValidResourceBounds :: AllResources ( all_resource_bounds) =
333- & tx. resource_bounds
334- {
335- return Ok ( all_resource_bounds. l2_gas . max_amount ) ;
336- }
337- }
338- }
339- AccountTransaction :: DeployAccount ( tx) => {
340- if let DeployAccountTransaction :: V3 ( tx) = & tx. tx {
341- if let ValidResourceBounds :: AllResources ( all_resource_bounds) =
342- & tx. resource_bounds
343- {
344- return Ok ( all_resource_bounds. l2_gas . max_amount ) ;
345- }
346- }
347- }
348- AccountTransaction :: Invoke ( tx) => {
349- if let InvokeTransaction :: V3 ( tx) = & tx. tx {
350- if let ValidResourceBounds :: AllResources ( all_resource_bounds) =
351- & tx. resource_bounds
352- {
353- return Ok ( all_resource_bounds. l2_gas . max_amount ) ;
354- }
355- }
335+ /// Retrieves the resource bounds for a given transaction.
336+ fn extract_resource_bounds ( tx : & Transaction ) -> Result < AllResourceBounds , Error > {
337+ match tx {
338+ Transaction :: Account ( account_tx) => match & account_tx. tx {
339+ AccountTransaction :: Declare ( declare_tx) => match & declare_tx. tx {
340+ DeclareTransaction :: V3 ( DeclareTransactionV3 {
341+ resource_bounds : ValidResourceBounds :: AllResources ( all_resources) ,
342+ ..
343+ } ) => Ok ( * all_resources) ,
344+ _ => Err ( anyhow ! ( "Unsupported Declare transaction version" ) ) ,
345+ } ,
346+ AccountTransaction :: DeployAccount ( deploy_tx) => match & deploy_tx. tx {
347+ DeployAccountTransaction :: V3 ( DeployAccountTransactionV3 {
348+ resource_bounds : ValidResourceBounds :: AllResources ( all_resources) ,
349+ ..
350+ } ) => Ok ( * all_resources) ,
351+ _ => Err ( anyhow ! (
352+ "Unsupported DeployAccount transaction version"
353+ ) ) ,
354+ } ,
355+ AccountTransaction :: Invoke ( invoke_tx) => match & invoke_tx. tx {
356+ InvokeTransaction :: V3 ( InvokeTransactionV3 {
357+ resource_bounds : ValidResourceBounds :: AllResources ( all_resources) ,
358+ ..
359+ } ) => Ok ( * all_resources) ,
360+ _ => Err ( anyhow ! ( "Unsupported Invoke transaction version" ) ) ,
361+ } ,
362+ } ,
363+ _ => Err ( anyhow ! ( "Unsupported transaction type" ) ) ,
364+ }
365+ }
366+
367+ /// Calculates the maximum amount of L2 gas that can be covered by the balance.
368+ fn calculate_max_l2_gas_covered < S > (
369+ tx : & Transaction ,
370+ block_context : & blockifier:: context:: BlockContext ,
371+ state : & mut S ,
372+ ) -> Result < GasAmount , Error >
373+ where
374+ S : UpdatableState ,
375+ {
376+ // Extract initial resource bounds from the transaction.
377+ let initial_resource_bounds = extract_resource_bounds ( tx) ?;
378+
379+ // Create resource bounds without L2 gas.
380+ let resource_bounds_without_l2_gas = AllResourceBounds {
381+ l2_gas : Default :: default ( ) ,
382+ ..initial_resource_bounds
383+ } ;
384+
385+ // Calculate the maximum possible fee without L2 gas.
386+ let max_fee_without_l2_gas =
387+ ValidResourceBounds :: AllResources ( resource_bounds_without_l2_gas) . max_possible_fee ( ) ;
388+
389+ match tx {
390+ Transaction :: Account ( account_transaction) => {
391+ // Retrieve the fee token address.
392+ let fee_token_address = block_context
393+ . chain_info ( )
394+ . fee_token_address ( & account_transaction. fee_type ( ) ) ;
395+
396+ // Get the balance of the fee token.
397+ let balance = state
398+ . get_fee_token_balance ( account_transaction. sender_address ( ) , fee_token_address) ?;
399+ let balance = ( balance. 1 . to_biguint ( ) << 128 ) + balance. 0 . to_biguint ( ) ;
400+
401+ if balance > max_fee_without_l2_gas. 0 . into ( ) {
402+ // Calculate the maximum amount of L2 gas that can be bought with the balance.
403+ let max_l2_gas = ( balance - max_fee_without_l2_gas. 0 )
404+ / initial_resource_bounds
405+ . l2_gas
406+ . max_price_per_unit
407+ . 0
408+ . max ( 1u64 . into ( ) ) ;
409+ Ok ( u64:: try_from ( max_l2_gas) . unwrap_or ( GasAmount :: MAX . 0 ) . into ( ) )
410+ } else {
411+ // Balance is less than committed L1 gas and L1 data gas, transaction will fail.
412+ // Let it pass through here so that execution returns a detailed error.
413+ Ok ( GasAmount :: ZERO )
356414 }
357415 }
416+ Transaction :: L1Handler ( _) => {
417+ // L1 handler transactions don't have L2 gas.
418+ Err ( anyhow ! ( "L1 handler transactions don't have L2 gas" ) )
419+ }
358420 }
359- Err ( anyhow:: anyhow!( "Failed to extract L2 gas limit" ) )
360421}
361422
362423fn is_out_of_gas ( execution_info : & TransactionExecutionInfo ) -> bool {
0 commit comments