Skip to main content

evm_oracle_state/
feed.rs

1use std::{convert::TryFrom, marker::PhantomData, ops::Deref};
2
3use alloy_primitives::Address;
4
5use crate::{AssetId, Denomination, FeedConfig, FeedId, OracleError, StalenessPolicy};
6
7/// Marker for finalized typed feed registrations.
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct FeedReady;
10
11/// Marker for builder-stage typed feed registrations.
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct FeedBuilderState;
14
15/// Builder-stage typed feed registration.
16pub type FeedBuilder = Feed<FeedBuilderState>;
17
18/// Typed feed registration primitive.
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct Feed<State = FeedReady> {
21    access: FeedAccess,
22    _state: PhantomData<State>,
23}
24
25/// Read-only accessors for finalized typed feed registrations.
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct FeedAccess {
28    inner: FeedInner,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32struct FeedInner {
33    kind: FeedKind,
34    id: Option<FeedId>,
35    label: Option<String>,
36    base: Option<AssetId>,
37    quote: Option<Denomination>,
38    staleness: StalenessPolicy,
39}
40
41#[derive(Clone, Debug, PartialEq, Eq)]
42enum FeedKind {
43    Proxy { proxy: Address },
44}
45
46impl Feed<FeedBuilderState> {
47    /// Register a Chainlink-compatible proxy feed.
48    pub fn proxy(proxy: Address) -> Self {
49        Self {
50            access: FeedAccess {
51                inner: FeedInner {
52                    kind: FeedKind::Proxy { proxy },
53                    id: None,
54                    label: None,
55                    base: None,
56                    quote: None,
57                    staleness: StalenessPolicy::default(),
58                },
59            },
60            _state: PhantomData,
61        }
62    }
63
64    /// Set a stable feed id.
65    pub fn id(mut self, id: impl Into<String>) -> Self {
66        self.access.inner.id = Some(FeedId::new(id));
67        self
68    }
69
70    /// Set a stable feed id.
71    pub fn feed_id(mut self, id: FeedId) -> Self {
72        self.access.inner.id = Some(id);
73        self
74    }
75
76    /// Set a human-readable label.
77    pub fn label(mut self, label: impl Into<String>) -> Self {
78        self.access.inner.label = Some(label.into());
79        self
80    }
81
82    /// Set the base asset.
83    pub fn base(mut self, base: AssetId) -> Self {
84        self.access.inner.base = Some(base);
85        self
86    }
87
88    /// Set the quote denomination.
89    pub fn quote(mut self, quote: Denomination) -> Self {
90        self.access.inner.quote = Some(quote);
91        self
92    }
93
94    /// Set a max-age staleness policy and finalize this feed.
95    pub fn max_age_secs(mut self, max_age_secs: u64) -> Feed {
96        self.access.inner.staleness = StalenessPolicy::max_age(max_age_secs);
97        self.build()
98    }
99
100    /// Set the full staleness policy and finalize this feed.
101    pub fn staleness(mut self, staleness: StalenessPolicy) -> Feed {
102        self.access.inner.staleness = staleness;
103        self.build()
104    }
105
106    /// Finalize this feed with its current settings.
107    pub fn build(self) -> Feed {
108        Feed {
109            access: self.access,
110            _state: PhantomData,
111        }
112    }
113}
114
115impl Deref for Feed {
116    type Target = FeedAccess;
117
118    fn deref(&self) -> &Self::Target {
119        &self.access
120    }
121}
122
123impl FeedAccess {
124    /// Return the proxy address for proxy feeds.
125    pub fn proxy(&self) -> Option<Address> {
126        match self.inner.kind {
127            FeedKind::Proxy { proxy } => Some(proxy),
128        }
129    }
130
131    /// Return the configured stable feed id.
132    pub fn id(&self) -> Option<FeedId> {
133        self.inner.id.clone()
134    }
135
136    /// Return the configured base asset.
137    pub fn base(&self) -> Option<&AssetId> {
138        self.inner.base.as_ref()
139    }
140
141    /// Return the configured quote denomination.
142    pub fn quote(&self) -> Option<&Denomination> {
143        self.inner.quote.as_ref()
144    }
145
146    /// Return the configured label.
147    pub fn label(&self) -> Option<&str> {
148        self.inner.label.as_deref()
149    }
150}
151
152impl TryFrom<Feed> for FeedConfig {
153    type Error = OracleError;
154
155    fn try_from(feed: Feed) -> Result<Self, Self::Error> {
156        feed_config_from_inner(feed.access.inner)
157    }
158}
159
160impl TryFrom<FeedBuilder> for FeedConfig {
161    type Error = OracleError;
162
163    fn try_from(feed: FeedBuilder) -> Result<Self, Self::Error> {
164        feed_config_from_inner(feed.access.inner)
165    }
166}
167
168fn feed_config_from_inner(feed: FeedInner) -> Result<FeedConfig, OracleError> {
169    let FeedKind::Proxy { proxy } = feed.kind;
170    Ok(FeedConfig {
171        proxy,
172        id: feed.id,
173        label: feed.label,
174        base: feed.base.map(String::from),
175        quote: feed.quote.map(String::from),
176        staleness: feed.staleness,
177    })
178}