@@ -11,6 +11,8 @@ use alloy::rpc::types::TransactionRequest;
1111use eyre:: { Result , WrapErr } ;
1212use tracing:: { info, warn} ;
1313
14+ use crate :: config:: TxConfig ;
15+
1416#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
1517enum SendErrorKind {
1618 NonceDrift ,
@@ -51,20 +53,28 @@ pub struct NonceSender {
5153 provider : DynProvider ,
5254 signer_addr : Address ,
5355 nonce : u64 ,
56+ tx_config : TxConfig ,
5457}
5558
5659impl NonceSender {
5760 /// Create a new NonceSender, fetching the current nonce from chain.
58- pub async fn new ( provider : DynProvider , signer_addr : Address ) -> Result < Self > {
61+ pub async fn new ( provider : DynProvider , signer_addr : Address , tx_config : TxConfig ) -> Result < Self > {
5962 let nonce = provider
6063 . get_transaction_count ( signer_addr)
6164 . await
6265 . wrap_err ( "failed to get initial nonce" ) ?;
63- info ! ( nonce, "NonceSender initialized" ) ;
66+ info ! (
67+ nonce,
68+ receipt_poll_interval_ms = tx_config. receipt_poll_interval_ms,
69+ gas_price_multiplier_bps = tx_config. gas_price_multiplier_bps,
70+ max_gas_price_wei = ?tx_config. max_gas_price_wei,
71+ "NonceSender initialized"
72+ ) ;
6473 Ok ( Self {
6574 provider,
6675 signer_addr,
6776 nonce,
77+ tx_config,
6878 } )
6979 }
7080
@@ -89,25 +99,48 @@ impl NonceSender {
8999 self . nonce
90100 }
91101
92- /// Enforce BSC minimum gas price on a transaction request.
93- ///
94- /// BSC uses legacy (non-EIP-1559) transactions. When no gas fields are set,
95- /// alloy auto-fills at the provider level — which can result in values below
96- /// the RPC's minimum. We force legacy `gas_price` to at least 0.05 gwei and
97- /// clear EIP-1559 fields to prevent alloy from choosing type-2 transactions.
98- fn apply_gas_floor ( tx : TransactionRequest ) -> TransactionRequest {
102+ /// Choose a competitive legacy gas price while preserving a hard floor.
103+ async fn resolve_gas_price ( & self , explicit_gas_price : Option < u128 > ) -> Result < u128 > {
104+ let configured_multiplier_bps = self . tx_config . gas_price_multiplier_bps . max ( 10_000 ) ;
105+ let live_gas_price = self
106+ . provider
107+ . get_gas_price ( )
108+ . await
109+ . wrap_err ( "failed to fetch live gas price" ) ?;
110+
111+ let bumped_live_gas_price = live_gas_price
112+ . saturating_mul ( configured_multiplier_bps as u128 )
113+ . saturating_add ( 9_999 )
114+ / 10_000 ;
115+
116+ let mut chosen_gas_price = explicit_gas_price
117+ . unwrap_or ( bumped_live_gas_price)
118+ . max ( BSC_MIN_GAS_PRICE ) ;
119+
120+ if let Some ( max_gas_price_wei) = self . tx_config . max_gas_price_wei {
121+ if chosen_gas_price > max_gas_price_wei {
122+ warn ! (
123+ live_gas_price,
124+ bumped_live_gas_price,
125+ chosen_gas_price,
126+ max_gas_price_wei,
127+ "capping legacy gas price at configured maximum"
128+ ) ;
129+ chosen_gas_price = max_gas_price_wei. max ( BSC_MIN_GAS_PRICE ) ;
130+ }
131+ }
132+
133+ Ok ( chosen_gas_price)
134+ }
135+
136+ /// Enforce BSC legacy gas pricing on a transaction request.
137+ async fn prepare_transaction ( & self , tx : TransactionRequest ) -> Result < TransactionRequest > {
99138 let mut tx = tx;
100- // Force legacy gas price — BSC doesn't use EIP-1559
101- let gp = tx. gas_price . unwrap_or ( BSC_MIN_GAS_PRICE ) ;
102- tx. gas_price = Some ( if gp < BSC_MIN_GAS_PRICE {
103- BSC_MIN_GAS_PRICE
104- } else {
105- gp
106- } ) ;
107- // Clear EIP-1559 fields so alloy sends a type-0 (legacy) tx
139+ let gas_price = self . resolve_gas_price ( tx. gas_price ) . await ?;
140+ tx. gas_price = Some ( gas_price) ;
108141 tx. max_fee_per_gas = None ;
109142 tx. max_priority_fee_per_gas = None ;
110- tx
143+ Ok ( tx )
111144 }
112145
113146 /// Send a transaction, stamping it with the next nonce.
@@ -120,7 +153,7 @@ impl NonceSender {
120153 /// The returned [`PendingTx`] can be `.await`ed for the receipt
121154 /// **after** releasing the Mutex lock.
122155 pub async fn send ( & mut self , tx : TransactionRequest ) -> Result < PendingTx > {
123- let tx = Self :: apply_gas_floor ( tx) ;
156+ let tx = self . prepare_transaction ( tx) . await ? ;
124157 let attempt = tx. clone ( ) . nonce ( self . nonce ) ;
125158 match self . provider . send_transaction ( attempt) . await {
126159 Ok ( pending) => {
0 commit comments