use crate::feed_id::ID;
use crate::report::base::{ReportBase, ReportError};
use num_bigint::BigInt;
#[derive(Debug)]
pub struct ReportDataV1 {
pub feed_id: ID,
pub observations_timestamp: u32,
pub benchmark_price: BigInt,
pub bid: BigInt,
pub ask: BigInt,
pub current_block_num: u64,
pub current_block_hash: [u8; 32],
pub valid_from_block_num: u64,
pub current_block_timestamp: u64,
}
impl ReportDataV1 {
pub fn decode(data: &[u8]) -> Result<Self, ReportError> {
if data.len() < 9 * ReportBase::WORD_SIZE {
return Err(ReportError::DataTooShort("ReportDataV1"));
}
let feed_id = ID(data[..ReportBase::WORD_SIZE]
.try_into()
.map_err(|_| ReportError::InvalidLength("feed_id (bytes32)"))?);
let observations_timestamp = ReportBase::read_uint32(data, ReportBase::WORD_SIZE)?;
let benchmark_price = ReportBase::read_int192(data, 2 * ReportBase::WORD_SIZE)?;
let bid = ReportBase::read_int192(data, 3 * ReportBase::WORD_SIZE)?;
let ask = ReportBase::read_int192(data, 4 * ReportBase::WORD_SIZE)?;
let current_block_num = ReportBase::read_uint64(data, 5 * ReportBase::WORD_SIZE)?;
let current_block_hash = data[6 * ReportBase::WORD_SIZE..7 * ReportBase::WORD_SIZE]
.try_into()
.map_err(|_| ReportError::InvalidLength("current_block_hash (bytes32)"))?;
let valid_from_block_num = ReportBase::read_uint64(data, 7 * ReportBase::WORD_SIZE)?;
let current_block_timestamp = ReportBase::read_uint64(data, 8 * ReportBase::WORD_SIZE)?;
Ok(Self {
feed_id,
observations_timestamp,
benchmark_price,
bid,
ask,
current_block_num,
current_block_hash,
valid_from_block_num,
current_block_timestamp,
})
}
pub fn abi_encode(&self) -> Result<Vec<u8>, ReportError> {
let mut buffer = Vec::with_capacity(9 * ReportBase::WORD_SIZE);
buffer.extend_from_slice(&self.feed_id.0);
buffer.extend_from_slice(&ReportBase::encode_uint32(self.observations_timestamp)?);
buffer.extend_from_slice(&ReportBase::encode_int192(&self.benchmark_price)?);
buffer.extend_from_slice(&ReportBase::encode_int192(&self.bid)?);
buffer.extend_from_slice(&ReportBase::encode_int192(&self.ask)?);
buffer.extend_from_slice(&ReportBase::encode_uint64(self.current_block_num)?);
buffer.extend_from_slice(&self.current_block_hash);
buffer.extend_from_slice(&ReportBase::encode_uint64(self.valid_from_block_num)?);
buffer.extend_from_slice(&ReportBase::encode_uint64(self.current_block_timestamp)?);
Ok(buffer)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::report::tests::{generate_mock_report_data_v1, MOCK_PRICE, MOCK_TIMESTAMP};
const V1_FEED_ID_STR: &str =
"0x00016b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472";
#[test]
fn decode_report_data_v1() {
let report_data: ReportDataV1 = generate_mock_report_data_v1();
let encoded = report_data.abi_encode().unwrap();
let decoded = ReportDataV1::decode(&encoded).unwrap();
let expected_feed_id = ID::from_hex_str(V1_FEED_ID_STR).unwrap();
let expected_timestamp = MOCK_TIMESTAMP;
let expected_current_block_num = 100;
let expected_current_block_hash = [
0, 0, 7, 4, 7, 2, 4, 1, 82, 38, 2, 9, 6, 5, 6, 8, 2, 8, 5, 5, 163, 53, 239, 127, 174,
105, 107, 102, 63, 27, 132, 1,
];
let expected_valid_from_block_num = 768986;
assert_eq!(decoded.feed_id, expected_feed_id);
assert_eq!(decoded.observations_timestamp, expected_timestamp);
assert_eq!(decoded.benchmark_price, BigInt::from(MOCK_PRICE));
assert_eq!(decoded.bid, BigInt::from(MOCK_PRICE));
assert_eq!(decoded.ask, BigInt::from(MOCK_PRICE));
assert_eq!(decoded.current_block_num, expected_current_block_num);
assert_eq!(decoded.current_block_hash, expected_current_block_hash);
assert_eq!(decoded.valid_from_block_num, expected_valid_from_block_num);
assert_eq!(decoded.current_block_timestamp, expected_timestamp as u64);
}
}