Skip to main content

evm_oracle_state/
layout.rs

1use alloy_primitives::{Address, B256};
2
3/// Known aggregator storage layouts.
4#[non_exhaustive]
5#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub enum AggregatorLayout {
7    /// Chainlink OCR2 `AccessControlledOCR2Aggregator 1.0.0` compatible layout.
8    ChainlinkOcr2V1,
9    /// Chainlink OCR1 `AccessControlledOffchainAggregator 2.0.0` compatible layout.
10    ChainlinkOcr1V2,
11    /// Chainlink OCR1 `AccessControlledOffchainAggregator 3.0.0` compatible layout.
12    ChainlinkOcr1V3,
13    /// Chainlink OCR1 `AccessControlledOffchainAggregator 4.0.0` compatible layout.
14    ChainlinkOcr1V4,
15    /// The aggregator exposed a version string, but this crate has no direct
16    /// storage adapter for it.
17    Unsupported,
18    /// No usable layout evidence was available.
19    Unknown,
20}
21
22/// How an aggregator layout was detected.
23#[non_exhaustive]
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum AggregatorLayoutConfidence {
26    /// The layout came directly from `typeAndVersion()`.
27    TypeAndVersion,
28    /// The layout came from a previously observed matching code hash.
29    CodeHashCache,
30    /// No usable evidence was available.
31    Unknown,
32}
33
34/// Evidence collected while identifying an aggregator implementation.
35#[derive(Clone, Debug, PartialEq, Eq)]
36pub struct AggregatorLayoutEvidence {
37    /// Aggregator address that was inspected.
38    pub aggregator: Address,
39    /// Raw `typeAndVersion()` result, when available.
40    pub type_and_version: Option<String>,
41    /// Runtime code hash, when available from the provider.
42    pub code_hash: Option<B256>,
43    /// Classified layout.
44    pub layout: AggregatorLayout,
45    /// Detection confidence.
46    pub confidence: AggregatorLayoutConfidence,
47}
48
49impl AggregatorLayoutEvidence {
50    /// Build evidence from a `typeAndVersion()` read and optional code hash.
51    pub fn from_type_and_version(
52        aggregator: Address,
53        type_and_version: impl Into<String>,
54        code_hash: Option<B256>,
55    ) -> Self {
56        let type_and_version = type_and_version.into();
57        Self {
58            aggregator,
59            layout: classify_type_and_version(&type_and_version),
60            type_and_version: Some(type_and_version),
61            code_hash,
62            confidence: AggregatorLayoutConfidence::TypeAndVersion,
63        }
64    }
65
66    /// Build evidence from a code-hash cache hit.
67    pub fn from_code_hash_cache(
68        aggregator: Address,
69        code_hash: B256,
70        layout: AggregatorLayout,
71    ) -> Self {
72        Self {
73            aggregator,
74            type_and_version: None,
75            code_hash: Some(code_hash),
76            layout,
77            confidence: AggregatorLayoutConfidence::CodeHashCache,
78        }
79    }
80
81    /// Build unknown evidence for an aggregator.
82    pub fn unknown(aggregator: Address, code_hash: Option<B256>) -> Self {
83        Self {
84            aggregator,
85            type_and_version: None,
86            code_hash,
87            layout: AggregatorLayout::Unknown,
88            confidence: AggregatorLayoutConfidence::Unknown,
89        }
90    }
91
92    /// Return true when the evidence supports OCR2 direct storage sync.
93    pub fn is_chainlink_ocr2_v1(&self) -> bool {
94        self.layout == AggregatorLayout::ChainlinkOcr2V1
95    }
96
97    /// Return true when the evidence supports a Chainlink OCR1 direct-storage layout.
98    pub fn is_chainlink_ocr1(&self) -> bool {
99        matches!(
100            self.layout,
101            AggregatorLayout::ChainlinkOcr1V2
102                | AggregatorLayout::ChainlinkOcr1V3
103                | AggregatorLayout::ChainlinkOcr1V4
104        )
105    }
106}
107
108/// Classify a Chainlink-style `typeAndVersion()` string.
109pub fn classify_type_and_version(type_and_version: &str) -> AggregatorLayout {
110    match type_and_version.trim() {
111        "AccessControlledOCR2Aggregator 1.0.0" | "OCR2Aggregator 1.0.0" => {
112            AggregatorLayout::ChainlinkOcr2V1
113        }
114        "AccessControlledOffchainAggregator 2.0.0" | "OffchainAggregator 2.0.0" => {
115            AggregatorLayout::ChainlinkOcr1V2
116        }
117        "AccessControlledOffchainAggregator 3.0.0" | "OffchainAggregator 3.0.0" => {
118            AggregatorLayout::ChainlinkOcr1V3
119        }
120        "AccessControlledOffchainAggregator 4.0.0" | "OffchainAggregator 4.0.0" => {
121            AggregatorLayout::ChainlinkOcr1V4
122        }
123        "" => AggregatorLayout::Unknown,
124        _ => AggregatorLayout::Unsupported,
125    }
126}