alloy_provider/fillers/
chain_id.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use std::sync::{Arc, OnceLock};

use alloy_network::{Network, TransactionBuilder};
use alloy_primitives::ChainId;
use alloy_transport::TransportResult;

use crate::{
    fillers::{FillerControlFlow, TxFiller},
    provider::SendableTx,
};

/// A [`TxFiller`] that populates the chain ID of a transaction.
///
/// If a chain ID is provided, it will be used for filling. If a chain ID
/// is not provided, the filler will attempt to fetch the chain ID from the
/// provider the first time a transaction is prepared, and will cache it for
/// future transactions.
///
/// Transactions that already have a chain_id set by the user will not be
/// modified.
///
/// # Example
///
/// ```
/// # use alloy_network::{NetworkWallet, EthereumWallet, Ethereum};
/// # use alloy_rpc_types_eth::TransactionRequest;
/// # use alloy_provider::{ProviderBuilder, RootProvider, Provider};
/// # async fn test<W: NetworkWallet<Ethereum> + Clone>(url: url::Url, wallet: W) -> Result<(), Box<dyn std::error::Error>> {
/// let provider = ProviderBuilder::new()
///     .with_chain_id(1)
///     .wallet(wallet)
///     .on_http(url);
///
/// provider.send_transaction(TransactionRequest::default()).await;
/// # Ok(())
/// # }
/// ```
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ChainIdFiller(Arc<OnceLock<ChainId>>);

impl ChainIdFiller {
    /// Create a new [`ChainIdFiller`] with an optional chain ID.
    ///
    /// If a chain ID is provided, it will be used for filling. If a chain ID
    /// is not provided, the filler will attempt to fetch the chain ID from the
    /// provider the first time a transaction is prepared.
    pub fn new(chain_id: Option<ChainId>) -> Self {
        let lock = OnceLock::new();
        if let Some(chain_id) = chain_id {
            lock.set(chain_id).expect("brand new");
        }
        Self(Arc::new(lock))
    }
}

impl<N: Network> TxFiller<N> for ChainIdFiller {
    type Fillable = ChainId;

    fn status(&self, tx: &N::TransactionRequest) -> FillerControlFlow {
        if tx.chain_id().is_some() {
            FillerControlFlow::Finished
        } else {
            FillerControlFlow::Ready
        }
    }

    fn fill_sync(&self, tx: &mut SendableTx<N>) {
        if let Some(chain_id) = self.0.get() {
            if let Some(builder) = tx.as_mut_builder() {
                if builder.chain_id().is_none() {
                    builder.set_chain_id(*chain_id)
                }
            };
        }
    }

    async fn prepare<P, T>(
        &self,
        provider: &P,
        _tx: &N::TransactionRequest,
    ) -> TransportResult<Self::Fillable>
    where
        P: crate::Provider<T, N>,
        T: alloy_transport::Transport + Clone,
    {
        match self.0.get().copied() {
            Some(chain_id) => Ok(chain_id),
            None => {
                let chain_id = provider.get_chain_id().await?;
                Ok(*self.0.get_or_init(|| chain_id))
            }
        }
    }

    async fn fill(
        &self,
        _fillable: Self::Fillable,
        mut tx: SendableTx<N>,
    ) -> TransportResult<SendableTx<N>> {
        self.fill_sync(&mut tx);
        Ok(tx)
    }
}