kona_protocol/info/
isthmus.rs1use alloc::vec::Vec;
4use alloy_primitives::{Address, B256, Bytes, U256};
5
6use crate::DecodeError;
7
8#[derive(Debug, Clone, Hash, Eq, PartialEq, Default, Copy)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct L1BlockInfoIsthmus {
30 pub number: u64,
32 pub time: u64,
34 pub base_fee: u64,
36 pub block_hash: B256,
38 pub sequence_number: u64,
40 pub batcher_address: Address,
42 pub blob_base_fee: u128,
44 pub blob_base_fee_scalar: u32,
46 pub base_fee_scalar: u32,
48 pub operator_fee_scalar: u32,
50 pub operator_fee_constant: u64,
52}
53
54impl L1BlockInfoIsthmus {
55 pub const L1_SCALAR: u8 = 2;
57
58 pub const L1_INFO_TX_LEN: usize = 4 + 32 * 5 + 4 + 8;
60
61 pub const L1_INFO_TX_SELECTOR: [u8; 4] = [0x09, 0x89, 0x99, 0xbe];
63
64 pub fn encode_calldata(&self) -> Bytes {
66 let mut buf = Vec::with_capacity(Self::L1_INFO_TX_LEN);
67 buf.extend_from_slice(Self::L1_INFO_TX_SELECTOR.as_ref());
68 buf.extend_from_slice(self.base_fee_scalar.to_be_bytes().as_ref());
69 buf.extend_from_slice(self.blob_base_fee_scalar.to_be_bytes().as_ref());
70 buf.extend_from_slice(self.sequence_number.to_be_bytes().as_ref());
71 buf.extend_from_slice(self.time.to_be_bytes().as_ref());
72 buf.extend_from_slice(self.number.to_be_bytes().as_ref());
73 buf.extend_from_slice(U256::from(self.base_fee).to_be_bytes::<32>().as_ref());
74 buf.extend_from_slice(U256::from(self.blob_base_fee).to_be_bytes::<32>().as_ref());
75 buf.extend_from_slice(self.block_hash.as_ref());
76 buf.extend_from_slice(self.batcher_address.into_word().as_ref());
77 buf.extend_from_slice(self.operator_fee_scalar.to_be_bytes().as_ref());
78 buf.extend_from_slice(self.operator_fee_constant.to_be_bytes().as_ref());
79 buf.into()
80 }
81
82 pub fn decode_calldata(r: &[u8]) -> Result<Self, DecodeError> {
84 if r.len() != Self::L1_INFO_TX_LEN {
85 return Err(DecodeError::InvalidIsthmusLength(Self::L1_INFO_TX_LEN, r.len()));
86 }
87
88 let mut base_fee_scalar = [0u8; 4];
93 base_fee_scalar.copy_from_slice(&r[4..8]);
94 let base_fee_scalar = u32::from_be_bytes(base_fee_scalar);
95
96 let mut blob_base_fee_scalar = [0u8; 4];
98 blob_base_fee_scalar.copy_from_slice(&r[8..12]);
99 let blob_base_fee_scalar = u32::from_be_bytes(blob_base_fee_scalar);
100
101 let mut sequence_number = [0u8; 8];
103 sequence_number.copy_from_slice(&r[12..20]);
104 let sequence_number = u64::from_be_bytes(sequence_number);
105
106 let mut time = [0u8; 8];
108 time.copy_from_slice(&r[20..28]);
109 let time = u64::from_be_bytes(time);
110
111 let mut number = [0u8; 8];
113 number.copy_from_slice(&r[28..36]);
114 let number = u64::from_be_bytes(number);
115
116 let mut base_fee = [0u8; 8];
118 base_fee.copy_from_slice(&r[60..68]);
119 let base_fee = u64::from_be_bytes(base_fee);
120
121 let mut blob_base_fee = [0u8; 16];
123 blob_base_fee.copy_from_slice(&r[84..100]);
124 let blob_base_fee = u128::from_be_bytes(blob_base_fee);
125
126 let block_hash = B256::from_slice(r[100..132].as_ref());
127 let batcher_address = Address::from_slice(r[144..164].as_ref());
128
129 let mut operator_fee_scalar = [0u8; 4];
131 operator_fee_scalar.copy_from_slice(&r[164..168]);
132 let operator_fee_scalar = u32::from_be_bytes(operator_fee_scalar);
133
134 let mut operator_fee_constant = [0u8; 8];
136 operator_fee_constant.copy_from_slice(&r[168..176]);
137 let operator_fee_constant = u64::from_be_bytes(operator_fee_constant);
138
139 Ok(Self {
140 number,
141 time,
142 base_fee,
143 block_hash,
144 sequence_number,
145 batcher_address,
146 blob_base_fee,
147 blob_base_fee_scalar,
148 base_fee_scalar,
149 operator_fee_scalar,
150 operator_fee_constant,
151 })
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use alloc::vec;
159
160 #[test]
161 fn test_decode_calldata_isthmus_invalid_length() {
162 let r = vec![0u8; 1];
163 assert_eq!(
164 L1BlockInfoIsthmus::decode_calldata(&r),
165 Err(DecodeError::InvalidIsthmusLength(L1BlockInfoIsthmus::L1_INFO_TX_LEN, r.len()))
166 );
167 }
168
169 #[test]
170 fn test_l1_block_info_isthmus_roundtrip_calldata_encoding() {
171 let info = L1BlockInfoIsthmus {
172 number: 1,
173 time: 2,
174 base_fee: 3,
175 block_hash: B256::from([4; 32]),
176 sequence_number: 5,
177 batcher_address: Address::from_slice(&[6; 20]),
178 blob_base_fee: 7,
179 blob_base_fee_scalar: 8,
180 base_fee_scalar: 9,
181 operator_fee_scalar: 10,
182 operator_fee_constant: 11,
183 };
184
185 let calldata = info.encode_calldata();
186 let decoded_info = L1BlockInfoIsthmus::decode_calldata(&calldata).unwrap();
187
188 assert_eq!(info, decoded_info);
189 }
190}