eth_prices/network/instant.rs
1use std::collections::HashMap;
2
3use alloy::{primitives::BlockNumber, providers::Provider};
4
5use crate::{
6 EthPricesError,
7 network::{Network, NetworkId, NetworkTime},
8 provider::RpcProvider,
9};
10
11/// A snapshot of network states across multiple chains / data sources.
12///
13/// Internally a `HashMap<Network, NetworkTime>`, where:
14///
15/// - [`NetworkTime`] stores the actual point-in-time (block height or unix timestamp)
16/// together with the provider needed to query it.
17///
18/// ```rust,ignore
19/// use eth_prices::network::NetworkInstant;
20///
21/// let networks = NetworkInstant::default()
22/// .with_evm_provider_latest(eth_provider).await?
23/// .with_evm_provider_latest(sep_provider).await?
24/// .with_now();
25/// ```
26///
27/// Note that the above example makes a `eth_chainId` and `eth_blockNumber` query per provider supplied.
28///
29/// # Quoting at a given network time
30///
31/// Start with [`Default::default()`] and chain builder methods. Each builder consumes
32/// and returns `self`, enabling a fluent style:
33///
34/// ```rust,ignore
35/// use eth_prices::network::NetworkInstant;
36///
37/// let networks = NetworkInstant::default()
38/// .with_evm_block(1.into(), 123_456_789, eth_provider)
39/// .with_evm_block(11155111.into(), 123_456_789, sep_provider)
40/// .with_fiat_timestamp(time);
41/// ```
42///
43/// For single-network convenience, start with [`NetworkTime::instant()`] instead.
44///
45#[derive(Default, Debug, Clone)]
46pub struct NetworkInstant(pub HashMap<Network, NetworkTime>);
47
48impl NetworkInstant {
49 /// Look up the [`NetworkTime`] for a given network.
50 pub fn get(&self, network_id: &Network) -> Option<&NetworkTime> {
51 self.0.get(network_id)
52 }
53
54 /// Convenience accessor: extract the EVM fields for a specific chain.
55 ///
56 /// Returns `(chain_id, block_number, provider)` if the network exists and is
57 /// an `EVM` variant.
58 pub fn get_evm_block(
59 &self,
60 network_id: Network,
61 ) -> Option<(&NetworkId, &BlockNumber, &RpcProvider)> {
62 self.0
63 .get(&network_id)
64 .and_then(|network_time| network_time.as_evm())
65 }
66
67 /// Convenience accessor: extract the fiat timestamp (network id `0`).
68 pub fn get_fiat_timestamp(&self) -> Option<&u64> {
69 self.0
70 .get(&Network::Fiat)
71 .and_then(|network_time| network_time.as_fiat())
72 }
73
74 // ── Builder methods ──────────────────────────────────────────────────────────
75
76 /// Set a fiat timestamp at network id `0`.
77 ///
78 /// Used by [`ecb`](crate::quoter::ecb) quoters to pick a date.
79 pub fn with_fiat_timestamp(mut self, timestamp: u64) -> Self {
80 self.0.insert(Network::Fiat, NetworkTime::Fiat(timestamp));
81 self
82 }
83
84 /// Set a fiat timestamp to the current time.
85 #[cfg(feature = "time")]
86 pub fn with_now(mut self) -> Result<Self, EthPricesError> {
87 self.0.insert(Network::Fiat, NetworkTime::with_fiat_now());
88 Ok(self)
89 }
90
91 /// Set an EVM network to a specific block number.
92 ///
93 /// Use this when you already know the block height (e.g. from a historical query
94 /// or configuration).
95 pub fn with_evm_block(
96 mut self,
97 network_id: NetworkId,
98 block_number: BlockNumber,
99 provider: RpcProvider,
100 ) -> Self {
101 self.0.insert(
102 network_id.clone().into(),
103 NetworkTime::EVM(network_id, block_number, provider),
104 );
105 self
106 }
107
108 /// Set an EVM network to the latest block, fetched from the RPC.
109 ///
110 /// This is an async builder — it must be `.await`ed before further chaining:
111 ///
112 /// ```rust,ignore
113 /// let networks = NetworkInstant::default()
114 /// .with_evm_latest(1, eth_provider).await?
115 /// .with_evm_latest(42161, arb_provider).await?;
116 /// ```
117 pub async fn with_evm_latest(
118 mut self,
119 network_id: NetworkId,
120 provider: RpcProvider,
121 ) -> Result<Self, EthPricesError> {
122 self.0.insert(
123 network_id.clone().into(),
124 NetworkTime::from_provider_latest(provider, network_id).await?,
125 );
126 Ok(self)
127 }
128
129 pub async fn with_evm_provider(
130 mut self,
131 provider: RpcProvider,
132 ) -> Result<Self, EthPricesError> {
133 let network_id = NetworkId::from_provider(&provider).await?;
134 let block_number = provider.get_block_number().await?;
135 let time = NetworkTime::from_provider(provider, network_id.clone(), block_number);
136
137 self.0.insert(network_id.into(), time);
138 Ok(self)
139 }
140}