diff --git a/rust/chains/abacus-ethereum/src/inbox.rs b/rust/chains/abacus-ethereum/src/inbox.rs index 426fde031..adc932b6b 100644 --- a/rust/chains/abacus-ethereum/src/inbox.rs +++ b/rust/chains/abacus-ethereum/src/inbox.rs @@ -1,20 +1,21 @@ #![allow(clippy::enum_variant_names)] #![allow(missing_docs)] -use abacus_core::{accumulator::merkle::Proof, MessageStatus, *}; -use abacus_core::{ - AbacusCommon, AbacusCommonIndexer, AbacusMessage, ChainCommunicationError, Checkpoint, - CheckpointMeta, CheckpointWithMeta, ContractLocator, Inbox, TxOutcome, -}; +use std::{error::Error as StdError, sync::Arc}; + use async_trait::async_trait; use ethers::contract::abigen; use ethers::core::types::{H256, U256}; use eyre::Result; use tracing::instrument; -use std::{error::Error as StdError, sync::Arc}; +use abacus_core::{accumulator::merkle::Proof, MessageStatus, *}; +use abacus_core::{ + AbacusCommon, AbacusCommonIndexer, AbacusMessage, ChainCommunicationError, Checkpoint, + CheckpointMeta, CheckpointWithMeta, ContractLocator, Inbox, TxOutcome, +}; -use crate::report_tx; +use crate::tx::report_tx; abigen!( EthereumInboxInternal, @@ -256,7 +257,7 @@ where ); let gas = tx.estimate_gas().await?.saturating_add(U256::from(100000)); let gassed = tx.gas(gas); - Ok(report_tx!(gassed).into()) + Ok(report_tx(gassed).await?.into()) } #[tracing::instrument(err)] diff --git a/rust/chains/abacus-ethereum/src/lib.rs b/rust/chains/abacus-ethereum/src/lib.rs index 0bb50ea7d..1e49a7e47 100644 --- a/rust/chains/abacus-ethereum/src/lib.rs +++ b/rust/chains/abacus-ethereum/src/lib.rs @@ -4,12 +4,20 @@ #![warn(missing_docs)] #![warn(unused_extern_crates)] -use abacus_core::*; +use std::sync::Arc; + use ethers::providers::Middleware; use ethers::types::{Address, BlockId, BlockNumber, NameOrAddress, H160}; use eyre::Result; use num::Num; -use std::sync::Arc; + +use abacus_core::*; +pub use retrying::{RetryingProvider, RetryingProviderError}; + +#[cfg(not(doctest))] +pub use crate::{inbox::*, outbox::*, validator_manager::*}; + +mod tx; #[macro_use] mod macros; @@ -28,7 +36,6 @@ mod validator_manager; /// Retrying Provider mod retrying; -pub use retrying::{RetryingProvider, RetryingProviderError}; /// Ethereum connection configuration #[derive(Debug, serde::Deserialize, Clone)] @@ -54,9 +61,6 @@ impl Default for Connection { } } -#[cfg(not(doctest))] -pub use crate::{inbox::*, outbox::*, validator_manager::*}; - #[allow(dead_code)] /// A live connection to an ethereum-compatible chain. pub struct Chain { diff --git a/rust/chains/abacus-ethereum/src/macros.rs b/rust/chains/abacus-ethereum/src/macros.rs index 0fd7d0ba0..0d6d61950 100644 --- a/rust/chains/abacus-ethereum/src/macros.rs +++ b/rust/chains/abacus-ethereum/src/macros.rs @@ -1,72 +1,3 @@ -/// Dispatches a transaction, logs the tx id, and returns the result -#[macro_export] -macro_rules! report_tx { - ($tx:expr) => {{ - - // "0x..." - let data = format!("0x{}", hex::encode(&$tx.tx.data().map(|b| b.to_vec()).unwrap_or_default())); - - let to = $tx.tx.to().cloned().unwrap_or_else(|| ethers::types::NameOrAddress::Address(Default::default())); - - tracing::info!( - to = ?to, - data = %data, - "Dispatching transaction" - ); - // We can set the gas higher here! - let dispatch_fut = $tx.send(); - let dispatched = dispatch_fut.await?; - - let tx_hash: ethers::core::types::H256 = *dispatched; - - tracing::info!( - to = ?to, - data = %data, - tx_hash = ?tx_hash, - "Dispatched tx" - ); - - - let wrapped_tx_submission = tokio::time::timeout(std::time::Duration::from_secs(300), dispatched); - - match wrapped_tx_submission.await { - Ok(tx_submission) => { - match tx_submission { - Ok(Some(receipt)) => { - tracing::info!( - tx_hash = ?tx_hash, - "confirmed transaction" - ); - - receipt - } - // ethers-rs will return None if it can no longer poll for the tx in the mempool - Ok(None) => { - return Err(abacus_core::ChainCommunicationError::DroppedError(tx_hash)) - } - // Pass through this error - Err(x) => { - tracing::error!( - tx_hash = ?tx_hash, - error = ?x, - "encountered error when waiting for receipt", - ); - return Err(x.into()) - } - } - } - Err(x) => { - tracing::error!( - tx_hash = ?tx_hash, - error = ?x, - "waiting for receipt timed out", - ); - return Err(abacus_core::ChainCommunicationError::TransactionTimeout()) - } - } - }}; -} - macro_rules! boxed_trait { (@finish $provider:expr, $abi:ident, $signer:ident, $($tail:tt)*) => {{ if let Some(signer) = $signer { diff --git a/rust/chains/abacus-ethereum/src/outbox.rs b/rust/chains/abacus-ethereum/src/outbox.rs index 90c071f87..3f28542ff 100644 --- a/rust/chains/abacus-ethereum/src/outbox.rs +++ b/rust/chains/abacus-ethereum/src/outbox.rs @@ -1,16 +1,18 @@ #![allow(clippy::enum_variant_names)] #![allow(missing_docs)] -use abacus_core::*; -use abacus_core::{ChainCommunicationError, Message, RawCommittedMessage, TxOutcome}; +use std::{error::Error as StdError, sync::Arc}; + use async_trait::async_trait; use ethers::contract::abigen; use ethers::core::types::H256; use eyre::Result; -use std::{error::Error as StdError, sync::Arc}; use tracing::instrument; -use crate::report_tx; +use abacus_core::*; +use abacus_core::{ChainCommunicationError, Message, RawCommittedMessage, TxOutcome}; + +use crate::tx::report_tx; abigen!( EthereumOutboxInternal, @@ -257,7 +259,7 @@ where message.body.clone().into(), ); - Ok(report_tx!(tx).into()) + Ok(report_tx(tx).await?.into()) } #[tracing::instrument(err, skip(self))] @@ -279,6 +281,6 @@ where async fn create_checkpoint(&self) -> Result { let tx = self.contract.checkpoint(); - Ok(report_tx!(tx).into()) + Ok(report_tx(tx).await?.into()) } } diff --git a/rust/chains/abacus-ethereum/src/tx.rs b/rust/chains/abacus-ethereum/src/tx.rs new file mode 100644 index 000000000..824ac575c --- /dev/null +++ b/rust/chains/abacus-ethereum/src/tx.rs @@ -0,0 +1,81 @@ +use std::time::Duration; + +use ethers::abi::Detokenize; +use ethers::prelude::*; +use ethers_contract::builders::ContractCall; +use tracing::{error, info}; + +use abacus_core::ChainCommunicationError; + +use crate::Middleware; + +/// Dispatches a transaction, logs the tx id, and returns the result +pub(crate) async fn report_tx( + tx: ContractCall, +) -> Result +where + M: Middleware + 'static, + D: Detokenize, +{ + // "0x..." + let data = format!( + "0x{}", + hex::encode(&tx.tx.data().map(|b| b.to_vec()).unwrap_or_default()) + ); + + let to = tx + .tx + .to() + .cloned() + .unwrap_or_else(|| NameOrAddress::Address(Default::default())); + + info!( + to = ?to, + data = %data, + "Dispatching transaction" + ); + // We can set the gas higher here! + let dispatch_fut = tx.send(); + let dispatched = dispatch_fut.await?; + + let tx_hash: H256 = *dispatched; + + info!( + to = ?to, + data = %data, + tx_hash = ?tx_hash, + "Dispatched tx" + ); + + match tokio::time::timeout(Duration::from_secs(300), dispatched).await { + // all good + Ok(Ok(Some(receipt))) => { + info!( + tx_hash = ?tx_hash, + "confirmed transaction" + ); + + Ok(receipt) + } + // ethers-rs will return None if it can no longer poll for the tx in the mempool + Ok(Ok(None)) => Err(ChainCommunicationError::DroppedError(tx_hash)), + // Received error, pass it through + Ok(Err(x)) => { + error!( + tx_hash = ?tx_hash, + error = ?x, + "encountered error when waiting for receipt", + ); + Err(x.into()) + } + // Timed out + Err(x) => { + error!( + tx_hash = ?tx_hash, + error = ?x, + "waiting for receipt timed out", + ); + Err(ChainCommunicationError::TransactionTimeout()) + } + } +} diff --git a/rust/chains/abacus-ethereum/src/validator_manager.rs b/rust/chains/abacus-ethereum/src/validator_manager.rs index c76fbce68..35de2bb4f 100644 --- a/rust/chains/abacus-ethereum/src/validator_manager.rs +++ b/rust/chains/abacus-ethereum/src/validator_manager.rs @@ -1,16 +1,17 @@ #![allow(clippy::enum_variant_names)] #![allow(missing_docs)] -use abacus_core::{ChainCommunicationError, ContractLocator, TxOutcome}; -use abacus_core::{InboxValidatorManager, MultisigSignedCheckpoint}; +use std::sync::Arc; + use async_trait::async_trait; use ethers::contract::abigen; use ethers::core::types::Address; use eyre::Result; -use std::sync::Arc; +use abacus_core::{ChainCommunicationError, ContractLocator, TxOutcome}; +use abacus_core::{InboxValidatorManager, MultisigSignedCheckpoint}; -use crate::report_tx; +use crate::tx::report_tx; abigen!( EthereumInboxValidatorManagerInternal, @@ -91,6 +92,6 @@ where .collect(), ); - Ok(report_tx!(tx).into()) + Ok(report_tx(tx).await?.into()) } }