Skip to content

Commit b6c094c

Browse files
authored
fix: error handling with retries when waiting for receipt (#9650)
* fix: error handling with retries when waiting for receipt * Add RetryError::Continue variant, rework receipts tx check
1 parent be34f5b commit b6c094c

File tree

2 files changed

+30
-29
lines changed

2 files changed

+30
-29
lines changed

crates/common/src/retry.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use std::{future::Future, time::Duration};
66
/// Error type for Retry.
77
#[derive(Debug, thiserror::Error)]
88
pub enum RetryError<E = Report> {
9+
/// Continues operation without decrementing retries.
10+
Continue(E),
911
/// Keeps retrying operation.
1012
Retry(E),
1113
/// Stops retrying operation immediately.
@@ -74,6 +76,12 @@ impl Retry {
7476
{
7577
loop {
7678
match callback().await {
79+
Err(RetryError::Continue(e)) => {
80+
self.handle_continue(e);
81+
if !self.delay.is_zero() {
82+
tokio::time::sleep(self.delay).await;
83+
}
84+
}
7785
Err(RetryError::Retry(e)) if self.retries > 0 => {
7886
self.handle_err(e);
7987
if !self.delay.is_zero() {
@@ -89,6 +97,10 @@ impl Retry {
8997
fn handle_err(&mut self, err: Error) {
9098
debug_assert!(self.retries > 0);
9199
self.retries -= 1;
100+
self.handle_continue(err);
101+
}
102+
103+
fn handle_continue(&mut self, err: Error) {
92104
let _ = sh_warn!(
93105
"{msg}{delay} ({retries} tries remaining)",
94106
msg = crate::errors::display_chain(&err),

crates/script/src/receipts.rs

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use alloy_chains::Chain;
22
use alloy_network::AnyTransactionReceipt;
33
use alloy_primitives::{utils::format_units, TxHash, U256};
44
use alloy_provider::{PendingTransactionBuilder, PendingTransactionError, Provider, WatchTxError};
5-
use eyre::Result;
6-
use foundry_common::{provider::RetryProvider, shell};
5+
use eyre::{eyre, Result};
6+
use foundry_common::{provider::RetryProvider, retry, retry::RetryError, shell};
77
use std::time::Duration;
88

99
/// Convenience enum for internal signalling of transaction status
@@ -30,39 +30,28 @@ pub async fn check_tx_status(
3030
hash: TxHash,
3131
timeout: u64,
3232
) -> (TxHash, Result<TxStatus, eyre::Report>) {
33-
// We use the inner future so that we can use ? operator in the future, but
34-
// still neatly return the tuple
35-
let result = async move {
36-
// First check if there's a receipt
37-
let receipt_opt = provider.get_transaction_receipt(hash).await?;
38-
if let Some(receipt) = receipt_opt {
39-
return Ok(receipt.into());
40-
}
41-
42-
loop {
33+
let result = retry::Retry::new_no_delay(3)
34+
.run_async_until_break(|| async {
4335
match PendingTransactionBuilder::new(provider.clone(), hash)
4436
.with_timeout(Some(Duration::from_secs(timeout)))
4537
.get_receipt()
4638
.await
4739
{
48-
Ok(receipt) => return Ok(receipt.into()),
49-
// do nothing on timeout, we will check whether tx is dropped below
50-
Err(PendingTransactionError::TxWatcher(WatchTxError::Timeout)) => {}
51-
// treat other errors as fatal
52-
Err(e) => return Err(e.into()),
53-
}
54-
55-
if provider.get_transaction_by_hash(hash).await?.is_some() {
56-
trace!("tx is still known to the node, waiting for receipt");
57-
} else {
58-
trace!("eth_getTransactionByHash returned null, assuming dropped");
59-
break
40+
Ok(receipt) => Ok(receipt.into()),
41+
Err(e) => match provider.get_transaction_by_hash(hash).await {
42+
Ok(_) => match e {
43+
PendingTransactionError::TxWatcher(WatchTxError::Timeout) => {
44+
Err(RetryError::Continue(eyre!(
45+
"tx is still known to the node, waiting for receipt"
46+
)))
47+
}
48+
_ => Err(RetryError::Retry(e.into())),
49+
},
50+
Err(_) => Ok(TxStatus::Dropped),
51+
},
6052
}
61-
}
62-
63-
Ok(TxStatus::Dropped)
64-
}
65-
.await;
53+
})
54+
.await;
6655

6756
(hash, result)
6857
}

0 commit comments

Comments
 (0)