eth_prices/network/time.rs
1use alloy::{primitives::BlockNumber, providers::Provider};
2
3use crate::{
4 EthPricesError,
5 network::{NetworkId, NetworkInstant},
6 provider::RpcProvider,
7};
8
9/// A single point-in-time on a specific network.
10///
11/// Each variant carries the information needed to query a rate at that moment:
12///
13/// | Variant | Use case | Payload |
14/// |---|---|---|
15/// | `EVM(chain_id, block, provider)` | On-chain asset quotes | chain ID, block height, RPC provider |
16/// | `Fiat(timestamp)` | ECB / off-chain fiat rates | Unix timestamp (seconds) |
17///
18/// # Converting to a [`NetworkInstant`]
19///
20/// Use [`instant()`](NetworkTime::instant) to wrap a single [`NetworkTime`] into a
21/// [`NetworkInstant`]
22///
23/// ```rust,ignore
24/// use eth_prices::network::NetworkTime;
25///
26/// let time = NetworkTime::EVM(1, 20_000_000, provider);
27/// let networks = time.instant();
28/// ```
29///
30/// For multi-network scenarios (e.g. different EVM chains + a fiat timestamp) build a
31/// [`NetworkInstant`] directly with its builder methods instead.
32#[derive(Debug, Clone)]
33pub enum NetworkTime {
34 /// An EVM chain at a specific block height.
35 ///
36 /// Fields: `(chain_id, block_number, provider)`
37 EVM(NetworkId, BlockNumber, RpcProvider),
38 /// An off-chain fiat timestamp (Unix seconds).
39 Fiat(u64),
40}
41
42impl NetworkTime {
43 /// Extract the EVM fields, if this is the `EVM` variant.
44 pub fn as_evm(&self) -> Option<(&NetworkId, &BlockNumber, &RpcProvider)> {
45 match self {
46 NetworkTime::EVM(chain_id, block_number, provider) => {
47 Some((chain_id, block_number, provider))
48 }
49 _ => None,
50 }
51 }
52
53 /// Extract the fiat timestamp, if this is the `Fiat` variant.
54 pub fn as_fiat(&self) -> Option<&u64> {
55 match self {
56 NetworkTime::Fiat(date_time) => Some(date_time),
57 _ => None,
58 }
59 }
60
61 /// Convert this single [`NetworkTime`] into a [`NetworkInstant`].
62 ///
63 /// This is the simplest way to create a [`NetworkInstant`] when only one network
64 /// is needed. For multi-network scenarios build the [`NetworkInstant`] directly
65 /// with its default + builder methods.
66 pub fn instant(self) -> NetworkInstant {
67 match self {
68 NetworkTime::EVM(network_id, block_number, provider) => {
69 NetworkInstant::default().with_evm_block(network_id, block_number, provider)
70 }
71 NetworkTime::Fiat(date_time) => {
72 NetworkInstant::default().with_fiat_timestamp(date_time)
73 }
74 }
75 }
76
77 pub async fn from_provider_latest(
78 provider: RpcProvider,
79 network_id: NetworkId,
80 ) -> Result<Self, EthPricesError> {
81 provider
82 .get_block_number()
83 .await
84 .map(|block_number| NetworkTime::EVM(network_id, block_number, provider))
85 .map_err(EthPricesError::from)
86 }
87
88 pub fn from_provider(
89 provider: RpcProvider,
90 network_id: NetworkId,
91 block_number: BlockNumber,
92 ) -> Self {
93 NetworkTime::EVM(network_id, block_number, provider)
94 }
95
96 /// Set a fiat timestamp to the current time.
97 #[cfg(feature = "time")]
98 pub fn with_fiat_now() -> Self {
99 use std::time::{SystemTime, UNIX_EPOCH};
100
101 let time = SystemTime::now()
102 .duration_since(UNIX_EPOCH)
103 .unwrap()
104 .as_secs();
105 NetworkTime::Fiat(time)
106 }
107}