Ver Fonte

fix(fortuna): Fix nonce increment issues on high usage chains (#2868)

Jayant Krishnamurthy há 4 meses atrás
pai
commit
af5d5f88c7

+ 1 - 1
Cargo.lock

@@ -3071,7 +3071,7 @@ dependencies = [
 
 [[package]]
 name = "fortuna"
-version = "8.2.1"
+version = "8.2.2"
 dependencies = [
  "anyhow",
  "axum 0.6.20",

+ 1 - 1
apps/fortuna/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "fortuna"
-version = "8.2.1"
+version = "8.2.2"
 edition = "2021"
 
 [lib]

+ 13 - 3
apps/fortuna/src/eth_utils/utils.rs

@@ -196,7 +196,7 @@ pub async fn submit_tx_with_backoff<T: Middleware + NonceManaged + 'static>(
 
 #[allow(clippy::large_enum_variant)]
 pub enum SubmitTxError<T: Middleware + NonceManaged + 'static> {
-    GasUsageEstimateError(ContractError<T>),
+    GasUsageEstimateError(TypedTransaction, ContractError<T>),
     GasLimitExceeded { estimate: U256, limit: U256 },
     GasPriceEstimateError(<T as Middleware>::Error),
     SubmissionError(TypedTransaction, <T as Middleware>::Error),
@@ -211,8 +211,8 @@ where
 {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
-            SubmitTxError::GasUsageEstimateError(e) => {
-                write!(f, "Error estimating gas for reveal: {e:?}")
+            SubmitTxError::GasUsageEstimateError(tx, e) => {
+                write!(f, "Error estimating gas for reveal: Tx:{tx:?}, Error:{e:?}")
             }
             SubmitTxError::GasLimitExceeded { estimate, limit } => write!(
                 f,
@@ -247,7 +247,16 @@ pub async fn submit_tx<T: Middleware + NonceManaged + 'static>(
     // A value of 100 submits the tx with the same fee as the estimate.
     fee_estimate_multiplier_pct: u64,
 ) -> Result<TransactionReceipt, backoff::Error<SubmitTxError<T>>> {
+    // Estimate the gas *before* filling the transaction. Filling the transaction increments the nonce of the
+    // provider. If we can't send the transaction (because the gas estimation fails), then the nonce will be
+    // out of sync with the one on-chain, causing subsequent transactions to fail.
+    let gas: U256 = call.estimate_gas().await.map_err(|e| {
+        backoff::Error::transient(SubmitTxError::GasUsageEstimateError(call.tx.clone(), e))
+    })?;
+
     let mut transaction = call.tx.clone();
+    // Setting the gas here avoids a redundant call to estimate_gas within the Provider's fill_transaction method.
+    transaction.set_gas(gas);
 
     // manually fill the tx with the gas price info, so we can log the details in case of error
     client
@@ -258,6 +267,7 @@ pub async fn submit_tx<T: Middleware + NonceManaged + 'static>(
             if let Some(e) = e.as_error_response() {
                 if let Some(e) = e.as_revert_data() {
                     return backoff::Error::transient(SubmitTxError::GasUsageEstimateError(
+                        transaction.clone(),
                         ContractError::Revert(e.clone()),
                     ));
                 }

+ 7 - 5
apps/fortuna/src/keeper/process_event.rs

@@ -137,15 +137,17 @@ pub async fn process_event_with_backoff(
     );
     let error_mapper = |num_retries, e| {
         if let backoff::Error::Transient {
-            err: SubmitTxError::GasUsageEstimateError(ContractError::Revert(revert)),
+            err: SubmitTxError::GasUsageEstimateError(tx, ContractError::Revert(revert)),
             ..
         } = &e
         {
             if let Ok(PythRandomErrorsErrors::NoSuchRequest(_)) =
                 PythRandomErrorsErrors::decode(revert)
             {
-                let err =
-                    SubmitTxError::GasUsageEstimateError(ContractError::Revert(revert.clone()));
+                let err = SubmitTxError::GasUsageEstimateError(
+                    tx.clone(),
+                    ContractError::Revert(revert.clone()),
+                );
                 // Slow down the retries if the request is not found.
                 // This probably means that the request is already fulfilled via another process.
                 // After 5 retries, we return the error permanently.
@@ -255,13 +257,13 @@ pub async fn process_event_with_backoff(
                     .inc();
                 // Do not display the internal error, it might include RPC details.
                 let reason = match e {
-                    SubmitTxError::GasUsageEstimateError(ContractError::Revert(revert)) => {
+                    SubmitTxError::GasUsageEstimateError(_, ContractError::Revert(revert)) => {
                         format!("Reverted: {revert}")
                     }
                     SubmitTxError::GasLimitExceeded { limit, estimate } => {
                         format!("Gas limit exceeded: limit = {limit}, estimate = {estimate}")
                     }
-                    SubmitTxError::GasUsageEstimateError(_) => {
+                    SubmitTxError::GasUsageEstimateError(_, _) => {
                         "Unable to estimate gas usage".to_string()
                     }
                     SubmitTxError::GasPriceEstimateError(_) => {