alloy_provider/fillers/
nonce.rs

1use crate::{
2    fillers::{FillerControlFlow, TxFiller},
3    provider::SendableTx,
4    Provider,
5};
6use alloy_network::{Network, TransactionBuilder};
7use alloy_primitives::Address;
8use alloy_transport::TransportResult;
9use async_trait::async_trait;
10use dashmap::DashMap;
11use futures::lock::Mutex;
12use std::sync::Arc;
13
14/// A trait that determines the behavior of filling nonces.
15#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
16#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
17pub trait NonceManager: Clone + Send + Sync + std::fmt::Debug {
18    /// Get the next nonce for the given account.
19    async fn get_next_nonce<P, N>(&self, provider: &P, address: Address) -> TransportResult<u64>
20    where
21        P: Provider<N>,
22        N: Network;
23}
24
25/// This [`NonceManager`] implementation will fetch the transaction count for any new account it
26/// sees.
27///
28/// Unlike [`CachedNonceManager`], this implementation does not store the transaction count locally,
29/// which results in more frequent calls to the provider, but it is more resilient to chain
30/// reorganizations.
31#[derive(Clone, Debug, Default)]
32#[non_exhaustive]
33pub struct SimpleNonceManager;
34
35#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
36#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
37impl NonceManager for SimpleNonceManager {
38    async fn get_next_nonce<P, N>(&self, provider: &P, address: Address) -> TransportResult<u64>
39    where
40        P: Provider<N>,
41        N: Network,
42    {
43        provider.get_transaction_count(address).pending().await
44    }
45}
46
47/// Cached nonce manager
48///
49/// This [`NonceManager`] implementation will fetch the transaction count for any new account it
50/// sees, store it locally and increment the locally stored nonce as transactions are sent via
51/// [`Provider::send_transaction`].
52///
53/// There is also an alternative implementation [`SimpleNonceManager`] that does not store the
54/// transaction count locally.
55#[derive(Clone, Debug, Default)]
56pub struct CachedNonceManager {
57    nonces: Arc<DashMap<Address, Arc<Mutex<u64>>>>,
58}
59
60#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
61#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
62impl NonceManager for CachedNonceManager {
63    async fn get_next_nonce<P, N>(&self, provider: &P, address: Address) -> TransportResult<u64>
64    where
65        P: Provider<N>,
66        N: Network,
67    {
68        // Use `u64::MAX` as a sentinel value to indicate that the nonce has not been fetched yet.
69        const NONE: u64 = u64::MAX;
70
71        // Locks dashmap internally for a short duration to clone the `Arc`.
72        // We also don't want to hold the dashmap lock through the await point below.
73        let nonce = {
74            let rm = self.nonces.entry(address).or_insert_with(|| Arc::new(Mutex::new(NONE)));
75            Arc::clone(rm.value())
76        };
77
78        let mut nonce = nonce.lock().await;
79        let new_nonce = if *nonce == NONE {
80            // Initialize the nonce if we haven't seen this account before.
81            provider.get_transaction_count(address).await?
82        } else {
83            *nonce + 1
84        };
85        *nonce = new_nonce;
86        Ok(new_nonce)
87    }
88}
89
90/// A [`TxFiller`] that fills nonces on transactions. The behavior of filling nonces is determined
91/// by the [`NonceManager`].
92///
93/// # Note
94///
95/// - If the transaction request does not have a sender set, this layer will not fill nonces.
96/// - Using two providers with their own nonce layer can potentially fill invalid nonces if
97///   transactions are sent from the same address, as the next nonce to be used is cached internally
98///   in the layer.
99///
100/// # Example
101///
102/// ```
103/// # use alloy_network::{Ethereum};
104/// # use alloy_rpc_types_eth::TransactionRequest;
105/// # use alloy_provider::{ProviderBuilder, RootProvider, Provider};
106/// # use alloy_signer_local::PrivateKeySigner;
107/// # async fn test(url: url::Url) -> Result<(), Box<dyn std::error::Error>> {
108/// let pk: PrivateKeySigner = "0x...".parse()?;
109/// let provider = ProviderBuilder::<_, _, Ethereum>::default()
110///     .with_simple_nonce_management()
111///     .wallet(pk)
112///     .on_http(url);
113///
114/// provider.send_transaction(TransactionRequest::default()).await;
115/// # Ok(())
116/// # }
117/// ```
118#[derive(Clone, Debug, Default)]
119pub struct NonceFiller<M: NonceManager = SimpleNonceManager> {
120    nonce_manager: M,
121}
122
123impl<M: NonceManager> NonceFiller<M> {
124    /// Creates a new [`NonceFiller`] with the specified [`NonceManager`].
125    pub const fn new(nonce_manager: M) -> Self {
126        Self { nonce_manager }
127    }
128}
129
130impl<M: NonceManager, N: Network> TxFiller<N> for NonceFiller<M> {
131    type Fillable = u64;
132
133    fn status(&self, tx: &<N as Network>::TransactionRequest) -> FillerControlFlow {
134        if tx.nonce().is_some() {
135            return FillerControlFlow::Finished;
136        }
137        if tx.from().is_none() {
138            return FillerControlFlow::missing("NonceManager", vec!["from"]);
139        }
140        FillerControlFlow::Ready
141    }
142
143    fn fill_sync(&self, _tx: &mut SendableTx<N>) {}
144
145    async fn prepare<P>(
146        &self,
147        provider: &P,
148        tx: &N::TransactionRequest,
149    ) -> TransportResult<Self::Fillable>
150    where
151        P: Provider<N>,
152    {
153        let from = tx.from().expect("checked by 'ready()'");
154        self.nonce_manager.get_next_nonce(provider, from).await
155    }
156
157    async fn fill(
158        &self,
159        nonce: Self::Fillable,
160        mut tx: SendableTx<N>,
161    ) -> TransportResult<SendableTx<N>> {
162        if let Some(builder) = tx.as_mut_builder() {
163            builder.set_nonce(nonce);
164        }
165        Ok(tx)
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172    use crate::{ProviderBuilder, WalletProvider};
173    use alloy_consensus::Transaction;
174    use alloy_primitives::{address, U256};
175    use alloy_rpc_types_eth::TransactionRequest;
176
177    async fn check_nonces<P, N, M>(
178        filler: &NonceFiller<M>,
179        provider: &P,
180        address: Address,
181        start: u64,
182    ) where
183        P: Provider<N>,
184        N: Network,
185        M: NonceManager,
186    {
187        for i in start..start + 5 {
188            let nonce = filler.nonce_manager.get_next_nonce(&provider, address).await.unwrap();
189            assert_eq!(nonce, i);
190        }
191    }
192
193    #[tokio::test]
194    async fn smoke_test() {
195        let filler = NonceFiller::<CachedNonceManager>::default();
196        let provider = ProviderBuilder::new().on_anvil();
197        let address = Address::ZERO;
198        check_nonces(&filler, &provider, address, 0).await;
199
200        #[cfg(feature = "anvil-api")]
201        {
202            use crate::ext::AnvilApi;
203            filler.nonce_manager.nonces.clear();
204            provider.anvil_set_nonce(address, 69).await.unwrap();
205            check_nonces(&filler, &provider, address, 69).await;
206        }
207    }
208
209    #[tokio::test]
210    async fn concurrency() {
211        let filler = Arc::new(NonceFiller::<CachedNonceManager>::default());
212        let provider = Arc::new(ProviderBuilder::new().on_anvil());
213        let address = Address::ZERO;
214        let tasks = (0..5)
215            .map(|_| {
216                let filler = Arc::clone(&filler);
217                let provider = Arc::clone(&provider);
218                tokio::spawn(async move {
219                    filler.nonce_manager.get_next_nonce(&provider, address).await
220                })
221            })
222            .collect::<Vec<_>>();
223
224        let mut ns = Vec::new();
225        for task in tasks {
226            ns.push(task.await.unwrap().unwrap());
227        }
228        ns.sort_unstable();
229        assert_eq!(ns, (0..5).collect::<Vec<_>>());
230
231        assert_eq!(filler.nonce_manager.nonces.len(), 1);
232        assert_eq!(*filler.nonce_manager.nonces.get(&address).unwrap().value().lock().await, 4);
233    }
234
235    #[tokio::test]
236    async fn no_nonce_if_sender_unset() {
237        let provider = ProviderBuilder::new()
238            .disable_recommended_fillers()
239            .with_cached_nonce_management()
240            .on_anvil();
241
242        let tx = TransactionRequest {
243            value: Some(U256::from(100)),
244            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
245            gas_price: Some(20e9 as u128),
246            gas: Some(21000),
247            ..Default::default()
248        };
249
250        // errors because signer layer expects nonce to be set, which it is not
251        assert!(provider.send_transaction(tx).await.is_err());
252    }
253
254    #[tokio::test]
255    async fn increments_nonce() {
256        let provider = ProviderBuilder::new()
257            .disable_recommended_fillers()
258            .with_cached_nonce_management()
259            .on_anvil_with_wallet();
260
261        let from = provider.default_signer_address();
262        let tx = TransactionRequest {
263            from: Some(from),
264            value: Some(U256::from(100)),
265            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
266            gas_price: Some(20e9 as u128),
267            gas: Some(21000),
268            ..Default::default()
269        };
270
271        let pending = provider.send_transaction(tx.clone()).await.unwrap();
272        let tx_hash = pending.watch().await.unwrap();
273        let mined_tx = provider
274            .get_transaction_by_hash(tx_hash)
275            .await
276            .expect("failed to fetch tx")
277            .expect("tx not included");
278        assert_eq!(mined_tx.nonce(), 0);
279
280        let pending = provider.send_transaction(tx).await.unwrap();
281        let tx_hash = pending.watch().await.unwrap();
282        let mined_tx = provider
283            .get_transaction_by_hash(tx_hash)
284            .await
285            .expect("fail to fetch tx")
286            .expect("tx didn't finalize");
287        assert_eq!(mined_tx.nonce(), 1);
288    }
289
290    #[tokio::test]
291    async fn cloned_managers() {
292        let cnm1 = CachedNonceManager::default();
293        let cnm2 = cnm1.clone();
294
295        let provider = ProviderBuilder::new().on_anvil();
296        let address = Address::ZERO;
297
298        assert_eq!(cnm1.get_next_nonce(&provider, address).await.unwrap(), 0);
299        assert_eq!(cnm2.get_next_nonce(&provider, address).await.unwrap(), 1);
300        assert_eq!(cnm1.get_next_nonce(&provider, address).await.unwrap(), 2);
301        assert_eq!(cnm2.get_next_nonce(&provider, address).await.unwrap(), 3);
302    }
303}