@@ -13,9 +13,10 @@ use std::ops::{Div, Mul};
1313use std:: str:: FromStr ;
1414use std:: time:: Duration ;
1515use std:: { cmp:: min, collections:: HashMap } ;
16+ use tokio:: time:: sleep;
1617
1718use anyhow:: { Context , Result } ;
18- use services:: input:: { fetch_eth_to_usd_rate , HeaderRangeRequestData , RpcDataFetcher } ;
19+ use services:: input:: { fetch_usd_rate , HeaderRangeRequestData , RpcDataFetcher } ;
1920use services:: Timeout ;
2021use sp1_sdk:: network:: FulfillmentStrategy ;
2122use sp1_sdk:: EnvProver ;
@@ -778,6 +779,61 @@ where
778779 }
779780 }
780781
782+ async fn submit_proof (
783+ & self ,
784+ chain_id : u64 ,
785+ tx : N :: TransactionRequest ,
786+ ) -> Result < N :: ReceiptResponse > {
787+ let contract = self
788+ . contracts
789+ . get ( & chain_id)
790+ . expect ( "No contract for chain id" ) ;
791+
792+ let receipt = contract
793+ . provider ( )
794+ . send_transaction ( tx)
795+ . await ?
796+ . with_required_confirmations ( NUM_CONFIRMATIONS )
797+ . with_timeout ( Some ( Duration :: from_secs ( RELAY_TIMEOUT_SECONDS ) ) )
798+ . get_receipt ( )
799+ . await ?;
800+
801+ if !receipt. status ( ) {
802+ return Err ( anyhow:: anyhow!( "Transaction reverted!" ) ) ;
803+ }
804+ Ok ( receipt)
805+ }
806+
807+ async fn estimate_effective_usd_gas_fee (
808+ & self ,
809+ chain_id : u64 ,
810+ tx : & N :: TransactionRequest ,
811+ ) -> Result < f64 > {
812+ let contract = self
813+ . contracts
814+ . get ( & chain_id)
815+ . expect ( "No contract for chain id" ) ;
816+
817+ let wei: f64 = Unit :: ETHER . wei_const ( ) . to :: < u64 > ( ) as f64 ;
818+ let gas_estimate: f64 = contract. provider ( ) . estimate_gas ( tx. clone ( ) ) . await ? as f64 ;
819+
820+ let max_fee_per_gas = contract
821+ . provider ( )
822+ . estimate_eip1559_fees ( )
823+ . await ?
824+ . max_fee_per_gas as f64 ;
825+ let effective_gas_estimate = gas_estimate. mul ( max_fee_per_gas) . div ( wei) ;
826+ let usd_estimate = convert_to_usd_gas_fee ( effective_gas_estimate) . await ?;
827+ info ! (
828+ message = "Gas estimate" ,
829+ gas_estimate = gas_estimate,
830+ effective_gas_estimate = effective_gas_estimate,
831+ max_fee_per_gas = max_fee_per_gas,
832+ usd_estimate = round_to_decimals( usd_estimate, 2 )
833+ ) ;
834+ Ok ( round_to_decimals ( usd_estimate, 6 ) )
835+ }
836+
781837 /// Relay a transaction to the given chain id.
782838 ///
783839 /// NOTE: Assumes the provider has a wallet.
@@ -796,39 +852,52 @@ where
796852 NUM_RELAY_RETRIES ,
797853 )
798854 . await
799- } else {
800- let contract = self
801- . contracts
802- . get ( & chain_id)
803- . expect ( "No contract for chain id" ) ;
804-
805- let receipt = contract
806- . provider ( )
807- . send_transaction ( tx)
808- . await ?
809- . with_required_confirmations ( NUM_CONFIRMATIONS )
810- . with_timeout ( Some ( Duration :: from_secs ( RELAY_TIMEOUT_SECONDS ) ) )
811- . get_receipt ( )
812- . await ?;
813-
814- if !receipt. status ( ) {
815- return Err ( anyhow:: anyhow!( "Transaction reverted!" ) ) ;
816- }
817-
818- let wei = Unit :: ETHER . wei_const ( ) . to :: < u128 > ( ) as f64 ;
819- let effective_gas_price: f64 = receipt. effective_gas_price ( ) as f64 ;
820- let effective_gas_used = effective_gas_price. mul ( receipt. gas_used ( ) as f64 ) . div ( wei) ;
821-
822- let eth_to_usd_rate = fetch_eth_to_usd_rate ( ) . await ;
823- let usd_fee = effective_gas_used. mul ( eth_to_usd_rate. from_asset . to_asset ) ;
855+ } else if matches ! ( chain_id, 1 ) {
856+ let ( max_estimate_retries, retry_sleep_interval, max_usd_fee_threshold) =
857+ get_retry_envs ( ) ?;
858+
859+ let mut attempt: u8 = 0 ;
860+
861+ let tx_hash: B256 = loop {
862+ let effective_gas_estimate = self
863+ . estimate_effective_usd_gas_fee ( chain_id, & tx)
864+ . await
865+ . expect ( "Fail to estimate USD gas fees" ) ;
866+
867+ let should_send_now = effective_gas_estimate <= max_usd_fee_threshold
868+ || attempt == max_estimate_retries;
869+
870+ if should_send_now {
871+ let receipt = self . submit_proof ( chain_id, tx) . await ?;
872+ let wei = Unit :: ETHER . wei_const ( ) . to :: < u128 > ( ) as f64 ;
873+ let effective_gas_price: f64 = receipt. effective_gas_price ( ) as f64 ;
874+ let effective_gas_used =
875+ effective_gas_price. mul ( receipt. gas_used ( ) as f64 ) . div ( wei) ;
876+
877+ let eth_to_usd_rate = fetch_usd_rate ( ) . await ?;
878+ let usd_fee = effective_gas_used. mul ( eth_to_usd_rate. from_asset . to_asset ) ;
879+
880+ info ! (
881+ message = "Transaction gas fee used" ,
882+ gas_fee = effective_gas_used,
883+ usd_fee = usd_fee,
884+ tx_hash = %receipt. transaction_hash( )
885+ ) ;
886+
887+ break receipt. transaction_hash ( ) ;
888+ }
824889
825- info ! (
826- message = "Transaction gas fee used" ,
827- gas_fee = effective_gas_used,
828- usd_fee = usd_fee,
829- tx_hash = %receipt. transaction_hash( )
830- ) ;
890+ info ! (
891+ message = "USD Gas fee too high!!" ,
892+ usd_estimate = round_to_decimals( effective_gas_estimate, 2 )
893+ ) ;
894+ sleep ( Duration :: from_secs ( retry_sleep_interval) ) . await ;
895+ attempt += 1 ;
896+ } ;
831897
898+ return Ok ( tx_hash) ;
899+ } else {
900+ let receipt = self . submit_proof ( chain_id, tx) . await ?;
832901 Ok ( receipt. transaction_hash ( ) )
833902 }
834903 }
@@ -930,6 +999,36 @@ fn get_block_update_interval() -> u32 {
930999 block_update_interval
9311000}
9321001
1002+ fn get_retry_envs ( ) -> Result < ( u8 , u64 , f64 ) > {
1003+ let max_estimate_retries: u8 = env:: var ( "MAX_ESTIMATE_RETRIES" )
1004+ . unwrap_or ( "5" . to_string ( ) )
1005+ . parse ( ) ?;
1006+
1007+ let retry_sleep_interval: u64 = env:: var ( "RETRY_SLEEP_INTERVAL" )
1008+ . unwrap_or ( "60" . to_string ( ) )
1009+ . parse ( ) ?;
1010+
1011+ let max_usd_fee_threshold: f64 = env:: var ( "MAX_USD_FEE_THRESHOLD" )
1012+ . unwrap_or ( "2.00" . to_string ( ) )
1013+ . parse ( ) ?;
1014+
1015+ return Ok ( (
1016+ max_estimate_retries,
1017+ retry_sleep_interval,
1018+ max_usd_fee_threshold,
1019+ ) ) ;
1020+ }
1021+
1022+ fn round_to_decimals ( value : f64 , decimals : u32 ) -> f64 {
1023+ let factor = 10f64 . powi ( decimals as i32 ) ;
1024+ ( value * factor) . round ( ) / factor
1025+ }
1026+
1027+ async fn convert_to_usd_gas_fee ( gas_fee : f64 ) -> Result < f64 > {
1028+ let eth_to_usd_rate = fetch_usd_rate ( ) . await ?;
1029+ Ok ( gas_fee * eth_to_usd_rate. from_asset . to_asset )
1030+ }
1031+
9331032#[ tokio:: main]
9341033async fn main ( ) -> Result < ( ) > {
9351034 dotenv:: dotenv ( ) . ok ( ) ;
0 commit comments