use async_trait::async_trait; use ethers_core::types::transaction::eip2718::TypedTransaction; use ethers_core::types::*; use ethers_providers::{FromErr, Middleware, PendingTransaction}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use thiserror::Error; #[derive(Debug)] /// Middleware used for calculating nonces locally, useful for signing multiple /// consecutive transactions without waiting for them to hit the mempool pub struct NonceManagerMiddleware { inner: M, initialized: AtomicBool, nonce: AtomicU64, address: Address, } impl NonceManagerMiddleware where M: Middleware, { /// Instantiates the nonce manager with a 0 nonce. The `address` should be the /// address which you'll be sending transactions from pub fn new(inner: M, address: Address) -> Self { Self { initialized: false.into(), nonce: 0.into(), inner, address, } } /// Returns the next nonce to be used pub fn next(&self) -> U256 { let nonce = self.nonce.fetch_add(1, Ordering::SeqCst); nonce.into() } async fn get_transaction_count_with_manager( &self, block: Option, ) -> Result> { // initialize the nonce the first time the manager is called if !self.initialized.load(Ordering::SeqCst) { let nonce = self .inner .get_transaction_count(self.address, block) .await .map_err(FromErr::from)?; self.nonce.store(nonce.as_u64(), Ordering::SeqCst); self.initialized.store(true, Ordering::SeqCst); } Ok(self.next()) } } #[derive(Error, Debug)] /// Thrown when an error happens at the Nonce Manager pub enum NonceManagerError { /// Thrown when the internal middleware errors #[error("{0}")] MiddlewareError(M::Error), } impl FromErr for NonceManagerError { fn from(src: M::Error) -> Self { NonceManagerError::MiddlewareError(src) } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Middleware for NonceManagerMiddleware where M: Middleware, { type Error = NonceManagerError; type Provider = M::Provider; type Inner = M; fn inner(&self) -> &M { &self.inner } /// Signs and broadcasts the transaction. The optional parameter `block` can be passed so that /// gas cost and nonce calculations take it into account. For simple transactions this can be /// left to `None`. async fn send_transaction + Send + Sync>( &self, tx: T, block: Option, ) -> Result, Self::Error> { let mut tx = tx.into(); if tx.nonce().is_none() { tx.set_nonce(self.get_transaction_count_with_manager(block).await?); } match self.inner.send_transaction(tx.clone(), block).await { Ok(tx_hash) => Ok(tx_hash), Err(err) => { let nonce = self.get_transaction_count(self.address, block).await?; if nonce != self.nonce.load(Ordering::SeqCst).into() { // try re-submitting the transaction with the correct nonce if there // was a nonce mismatch self.nonce.store(nonce.as_u64(), Ordering::SeqCst); tx.set_nonce(nonce); self.inner .send_transaction(tx, block) .await .map_err(FromErr::from) } else { // propagate the error otherwise Err(FromErr::from(err)) } } } } }