evm_oracle_state/
signal.rs1use evm_fork_cache::reactive::HookSignal;
2
3use crate::{
4 AggregatorChange, OracleHookEvent, OraclePriceConfirmed, OraclePriceCorrected,
5 OraclePriceStale, OraclePriceUpdate, OracleSignalDecodeError, OracleUpdate,
6};
7
8pub const ORACLE_SIGNAL_NAMESPACE: &str = "evm-oracle-state";
10
11pub const ORACLE_LEGACY_ANSWER_UPDATED_KIND: &str = "oracle.answer_updated";
13
14#[non_exhaustive]
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17pub enum OracleSignalKind {
18 PriceUpdate,
20 PriceConfirmed,
22 PriceCorrected,
24 PriceStale,
26 AggregatorChanged,
28}
29
30impl OracleSignalKind {
31 pub const fn as_str(self) -> &'static str {
33 match self {
34 Self::PriceUpdate => "oracle.price_update",
35 Self::PriceConfirmed => "oracle.price_confirmed",
36 Self::PriceCorrected => "oracle.price_corrected",
37 Self::PriceStale => "oracle.price_stale",
38 Self::AggregatorChanged => "oracle.aggregator_changed",
39 }
40 }
41
42 pub fn parse(kind: &str) -> Option<Self> {
44 match kind {
45 "oracle.price_update" => Some(Self::PriceUpdate),
46 "oracle.price_confirmed" => Some(Self::PriceConfirmed),
47 "oracle.price_corrected" => Some(Self::PriceCorrected),
48 "oracle.price_stale" => Some(Self::PriceStale),
49 "oracle.aggregator_changed" => Some(Self::AggregatorChanged),
50 _ => None,
51 }
52 }
53}
54
55#[non_exhaustive]
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum OracleSignal<'a> {
59 PriceUpdate(&'a OraclePriceUpdate),
61 PriceConfirmed(&'a OraclePriceConfirmed),
63 PriceCorrected(&'a OraclePriceCorrected),
65 PriceStale(&'a OraclePriceStale),
67 AggregatorChanged(&'a AggregatorChange),
69}
70
71impl<'a> OracleSignal<'a> {
72 pub fn from_hook(signal: &'a HookSignal) -> Result<Option<Self>, OracleSignalDecodeError> {
74 if signal.namespace.as_ref() != ORACLE_SIGNAL_NAMESPACE {
75 return Ok(None);
76 }
77
78 let Some(kind) = OracleSignalKind::parse(signal.kind.as_ref()) else {
79 return Ok(None);
80 };
81 let payload =
82 signal
83 .payload
84 .as_ref()
85 .ok_or_else(|| OracleSignalDecodeError::MissingPayload {
86 kind: signal.kind.to_string(),
87 })?;
88
89 if let Some(event) = payload.downcast_ref::<OracleHookEvent>() {
90 return event
91 .as_signal()
92 .ok_or_else(|| OracleSignalDecodeError::WrongPayloadType {
93 kind: signal.kind.to_string(),
94 })
95 .map(Some);
96 }
97
98 match kind {
99 OracleSignalKind::PriceUpdate => payload
100 .downcast_ref::<OraclePriceUpdate>()
101 .map(Self::PriceUpdate),
102 OracleSignalKind::PriceConfirmed => payload
103 .downcast_ref::<OraclePriceConfirmed>()
104 .map(Self::PriceConfirmed),
105 OracleSignalKind::PriceCorrected => payload
106 .downcast_ref::<OraclePriceCorrected>()
107 .map(Self::PriceCorrected),
108 OracleSignalKind::PriceStale => payload
109 .downcast_ref::<OraclePriceStale>()
110 .map(Self::PriceStale),
111 OracleSignalKind::AggregatorChanged => payload
112 .downcast_ref::<AggregatorChange>()
113 .map(Self::AggregatorChanged),
114 }
115 .ok_or_else(|| OracleSignalDecodeError::WrongPayloadType {
116 kind: signal.kind.to_string(),
117 })
118 .map(Some)
119 }
120
121 pub fn to_event(self) -> OracleHookEvent {
123 match self {
124 Self::PriceUpdate(update) => OracleHookEvent::PriceUpdate(update.clone()),
125 Self::PriceConfirmed(confirmed) => OracleHookEvent::PriceConfirmed(confirmed.clone()),
126 Self::PriceCorrected(corrected) => OracleHookEvent::PriceCorrected(corrected.clone()),
127 Self::PriceStale(stale) => OracleHookEvent::PriceStale(stale.clone()),
128 Self::AggregatorChanged(change) => OracleHookEvent::AggregatorChanged(change.clone()),
129 }
130 }
131}
132
133impl OracleHookEvent {
134 pub fn signal_kind(&self) -> OracleSignalKind {
136 match self {
137 Self::PriceUpdate(_) => OracleSignalKind::PriceUpdate,
138 Self::PriceConfirmed(_) => OracleSignalKind::PriceConfirmed,
139 Self::PriceCorrected(_) => OracleSignalKind::PriceCorrected,
140 Self::PriceStale(_) => OracleSignalKind::PriceStale,
141 Self::AggregatorChanged(_) => OracleSignalKind::AggregatorChanged,
142 }
143 }
144
145 pub fn kind_str(&self) -> &'static str {
147 self.signal_kind().as_str()
148 }
149
150 pub(crate) fn as_signal(&self) -> Option<OracleSignal<'_>> {
151 match self {
152 Self::PriceUpdate(update) => Some(OracleSignal::PriceUpdate(update)),
153 Self::PriceConfirmed(confirmed) => Some(OracleSignal::PriceConfirmed(confirmed)),
154 Self::PriceCorrected(corrected) => Some(OracleSignal::PriceCorrected(corrected)),
155 Self::PriceStale(stale) => Some(OracleSignal::PriceStale(stale)),
156 Self::AggregatorChanged(change) => Some(OracleSignal::AggregatorChanged(change)),
157 }
158 }
159}
160
161impl OracleUpdate {
162 pub const KIND: &'static str = ORACLE_LEGACY_ANSWER_UPDATED_KIND;
164}