diff --git a/crates/script/src/progress.rs b/crates/script/src/progress.rs index a0a5ba5609030..08a94ed02c53d 100644 --- a/crates/script/src/progress.rs +++ b/crates/script/src/progress.rs @@ -207,20 +207,30 @@ impl ScriptProgress { let mut tasks = futures::stream::iter(futs).buffer_unordered(10); let mut errors: Vec = vec![]; + let mut discarded_transactions = false; while let Some((tx_hash, result)) = tasks.next().await { match result { Err(err) => { - errors.push(format!("Failure on receiving a receipt for {tx_hash:?}:\n{err}")); - - seq_progress.inner.write().finish_tx_spinner(tx_hash); + // Check if this is a retry error for pending receipts + if err.to_string().contains("Received a pending receipt") { + // We've already retried several times with sleep, but the receipt is still pending + discarded_transactions = true; + deployment_sequence.remove_pending(tx_hash); + let msg = format!("Transaction {tx_hash:?} was discarded by RPC despite multiple retry attempts. It will be retried when using --resume."); + seq_progress.inner.write().finish_tx_spinner_with_msg(tx_hash, &msg)?; + } else { + errors.push(format!("Failure on receiving a receipt for {tx_hash:?}:\n{err}")); + seq_progress.inner.write().finish_tx_spinner(tx_hash); + } } Ok(TxStatus::Dropped) => { // We want to remove it from pending so it will be re-broadcast. deployment_sequence.remove_pending(tx_hash); - errors.push(format!("Transaction dropped from the mempool: {tx_hash:?}")); - - seq_progress.inner.write().finish_tx_spinner(tx_hash); + discarded_transactions = true; + + let msg = format!("Transaction {tx_hash:?} dropped from the mempool. It will be retried when using --resume."); + seq_progress.inner.write().finish_tx_spinner_with_msg(tx_hash, &msg)?; } Ok(TxStatus::Success(receipt)) => { trace!(tx_hash=?tx_hash, "received tx receipt"); @@ -249,11 +259,18 @@ impl ScriptProgress { // print any errors if !errors.is_empty() { let mut error_msg = errors.join("\n"); - if !deployment_sequence.pending.is_empty() { - error_msg += "\n\n Add `--resume` to your command to try and continue broadcasting - the transactions." + + // Add information about using --resume if necessary + if !deployment_sequence.pending.is_empty() || discarded_transactions { + error_msg += r#" + +Add `--resume` to your command to try and continue broadcasting the transactions. This will attempt to resend transactions that were discarded by the RPC."#; } + eyre::bail!(error_msg); + } else if discarded_transactions { + // If we have discarded transactions but no errors, still inform the user + sh_warn!("Some transactions were discarded by the RPC node. Use `--resume` to retry these transactions.")?; } Ok(()) diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 605cdf9ddb0de..93b9a69052256 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -37,7 +37,30 @@ pub async fn check_tx_status( .get_receipt() .await { - Ok(receipt) => Ok(receipt.into()), + Ok(receipt) => { + // Check if the receipt is pending (missing block information) + let is_pending = receipt.block_number.is_none() || receipt.block_hash.is_none() || receipt.transaction_index.is_none(); + + if !is_pending { + return Ok(receipt.into()); + } + + // Receipt is pending, try to sleep and retry a few times + match provider.get_transaction_by_hash(hash).await { + Ok(_) => { + // Sleep for a short time to allow the transaction to be mined + tokio::time::sleep(Duration::from_millis(500)).await; + // Transaction is still known to the node, retry + Err(RetryError::Retry(eyre!( + "Received a pending receipt for {hash}, but transaction is still known to the node, retrying" + ))) + } + Err(_) => { + // Transaction is not known to the node, mark it as dropped + Ok(TxStatus::Dropped) + } + } + } Err(e) => match provider.get_transaction_by_hash(hash).await { Ok(_) => match e { PendingTransactionError::TxWatcher(WatchTxError::Timeout) => {