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
use std::{future::Future, pin::Pin};
use alloy_primitives::{Address, B256, U256};
use crate::{OracleError, RoundData};
/// Boxed future used by the provider seam.
pub type ProviderFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, OracleError>> + Send + 'a>>;
/// Block identity for hash-pinned oracle reconciliation reads.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OracleBlockRef {
/// Block number.
pub number: u64,
/// Block hash.
pub hash: B256,
}
/// Minimal read seam for Chainlink-compatible proxy feeds.
///
/// The trait is intentionally small so tests and downstream systems can provide
/// their own provider, cache, or batched multicall implementation.
pub trait ChainlinkFeedProvider {
/// Read `decimals()` from the proxy.
fn decimals(&self, proxy: Address) -> ProviderFuture<'_, u8>;
/// Read `description()` from the proxy.
fn description(&self, proxy: Address) -> ProviderFuture<'_, String>;
/// Read `version()` from the proxy.
fn version(&self, proxy: Address) -> ProviderFuture<'_, U256>;
/// Read `latestRoundData()` from the proxy.
fn latest_round_data(&self, proxy: Address) -> ProviderFuture<'_, RoundData>;
/// Read `latestRoundData()` from the proxy at a specific block, when supported.
///
/// Providers that cannot pin by block may fall back to the unpinned read.
fn latest_round_data_at(
&self,
proxy: Address,
_block: Option<OracleBlockRef>,
) -> ProviderFuture<'_, RoundData> {
self.latest_round_data(proxy)
}
/// Best-effort read of the current underlying aggregator address.
fn aggregator(&self, proxy: Address) -> ProviderFuture<'_, Option<Address>>;
/// Best-effort read of the underlying aggregator address at a specific block.
///
/// Providers that cannot pin by block may fall back to the unpinned read.
fn aggregator_at(
&self,
proxy: Address,
_block: Option<OracleBlockRef>,
) -> ProviderFuture<'_, Option<Address>> {
self.aggregator(proxy)
}
/// Best-effort read of the aggregator implementation `typeAndVersion()`.
///
/// Providers that do not support implementation introspection may return
/// `Ok(None)`. Registration and reconciliation treat missing or failed
/// introspection as an unsupported layout and fall back to purge/refetch.
fn aggregator_type_and_version(
&self,
_aggregator: Address,
) -> ProviderFuture<'_, Option<String>> {
Box::pin(async { Ok(None) })
}
/// Best-effort block-pinned read of the aggregator `typeAndVersion()`.
fn aggregator_type_and_version_at(
&self,
aggregator: Address,
_block: Option<OracleBlockRef>,
) -> ProviderFuture<'_, Option<String>> {
self.aggregator_type_and_version(aggregator)
}
/// Best-effort read of the aggregator runtime code hash.
fn aggregator_code_hash(&self, _aggregator: Address) -> ProviderFuture<'_, Option<B256>> {
Box::pin(async { Ok(None) })
}
/// Best-effort block-pinned read of the aggregator runtime code hash.
fn aggregator_code_hash_at(
&self,
aggregator: Address,
_block: Option<OracleBlockRef>,
) -> ProviderFuture<'_, Option<B256>> {
self.aggregator_code_hash(aggregator)
}
}