kona_protocol/info/
interop.rs1use crate::DecodeError;
4use alloc::vec::Vec;
5use alloy_primitives::{Address, B256, Bytes, U256};
6
7#[derive(Debug, Clone, Hash, Eq, PartialEq, Default, Copy)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct L1BlockInfoInterop {
27 pub number: u64,
29 pub time: u64,
31 pub base_fee: u64,
33 pub block_hash: B256,
35 pub sequence_number: u64,
37 pub batcher_address: Address,
39 pub blob_base_fee: u128,
41 pub blob_base_fee_scalar: u32,
43 pub base_fee_scalar: u32,
45}
46
47impl L1BlockInfoInterop {
48 pub const L1_SCALAR: u8 = 1;
50
51 pub const L1_INFO_TX_LEN: usize = 4 + 32 * 5;
53
54 pub const L1_INFO_TX_SELECTOR: [u8; 4] = [0x76, 0x0e, 0xe0, 0x4d];
56
57 pub fn encode_calldata(&self) -> Bytes {
59 let mut buf = Vec::with_capacity(Self::L1_INFO_TX_LEN);
60 buf.extend_from_slice(Self::L1_INFO_TX_SELECTOR.as_ref());
61 buf.extend_from_slice(self.base_fee_scalar.to_be_bytes().as_ref());
62 buf.extend_from_slice(self.blob_base_fee_scalar.to_be_bytes().as_ref());
63 buf.extend_from_slice(self.sequence_number.to_be_bytes().as_ref());
64 buf.extend_from_slice(self.time.to_be_bytes().as_ref());
65 buf.extend_from_slice(self.number.to_be_bytes().as_ref());
66 buf.extend_from_slice(U256::from(self.base_fee).to_be_bytes::<32>().as_ref());
67 buf.extend_from_slice(U256::from(self.blob_base_fee).to_be_bytes::<32>().as_ref());
68 buf.extend_from_slice(self.block_hash.as_ref());
69 buf.extend_from_slice(self.batcher_address.into_word().as_ref());
70 buf.into()
71 }
72
73 pub fn decode_calldata(r: &[u8]) -> Result<Self, DecodeError> {
75 if r.len() != Self::L1_INFO_TX_LEN {
76 return Err(DecodeError::InvalidInteropLength(Self::L1_INFO_TX_LEN, r.len()));
77 }
78
79 let mut base_fee_scalar = [0u8; 4];
84 base_fee_scalar.copy_from_slice(&r[4..8]);
85 let base_fee_scalar = u32::from_be_bytes(base_fee_scalar);
86
87 let mut blob_base_fee_scalar = [0u8; 4];
89 blob_base_fee_scalar.copy_from_slice(&r[8..12]);
90 let blob_base_fee_scalar = u32::from_be_bytes(blob_base_fee_scalar);
91
92 let mut sequence_number = [0u8; 8];
94 sequence_number.copy_from_slice(&r[12..20]);
95 let sequence_number = u64::from_be_bytes(sequence_number);
96
97 let mut time = [0u8; 8];
99 time.copy_from_slice(&r[20..28]);
100 let time = u64::from_be_bytes(time);
101
102 let mut number = [0u8; 8];
104 number.copy_from_slice(&r[28..36]);
105 let number = u64::from_be_bytes(number);
106
107 let mut base_fee = [0u8; 8];
109 base_fee.copy_from_slice(&r[60..68]);
110 let base_fee = u64::from_be_bytes(base_fee);
111
112 let mut blob_base_fee = [0u8; 16];
114 blob_base_fee.copy_from_slice(&r[84..100]);
115 let blob_base_fee = u128::from_be_bytes(blob_base_fee);
116
117 let block_hash = B256::from_slice(r[100..132].as_ref());
118 let batcher_address = Address::from_slice(r[144..164].as_ref());
119
120 Ok(Self {
121 number,
122 time,
123 base_fee,
124 block_hash,
125 sequence_number,
126 batcher_address,
127 blob_base_fee,
128 blob_base_fee_scalar,
129 base_fee_scalar,
130 })
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use alloc::vec;
138
139 #[test]
140 fn test_decode_calldata_interop_invalid_length() {
141 let r = vec![0u8; 1];
142 assert_eq!(
143 L1BlockInfoInterop::decode_calldata(&r),
144 Err(DecodeError::InvalidInteropLength(L1BlockInfoInterop::L1_INFO_TX_LEN, r.len(),))
145 );
146 }
147
148 #[test]
149 fn test_l1_block_info_interop_roundtrip_calldata_encoding() {
150 let info = L1BlockInfoInterop {
151 number: 1,
152 time: 2,
153 base_fee: 3,
154 block_hash: B256::from([4u8; 32]),
155 sequence_number: 5,
156 batcher_address: Address::from([6u8; 20]),
157 blob_base_fee: 7,
158 blob_base_fee_scalar: 8,
159 base_fee_scalar: 9,
160 };
161
162 let calldata = info.encode_calldata();
163 let decoded_info = L1BlockInfoInterop::decode_calldata(&calldata).unwrap();
164 assert_eq!(info, decoded_info);
165 }
166}