chainlink_data_streams_report/
report.rs

1pub mod base;
2pub mod compress;
3pub mod v1;
4pub mod v10;
5pub mod v11;
6pub mod v12;
7pub mod v13;
8pub mod v2;
9pub mod v3;
10pub mod v4;
11pub mod v5;
12pub mod v6;
13pub mod v7;
14pub mod v8;
15pub mod v9;
16
17use base::{ReportBase, ReportError};
18
19use crate::feed_id::ID;
20
21use serde::{Deserialize, Serialize};
22
23/// Represents a report that will be returned from the Data Streams DON.
24///
25/// The `Report` struct contains the following fields:
26/// * `feed_id`: The unique identifier of the feed.
27/// * `valid_from_timestamp`: Earliest timestamp for which price is applicable.
28/// * `observations_timestamp`: Latest timestamp for which price is applicable.
29/// * `full_report`: The report data (bytes) that needs to be decoded further - to version-specific report data.
30///
31/// # Examples
32///
33/// ```rust
34/// use chainlink_data_streams_report::report::Report;
35/// use chainlink_data_streams_report::feed_id::ID;
36///
37/// let id = ID::from_hex_str("0x00016b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472").unwrap();
38/// let report = Report {
39///    feed_id: id,
40///    valid_from_timestamp: 1718885772,
41///    observations_timestamp: 1718885772,
42///    full_report: "00016b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b84720000000000000000000000000000000000000000000000000000000066741d8c00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000640000070407020401522602090605060802080505a335ef7fae696b663f1b840100000000000000000000000000000000000000000000000000000000000bbbda0000000000000000000000000000000000000000000000000000000066741d8c".to_string(),
43/// };
44/// ```
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct Report {
47    #[serde(rename = "feedID")]
48    // pub feed_id: [u8; 32],
49    pub feed_id: ID,
50
51    #[serde(rename = "validFromTimestamp")]
52    pub valid_from_timestamp: usize,
53
54    #[serde(rename = "observationsTimestamp")]
55    pub observations_timestamp: usize,
56
57    #[serde(rename = "fullReport")]
58    pub full_report: String,
59}
60
61/// ABI-decodes a full report payload into its report context (`bytes32[3]`) and report blob (`bytes`).
62/// The report blob is the actual report data that needs to be decoded further - to version-specific report data.
63///
64/// # Parameters
65///
66/// - `payload`: The full report payload.
67///
68/// Solidity Equivalent:
69/// ```solidity
70/// struct ReportCallback {
71///     bytes32[3] reportContext;
72///     bytes reportBlob;
73///     bytes32[] rawRs;
74///     bytes32[] rawSs;
75///     bytes32 rawVs;
76/// }
77/// ```
78///
79/// # Returns
80///
81/// The report context and report blob.
82///
83/// # Errors
84///
85/// Returns a `String` if the payload is too short, the offset is invalid, or the length is invalid.
86pub fn decode_full_report(payload: &[u8]) -> Result<(Vec<[u8; 32]>, Vec<u8>), ReportError> {
87    if payload.len() < 128 {
88        return Err(ReportError::DataTooShort("Payload is too short"));
89    }
90
91    // Decode the first three bytes32 elements
92    let report_context = (0..3)
93        .map(|i| payload[i * ReportBase::WORD_SIZE..(i + 1) * ReportBase::WORD_SIZE].try_into())
94        .collect::<Result<Vec<_>, _>>()
95        .map_err(|_| ReportError::ParseError("report_context"))?;
96
97    // Decode the offset for the bytes reportBlob data
98    let offset = usize::from_be_bytes(
99        payload[96..128][24..ReportBase::WORD_SIZE] // Offset value is stored as Little Endian
100            .try_into()
101            .map_err(|_| ReportError::ParseError("offset as usize"))?,
102    );
103
104    if offset < 128 || offset >= payload.len() {
105        return Err(ReportError::InvalidLength("offset"));
106    }
107
108    // Decode the length of the bytes reportBlob data
109    let length = usize::from_be_bytes(
110        payload[offset..offset + 32][24..ReportBase::WORD_SIZE] // Length value is stored as Little Endian
111            .try_into()
112            .map_err(|_| ReportError::ParseError("length as usize"))?,
113    );
114
115    if offset + ReportBase::WORD_SIZE + length > payload.len() {
116        return Err(ReportError::InvalidLength("bytes data"));
117    }
118
119    // Decode the remainder of the payload (actual bytes reportBlob data)
120    let report_blob =
121        payload[offset + ReportBase::WORD_SIZE..offset + ReportBase::WORD_SIZE + length].to_vec();
122
123    Ok((report_context, report_blob))
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::report::{
130        v1::ReportDataV1, v10::ReportDataV10, v11::ReportDataV11, v12::ReportDataV12,
131        v13::ReportDataV13, v2::ReportDataV2, v3::ReportDataV3, v4::ReportDataV4, v5::ReportDataV5,
132        v6::ReportDataV6, v7::ReportDataV7, v8::ReportDataV8, v9::ReportDataV9,
133    };
134    use num_bigint::BigInt;
135
136    const V1_FEED_ID: ID = ID([
137        0, 1, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
138        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
139    ]);
140    const V2_FEED_ID: ID = ID([
141        00, 02, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
142        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
143    ]);
144    const V3_FEED_ID: ID = ID([
145        00, 03, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
146        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
147    ]);
148    const V4_FEED_ID: ID = ID([
149        00, 04, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
150        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
151    ]);
152    const V5_FEED_ID: ID = ID([
153        00, 05, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
154        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
155    ]);
156    const V6_FEED_ID: ID = ID([
157        00, 06, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
158        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
159    ]);
160    const V7_FEED_ID: ID = ID([
161        00, 07, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
162        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
163    ]);
164    const V8_FEED_ID: ID = ID([
165        00, 08, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
166        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
167    ]);
168    const V9_FEED_ID: ID = ID([
169        00, 09, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
170        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
171    ]);
172    const V10_FEED_ID: ID = ID([
173        00, 10, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
174        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
175    ]);
176    const V11_FEED_ID: ID = ID([
177        00, 11, 251, 109, 19, 88, 151, 228, 170, 245, 101, 123, 255, 211, 176, 180, 143, 142, 42,
178        81, 49, 33, 76, 158, 194, 214, 46, 172, 93, 83, 32, 103,
179    ]);
180    const V12_FEED_ID: ID = ID([
181        00, 12, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58,
182        163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114,
183    ]);
184    const V13_FEED_ID: ID = ID([
185        00, 13, 19, 169, 185, 197, 227, 122, 9, 159, 55, 78, 146, 195, 121, 20, 175, 92, 38, 143,
186        58, 138, 151, 33, 241, 114, 81, 53, 191, 180, 203, 184,
187    ]);
188
189    pub const MOCK_TIMESTAMP: u32 = 1718885772;
190    pub const MOCK_LAST_SEEN_TIMESTAMP_NS: u64 = 1718885772000000000;
191    pub const MOCK_FEE: usize = 10;
192    pub const MOCK_PRICE: isize = 100;
193    pub const MARKET_STATUS_OPEN: u32 = 2;
194    pub const MOCK_ASK: isize = 229;
195    pub const MOCK_BEST_ASK: isize = 229;
196    pub const MOCK_BID: isize = 227;
197    pub const MOCK_BEST_BID: isize = 227;
198    pub const MOCK_ASK_VOLUME: u64 = 1500;
199    pub const MOCK_BID_VOLUME: u64 = 1200;
200    pub const MOCK_LAST_TRADED_PRICE: isize = 228;
201    pub const MOCK_MID: isize = 228;
202    pub const MOCK_MARKET_STATUS: u32 = 2;
203
204    pub fn generate_mock_report_data_v1() -> ReportDataV1 {
205        let report_data = ReportDataV1 {
206            feed_id: V1_FEED_ID,
207            observations_timestamp: MOCK_TIMESTAMP,
208            benchmark_price: BigInt::from(MOCK_PRICE),
209            bid: BigInt::from(MOCK_PRICE),
210            ask: BigInt::from(MOCK_PRICE),
211            current_block_num: 100,
212            current_block_hash: [
213                0, 0, 7, 4, 7, 2, 4, 1, 82, 38, 2, 9, 6, 5, 6, 8, 2, 8, 5, 5, 163, 53, 239, 127,
214                174, 105, 107, 102, 63, 27, 132, 1,
215            ],
216            valid_from_block_num: 768986,
217            current_block_timestamp: MOCK_TIMESTAMP as u64,
218        };
219
220        report_data
221    }
222
223    pub fn generate_mock_report_data_v2() -> ReportDataV2 {
224        let report_data = ReportDataV2 {
225            feed_id: V2_FEED_ID,
226            valid_from_timestamp: MOCK_TIMESTAMP,
227            observations_timestamp: MOCK_TIMESTAMP,
228            native_fee: BigInt::from(MOCK_FEE),
229            link_fee: BigInt::from(MOCK_FEE),
230            expires_at: MOCK_TIMESTAMP + 100,
231            benchmark_price: BigInt::from(MOCK_PRICE),
232        };
233
234        report_data
235    }
236
237    pub fn generate_mock_report_data_v3() -> ReportDataV3 {
238        let delta = BigInt::from(10) * BigInt::from(MOCK_PRICE) / BigInt::from(100); // 10% of mock_price
239
240        let report_data = ReportDataV3 {
241            feed_id: V3_FEED_ID,
242            valid_from_timestamp: MOCK_TIMESTAMP,
243            observations_timestamp: MOCK_TIMESTAMP,
244            native_fee: BigInt::from(MOCK_FEE),
245            link_fee: BigInt::from(MOCK_FEE),
246            expires_at: MOCK_TIMESTAMP + 100,
247            benchmark_price: BigInt::from(MOCK_PRICE),
248            bid: MOCK_PRICE - delta.clone(),
249            ask: MOCK_PRICE + delta,
250        };
251
252        report_data
253    }
254
255    pub fn generate_mock_report_data_v4() -> ReportDataV4 {
256        let report_data = ReportDataV4 {
257            feed_id: V4_FEED_ID,
258            valid_from_timestamp: MOCK_TIMESTAMP,
259            observations_timestamp: MOCK_TIMESTAMP,
260            native_fee: BigInt::from(MOCK_FEE),
261            link_fee: BigInt::from(MOCK_FEE),
262            expires_at: MOCK_TIMESTAMP + 100,
263            price: BigInt::from(MOCK_PRICE),
264            market_status: MARKET_STATUS_OPEN,
265        };
266
267        report_data
268    }
269
270    pub fn generate_mock_report_data_v5() -> ReportDataV5 {
271        let one_hour_in_seconds: u32 = 3600;
272
273        let report_data = ReportDataV5 {
274            feed_id: V5_FEED_ID,
275            valid_from_timestamp: MOCK_TIMESTAMP,
276            observations_timestamp: MOCK_TIMESTAMP,
277            native_fee: BigInt::from(MOCK_FEE),
278            link_fee: BigInt::from(MOCK_FEE),
279            expires_at: MOCK_TIMESTAMP + 100,
280            rate: BigInt::from(MOCK_PRICE),
281            timestamp: MOCK_TIMESTAMP,
282            duration: one_hour_in_seconds,
283        };
284
285        report_data
286    }
287
288    pub fn generate_mock_report_data_v6() -> ReportDataV6 {
289        let report_data = ReportDataV6 {
290            feed_id: V6_FEED_ID,
291            valid_from_timestamp: MOCK_TIMESTAMP,
292            observations_timestamp: MOCK_TIMESTAMP,
293            native_fee: BigInt::from(MOCK_FEE),
294            link_fee: BigInt::from(MOCK_FEE),
295            expires_at: MOCK_TIMESTAMP + 100,
296            price: BigInt::from(MOCK_PRICE),
297            price2: BigInt::from(MOCK_PRICE + 10),
298            price3: BigInt::from(MOCK_PRICE + 20),
299            price4: BigInt::from(MOCK_PRICE + 30),
300            price5: BigInt::from(MOCK_PRICE + 40),
301        };
302
303        report_data
304    }
305
306    pub fn generate_mock_report_data_v7() -> ReportDataV7 {
307        let report_data = ReportDataV7 {
308            feed_id: V7_FEED_ID,
309            valid_from_timestamp: MOCK_TIMESTAMP,
310            observations_timestamp: MOCK_TIMESTAMP,
311            native_fee: BigInt::from(MOCK_FEE),
312            link_fee: BigInt::from(MOCK_FEE),
313            expires_at: MOCK_TIMESTAMP + 100,
314            exchange_rate: BigInt::from(MOCK_PRICE),
315        };
316
317        report_data
318    }
319
320    pub fn generate_mock_report_data_v8() -> ReportDataV8 {
321        let report_data = ReportDataV8 {
322            feed_id: V8_FEED_ID,
323            valid_from_timestamp: MOCK_TIMESTAMP,
324            observations_timestamp: MOCK_TIMESTAMP,
325            native_fee: BigInt::from(MOCK_FEE),
326            link_fee: BigInt::from(MOCK_FEE),
327            expires_at: MOCK_TIMESTAMP + 100,
328            last_update_timestamp: MOCK_TIMESTAMP as u64,
329            mid_price: BigInt::from(MOCK_PRICE),
330            market_status: MARKET_STATUS_OPEN,
331        };
332
333        report_data
334    }
335
336    pub fn generate_mock_report_data_v9() -> ReportDataV9 {
337        const MOCK_NAV_PER_SHARE: isize = 1;
338        const MOCK_AUM: isize = 1000;
339        const RIPCORD_NORMAL: u32 = 0;
340
341        let report_data = ReportDataV9 {
342            feed_id: V9_FEED_ID,
343            valid_from_timestamp: MOCK_TIMESTAMP,
344            observations_timestamp: MOCK_TIMESTAMP,
345            native_fee: BigInt::from(MOCK_FEE),
346            link_fee: BigInt::from(MOCK_FEE),
347            expires_at: MOCK_TIMESTAMP + 100,
348            nav_per_share: BigInt::from(MOCK_NAV_PER_SHARE),
349            nav_date: MOCK_TIMESTAMP as u64,
350            aum: BigInt::from(MOCK_AUM),
351            ripcord: RIPCORD_NORMAL,
352        };
353
354        report_data
355    }
356
357    pub fn generate_mock_report_data_v10() -> ReportDataV10 {
358        const MOCK_MULTIPLIER: isize = 1000000000000000000; // 1.0 with 18 decimals
359
360        let report_data = ReportDataV10 {
361            feed_id: V10_FEED_ID,
362            valid_from_timestamp: MOCK_TIMESTAMP,
363            observations_timestamp: MOCK_TIMESTAMP,
364            native_fee: BigInt::from(MOCK_FEE),
365            link_fee: BigInt::from(MOCK_FEE),
366            expires_at: MOCK_TIMESTAMP + 100,
367            last_update_timestamp: MOCK_TIMESTAMP as u64,
368            price: BigInt::from(MOCK_PRICE),
369            market_status: MARKET_STATUS_OPEN,
370            current_multiplier: BigInt::from(MOCK_MULTIPLIER),
371            new_multiplier: BigInt::from(MOCK_MULTIPLIER),
372            activation_date_time: MOCK_TIMESTAMP + 200,
373            tokenized_price: BigInt::from(MOCK_PRICE * 2),
374        };
375
376        report_data
377    }
378
379    pub fn generate_mock_report_data_v11() -> ReportDataV11 {
380        let multiplier: BigInt = "1000000000000000000".parse::<BigInt>().unwrap(); // 1.0 with 18 decimals
381
382        let report_data = ReportDataV11 {
383            feed_id: V11_FEED_ID,
384            valid_from_timestamp: MOCK_TIMESTAMP,
385            observations_timestamp: MOCK_TIMESTAMP,
386            native_fee: BigInt::from(MOCK_FEE),
387            link_fee: BigInt::from(MOCK_FEE),
388            expires_at: MOCK_TIMESTAMP + 100,
389            mid: BigInt::from(MOCK_MID).checked_mul(&multiplier).unwrap(),
390            last_seen_timestamp_ns: MOCK_LAST_SEEN_TIMESTAMP_NS,
391            bid: BigInt::from(MOCK_BID).checked_mul(&multiplier).unwrap(),
392            bid_volume: BigInt::from(MOCK_BID_VOLUME).checked_mul(&multiplier).unwrap(),
393            ask: BigInt::from(MOCK_ASK).checked_mul(&multiplier).unwrap(),
394            ask_volume: BigInt::from(MOCK_ASK_VOLUME).checked_mul(&multiplier).unwrap(),
395            last_traded_price: BigInt::from(MOCK_LAST_TRADED_PRICE)
396                .checked_mul(&multiplier)
397                .unwrap(),
398            market_status: MOCK_MARKET_STATUS,
399        };
400
401        report_data
402    }
403
404    pub fn generate_mock_report_data_v12() -> ReportDataV12 {
405        const MOCK_NAV_PER_SHARE: isize = 1;
406        const MOCK_NEXT_NAV_PER_SHARE: isize = 2;
407        const RIPCORD_NORMAL: u32 = 0;
408
409        let report_data = ReportDataV12 {
410            feed_id: V12_FEED_ID,
411            valid_from_timestamp: MOCK_TIMESTAMP,
412            observations_timestamp: MOCK_TIMESTAMP,
413            native_fee: BigInt::from(MOCK_FEE),
414            link_fee: BigInt::from(MOCK_FEE),
415            expires_at: MOCK_TIMESTAMP + 100,
416            nav_per_share: BigInt::from(MOCK_NAV_PER_SHARE),
417            next_nav_per_share: BigInt::from(MOCK_NEXT_NAV_PER_SHARE),
418            nav_date: MOCK_TIMESTAMP as i64,
419            ripcord: RIPCORD_NORMAL,
420        };
421
422        report_data
423    }
424
425    pub fn generate_mock_report_data_v13() -> ReportDataV13 {
426        let multiplier: BigInt = "1000000000000000000".parse::<BigInt>().unwrap(); // 1.0 with 18 decimals
427
428        let report_data = ReportDataV13 {
429            feed_id: V13_FEED_ID,
430            valid_from_timestamp: MOCK_TIMESTAMP,
431            observations_timestamp: MOCK_TIMESTAMP,
432            native_fee: BigInt::from(MOCK_FEE),
433            link_fee: BigInt::from(MOCK_FEE),
434            expires_at: MOCK_TIMESTAMP + 100,
435            best_ask: BigInt::from(MOCK_BEST_ASK)
436                .checked_mul(&multiplier)
437                .unwrap(),
438            best_bid: BigInt::from(MOCK_BEST_BID)
439                .checked_mul(&multiplier)
440                .unwrap(),
441            ask_volume: MOCK_ASK_VOLUME,
442            bid_volume: MOCK_BID_VOLUME,
443            last_traded_price: BigInt::from(MOCK_LAST_TRADED_PRICE)
444                .checked_mul(&multiplier)
445                .unwrap(),
446        };
447
448        report_data
449    }
450
451    fn generate_mock_report(encoded_report_data: &[u8]) -> Vec<u8> {
452        let mut payload = Vec::new();
453
454        let report_context = vec![[0u8; 32]; 3];
455        for context in &report_context {
456            payload.extend_from_slice(context);
457        }
458
459        let mut offset = [0u8; 32];
460        let offset_value: usize = 96 + 32;
461        offset[24..32].copy_from_slice(&offset_value.to_be_bytes());
462        payload.extend_from_slice(&offset);
463
464        let mut length = [0u8; 32];
465        let length_value: usize = encoded_report_data.len();
466        length[24..32].copy_from_slice(&length_value.to_be_bytes());
467        payload.extend_from_slice(&length);
468
469        payload.extend_from_slice(encoded_report_data);
470
471        // Raw `r` values, `s` values, and `v` values are not used in this test
472
473        payload
474    }
475
476    fn bytes(hex_str: &str) -> Vec<u8> {
477        if hex_str.len() % 2 != 0 {
478            panic!("Invalid hex string: odd number of characters");
479        }
480
481        hex_str
482            .trim_start_matches("0x")
483            .as_bytes()
484            .chunks(2)
485            .map(|chunk| u8::from_str_radix(std::str::from_utf8(chunk).unwrap(), 16).unwrap())
486            .collect()
487    }
488
489    #[test]
490    fn test_decode_report_v1() {
491        let report_data = generate_mock_report_data_v1();
492        let encoded_report_data = report_data.abi_encode().unwrap();
493
494        let report = generate_mock_report(&encoded_report_data);
495
496        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
497
498        let expected_report_blob = vec![
499            "00016b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
500            "0000000000000000000000000000000000000000000000000000000066741d8c",
501            "0000000000000000000000000000000000000000000000000000000000000064",
502            "0000000000000000000000000000000000000000000000000000000000000064",
503            "0000000000000000000000000000000000000000000000000000000000000064",
504            "0000000000000000000000000000000000000000000000000000000000000064",
505            "0000070407020401522602090605060802080505a335ef7fae696b663f1b8401",
506            "00000000000000000000000000000000000000000000000000000000000bbbda",
507            "0000000000000000000000000000000000000000000000000000000066741d8c",
508        ];
509
510        assert_eq!(
511            report_blob,
512            bytes(&format!("0x{}", expected_report_blob.join("")))
513        );
514
515        let decoded_report = ReportDataV1::decode(&report_blob).unwrap();
516
517        assert_eq!(decoded_report.feed_id, V1_FEED_ID);
518    }
519
520    #[test]
521    fn test_decode_report_v2() {
522        let report_data = generate_mock_report_data_v2();
523        let encoded_report_data = report_data.abi_encode().unwrap();
524
525        let report = generate_mock_report(&encoded_report_data);
526
527        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
528
529        let expected_report_blob = vec![
530            "00026b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
531            "0000000000000000000000000000000000000000000000000000000066741d8c",
532            "0000000000000000000000000000000000000000000000000000000066741d8c",
533            "000000000000000000000000000000000000000000000000000000000000000a",
534            "000000000000000000000000000000000000000000000000000000000000000a",
535            "0000000000000000000000000000000000000000000000000000000066741df0",
536            "0000000000000000000000000000000000000000000000000000000000000064",
537        ];
538
539        assert_eq!(
540            report_blob,
541            bytes(&format!("0x{}", expected_report_blob.join("")))
542        );
543
544        let decoded_report = ReportDataV2::decode(&report_blob).unwrap();
545
546        assert_eq!(decoded_report.feed_id, V2_FEED_ID);
547    }
548
549    #[test]
550    fn test_decode_report_v3() {
551        let report_data = generate_mock_report_data_v3();
552        let encoded_report_data = report_data.abi_encode().unwrap();
553
554        let report = generate_mock_report(&encoded_report_data);
555
556        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
557
558        let expected_report_blob = vec![
559            "00036b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
560            "0000000000000000000000000000000000000000000000000000000066741d8c",
561            "0000000000000000000000000000000000000000000000000000000066741d8c",
562            "000000000000000000000000000000000000000000000000000000000000000a",
563            "000000000000000000000000000000000000000000000000000000000000000a",
564            "0000000000000000000000000000000000000000000000000000000066741df0",
565            "0000000000000000000000000000000000000000000000000000000000000064", // Price: 100
566            "000000000000000000000000000000000000000000000000000000000000005a", // Bid: 90
567            "000000000000000000000000000000000000000000000000000000000000006e", // Ask: 110
568        ];
569
570        assert_eq!(
571            report_blob,
572            bytes(&format!("0x{}", expected_report_blob.join("")))
573        );
574
575        let decoded_report = ReportDataV3::decode(&report_blob).unwrap();
576
577        assert_eq!(decoded_report.feed_id, V3_FEED_ID);
578    }
579
580    #[test]
581    fn test_decode_report_v4() {
582        let report_data = generate_mock_report_data_v4();
583        let encoded_report_data = report_data.abi_encode().unwrap();
584
585        let report = generate_mock_report(&encoded_report_data);
586
587        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
588
589        let expected_report_blob = vec![
590            "00046b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
591            "0000000000000000000000000000000000000000000000000000000066741d8c",
592            "0000000000000000000000000000000000000000000000000000000066741d8c",
593            "000000000000000000000000000000000000000000000000000000000000000a",
594            "000000000000000000000000000000000000000000000000000000000000000a",
595            "0000000000000000000000000000000000000000000000000000000066741df0",
596            "0000000000000000000000000000000000000000000000000000000000000064",
597            "0000000000000000000000000000000000000000000000000000000000000002", // Market status: Open
598        ];
599
600        assert_eq!(
601            report_blob,
602            bytes(&format!("0x{}", expected_report_blob.join("")))
603        );
604
605        let decoded_report = ReportDataV4::decode(&report_blob).unwrap();
606
607        assert_eq!(decoded_report.feed_id, V4_FEED_ID);
608    }
609
610    #[test]
611    fn test_decode_report_v5() {
612        let report_data = generate_mock_report_data_v5();
613        let encoded_report_data = report_data.abi_encode().unwrap();
614
615        let report = generate_mock_report(&encoded_report_data);
616
617        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
618
619        let expected_report_blob = vec![
620            "00056b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
621            "0000000000000000000000000000000000000000000000000000000066741d8c",
622            "0000000000000000000000000000000000000000000000000000000066741d8c",
623            "000000000000000000000000000000000000000000000000000000000000000a",
624            "000000000000000000000000000000000000000000000000000000000000000a",
625            "0000000000000000000000000000000000000000000000000000000066741df0",
626            "0000000000000000000000000000000000000000000000000000000000000064", // Rate: 100
627            "0000000000000000000000000000000000000000000000000000000066741d8c", // Timestamp
628            "0000000000000000000000000000000000000000000000000000000000000e10", // Duration: 3600
629        ];
630
631        assert_eq!(
632            report_blob,
633            bytes(&format!("0x{}", expected_report_blob.join("")))
634        );
635
636        let decoded_report = ReportDataV5::decode(&report_blob).unwrap();
637
638        assert_eq!(decoded_report.feed_id, V5_FEED_ID);
639    }
640
641    #[test]
642    fn test_decode_report_v6() {
643        let report_data = generate_mock_report_data_v6();
644        let encoded_report_data = report_data.abi_encode().unwrap();
645
646        let report = generate_mock_report(&encoded_report_data);
647
648        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
649
650        let expected_report_blob = vec![
651            "00066b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
652            "0000000000000000000000000000000000000000000000000000000066741d8c",
653            "0000000000000000000000000000000000000000000000000000000066741d8c",
654            "000000000000000000000000000000000000000000000000000000000000000a",
655            "000000000000000000000000000000000000000000000000000000000000000a",
656            "0000000000000000000000000000000000000000000000000000000066741df0",
657            "0000000000000000000000000000000000000000000000000000000000000064", // Price: 100
658            "000000000000000000000000000000000000000000000000000000000000006e", // Price2: 110
659            "0000000000000000000000000000000000000000000000000000000000000078", // Price3: 120
660            "0000000000000000000000000000000000000000000000000000000000000082", // Price4: 130
661            "000000000000000000000000000000000000000000000000000000000000008c", // Price5: 140
662        ];
663
664        assert_eq!(
665            report_blob,
666            bytes(&format!("0x{}", expected_report_blob.join("")))
667        );
668
669        let decoded_report = ReportDataV6::decode(&report_blob).unwrap();
670
671        assert_eq!(decoded_report.feed_id, V6_FEED_ID);
672    }
673
674    #[test]
675    fn test_decode_report_v7() {
676        let report_data = generate_mock_report_data_v7();
677        let encoded_report_data = report_data.abi_encode().unwrap();
678
679        let report = generate_mock_report(&encoded_report_data);
680
681        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
682
683        let expected_report_blob = vec![
684            "00076b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
685            "0000000000000000000000000000000000000000000000000000000066741d8c",
686            "0000000000000000000000000000000000000000000000000000000066741d8c",
687            "000000000000000000000000000000000000000000000000000000000000000a",
688            "000000000000000000000000000000000000000000000000000000000000000a",
689            "0000000000000000000000000000000000000000000000000000000066741df0",
690            "0000000000000000000000000000000000000000000000000000000000000064", // Exchange Rate: 100
691        ];
692
693        assert_eq!(
694            report_blob,
695            bytes(&format!("0x{}", expected_report_blob.join("")))
696        );
697
698        let decoded_report = ReportDataV7::decode(&report_blob).unwrap();
699
700        assert_eq!(decoded_report.feed_id, V7_FEED_ID);
701    }
702
703    #[test]
704    fn test_decode_report_v8() {
705        let report_data = generate_mock_report_data_v8();
706        let encoded_report_data = report_data.abi_encode().unwrap();
707
708        let report = generate_mock_report(&encoded_report_data);
709
710        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
711
712        let expected_report_blob = vec![
713            "00086b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
714            "0000000000000000000000000000000000000000000000000000000066741d8c",
715            "0000000000000000000000000000000000000000000000000000000066741d8c",
716            "000000000000000000000000000000000000000000000000000000000000000a",
717            "000000000000000000000000000000000000000000000000000000000000000a",
718            "0000000000000000000000000000000000000000000000000000000066741df0",
719            "0000000000000000000000000000000000000000000000000000000066741d8c",
720            "0000000000000000000000000000000000000000000000000000000000000064",
721            "0000000000000000000000000000000000000000000000000000000000000002", // Market status: Open
722        ];
723
724        assert_eq!(
725            report_blob,
726            bytes(&format!("0x{}", expected_report_blob.join("")))
727        );
728
729        let decoded_report = ReportDataV8::decode(&report_blob).unwrap();
730
731        assert_eq!(decoded_report.feed_id, V8_FEED_ID);
732    }
733
734    #[test]
735    fn test_decode_report_v9() {
736        let report_data = generate_mock_report_data_v9();
737        let encoded_report_data = report_data.abi_encode().unwrap();
738
739        let report = generate_mock_report(&encoded_report_data);
740
741        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
742
743        let expected_report_blob = vec![
744            "00096b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
745            "0000000000000000000000000000000000000000000000000000000066741d8c",
746            "0000000000000000000000000000000000000000000000000000000066741d8c",
747            "000000000000000000000000000000000000000000000000000000000000000a",
748            "000000000000000000000000000000000000000000000000000000000000000a",
749            "0000000000000000000000000000000000000000000000000000000066741df0",
750            "0000000000000000000000000000000000000000000000000000000000000001", // NAV per share
751            "0000000000000000000000000000000000000000000000000000000066741d8c",
752            "00000000000000000000000000000000000000000000000000000000000003e8", // AUM
753            "0000000000000000000000000000000000000000000000000000000000000000", // Ripcord: Normal
754        ];
755
756        assert_eq!(
757            report_blob,
758            bytes(&format!("0x{}", expected_report_blob.join("")))
759        );
760
761        let decoded_report = ReportDataV9::decode(&report_blob).unwrap();
762
763        assert_eq!(decoded_report.feed_id, V9_FEED_ID);
764    }
765
766    #[test]
767    fn test_decode_report_v10() {
768        let report_data = generate_mock_report_data_v10();
769        let encoded_report_data = report_data.abi_encode().unwrap();
770
771        let report = generate_mock_report(&encoded_report_data);
772
773        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
774
775        let expected_report_blob = vec![
776            "000a6b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
777            "0000000000000000000000000000000000000000000000000000000066741d8c",
778            "0000000000000000000000000000000000000000000000000000000066741d8c",
779            "000000000000000000000000000000000000000000000000000000000000000a",
780            "000000000000000000000000000000000000000000000000000000000000000a",
781            "0000000000000000000000000000000000000000000000000000000066741df0",
782            "0000000000000000000000000000000000000000000000000000000066741d8c",
783            "0000000000000000000000000000000000000000000000000000000000000064",
784            "0000000000000000000000000000000000000000000000000000000000000002", // Market status: Open
785            "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // Current multiplier: 1.0 with 18 decimals
786            "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // New multiplier: 1.0 with 18 decimals
787            "0000000000000000000000000000000000000000000000000000000066741e54", // Activation date time
788            "00000000000000000000000000000000000000000000000000000000000000c8", // Tokenized price: 200
789        ];
790
791        let expected = bytes(&format!("0x{}", expected_report_blob.join("")));
792        println!("Actual  : {}", hex::encode(&report_blob));
793        println!("Expected: {}", hex::encode(&expected));
794        assert_eq!(report_blob, expected);
795
796        let decoded_report = ReportDataV10::decode(&report_blob).unwrap();
797
798        assert_eq!(decoded_report.feed_id, V10_FEED_ID);
799    }
800
801    #[test]
802    fn test_decode_report_v11() {
803        let report_data = generate_mock_report_data_v11();
804        let encoded_report_data = report_data.abi_encode().unwrap();
805
806        let report = generate_mock_report(&encoded_report_data);
807
808        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
809
810        let expected_report_blob = vec![
811            "000bfb6d135897e4aaf5657bffd3b0b48f8e2a5131214c9ec2d62eac5d532067", // feed_id
812            "0000000000000000000000000000000000000000000000000000000066741d8c", // valid_from_timestamp
813            "0000000000000000000000000000000000000000000000000000000066741d8c", // observations_timestamp
814            "000000000000000000000000000000000000000000000000000000000000000a", // native_fee
815            "000000000000000000000000000000000000000000000000000000000000000a", // link_fee
816            "0000000000000000000000000000000000000000000000000000000066741df0", // expires_at
817            "00000000000000000000000000000000000000000000000c5c22b80115100000", // mid: 228 * 10^18
818            "00000000000000000000000000000000000000000000000017dab580a9887800", // last_seen_timestamp_ns
819            "00000000000000000000000000000000000000000000000c4e42014d6dac0000", // bid: 227 * 10^18
820            "0000000000000000000000000000000000000000000000410d586a20a4c00000", // bid_volume: 1200
821            "00000000000000000000000000000000000000000000000c6a036eb4bc740000", // ask: 229 * 10^18
822            "00000000000000000000000000000000000000000000005150ae84a8cdf00000", // ask_volume: 1500
823            "00000000000000000000000000000000000000000000000c5c22b80115100000", // last_traded_price: 228 * 10^18
824            "0000000000000000000000000000000000000000000000000000000000000002", // market_status: 2 (open)
825        ];
826
827        let expected = bytes(&format!("0x{}", expected_report_blob.join("")));
828        println!("Actual  : {}", hex::encode(&report_blob));
829        println!("Expected: {}", hex::encode(&expected));
830        assert_eq!(report_blob, expected);
831
832        let decoded_report = ReportDataV10::decode(&report_blob).unwrap();
833
834        assert_eq!(decoded_report.feed_id, V11_FEED_ID);
835    }
836
837    #[test]
838    fn test_decode_report_v12() {
839        let report_data = generate_mock_report_data_v12();
840        let encoded_report_data = report_data.abi_encode().unwrap();
841
842        let report = generate_mock_report(&encoded_report_data);
843
844        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
845
846        let expected_report_blob = vec![
847            "000c6b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472",
848            "0000000000000000000000000000000000000000000000000000000066741d8c",
849            "0000000000000000000000000000000000000000000000000000000066741d8c",
850            "000000000000000000000000000000000000000000000000000000000000000a",
851            "000000000000000000000000000000000000000000000000000000000000000a",
852            "0000000000000000000000000000000000000000000000000000000066741df0",
853            "0000000000000000000000000000000000000000000000000000000000000001", // NAV per share
854            "0000000000000000000000000000000000000000000000000000000000000002", // Next NAV per share
855            "0000000000000000000000000000000000000000000000000000000066741d8c", // NAV date
856            "0000000000000000000000000000000000000000000000000000000000000000", // Ripcord: Normal
857        ];
858
859        assert_eq!(
860            report_blob,
861            bytes(&format!("0x{}", expected_report_blob.join("")))
862        );
863
864        let decoded_report = ReportDataV12::decode(&report_blob).unwrap();
865
866        assert_eq!(decoded_report.feed_id, V12_FEED_ID);
867    }
868
869    #[test]
870    fn test_decode_report_v13() {
871        let report_data = generate_mock_report_data_v13();
872        let encoded_report_data = report_data.abi_encode().unwrap();
873
874        let report = generate_mock_report(&encoded_report_data);
875
876        let (_report_context, report_blob) = decode_full_report(&report).unwrap();
877
878        let expected_report_blob = vec![
879            "000d13a9b9c5e37a099f374e92c37914af5c268f3a8a9721f1725135bfb4cbb8", // feed_id
880            "0000000000000000000000000000000000000000000000000000000066741d8c", // valid_from_timestamp
881            "0000000000000000000000000000000000000000000000000000000066741d8c", // observations_timestamp
882            "000000000000000000000000000000000000000000000000000000000000000a", // native_fee
883            "000000000000000000000000000000000000000000000000000000000000000a", // link_fee
884            "0000000000000000000000000000000000000000000000000000000066741df0", // expires_at
885            "00000000000000000000000000000000000000000000000c6a036eb4bc740000", // best_ask: 229 * 10^18
886            "00000000000000000000000000000000000000000000000c4e42014d6dac0000", // best_bid: 227 * 10^18
887            "00000000000000000000000000000000000000000000000000000000000005dc", // ask_volume: 1500
888            "00000000000000000000000000000000000000000000000000000000000004b0", // bid_volume: 1200
889            "00000000000000000000000000000000000000000000000c5c22b80115100000", // last_traded_price: 228 * 10^18
890        ];
891
892        let expected = bytes(&format!("0x{}", expected_report_blob.join("")));
893        println!("Actual  : {}", hex::encode(&report_blob));
894        println!("Expected: {}", hex::encode(&expected));
895        assert_eq!(report_blob, expected);
896
897        let decoded_report = ReportDataV13::decode(&report_blob).unwrap();
898
899        assert_eq!(decoded_report.feed_id, V13_FEED_ID);
900    }
901}