kona_genesis/updates/
operator_fee.rs

1//! The Operator Fee update type.
2
3use alloy_primitives::LogData;
4use alloy_sol_types::{SolType, sol};
5
6use crate::{OperatorFeeUpdateError, SystemConfig, SystemConfigLog};
7
8/// The Operator Fee update type.
9#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct OperatorFeeUpdate {
12    /// The operator fee scalar.
13    pub operator_fee_scalar: u32,
14    /// The operator fee constant.
15    pub operator_fee_constant: u64,
16}
17
18impl OperatorFeeUpdate {
19    /// Applies the update to the [`SystemConfig`].
20    pub const fn apply(&self, config: &mut SystemConfig) {
21        config.operator_fee_scalar = Some(self.operator_fee_scalar);
22        config.operator_fee_constant = Some(self.operator_fee_constant);
23    }
24}
25
26impl TryFrom<&SystemConfigLog> for OperatorFeeUpdate {
27    type Error = OperatorFeeUpdateError;
28
29    fn try_from(log: &SystemConfigLog) -> Result<Self, Self::Error> {
30        let LogData { data, .. } = &log.log.data;
31        if data.len() != 96 {
32            return Err(OperatorFeeUpdateError::InvalidDataLen(data.len()));
33        }
34
35        let Ok(pointer) = <sol!(uint64)>::abi_decode_validate(&data[0..32]) else {
36            return Err(OperatorFeeUpdateError::PointerDecodingError);
37        };
38        if pointer != 32 {
39            return Err(OperatorFeeUpdateError::InvalidDataPointer(pointer));
40        }
41
42        let Ok(length) = <sol!(uint64)>::abi_decode_validate(&data[32..64]) else {
43            return Err(OperatorFeeUpdateError::LengthDecodingError);
44        };
45        if length != 32 {
46            return Err(OperatorFeeUpdateError::InvalidDataLength(length));
47        }
48
49        // The operator fee scalar and constant are
50        // packed into a single u256 as follows:
51        //
52        // | Bytes    | Actual Size | Variable |
53        // |----------|-------------|----------|
54        // | 0 .. 24  | uint32      | scalar   |
55        // | 24 .. 32 | uint64      | constant |
56        // |----------|-------------|----------|
57
58        let mut be_bytes = [0u8; 4];
59        be_bytes[0..4].copy_from_slice(&data[84..88]);
60        let operator_fee_scalar = u32::from_be_bytes(be_bytes);
61
62        let mut be_bytes = [0u8; 8];
63        be_bytes[0..8].copy_from_slice(&data[88..96]);
64        let operator_fee_constant = u64::from_be_bytes(be_bytes);
65
66        Ok(Self { operator_fee_scalar, operator_fee_constant })
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use crate::{CONFIG_UPDATE_EVENT_VERSION_0, CONFIG_UPDATE_TOPIC};
74    use alloc::vec;
75    use alloy_primitives::{Address, B256, Bytes, Log, LogData, hex};
76
77    #[test]
78    fn test_operator_fee_update_try_from() {
79        let log = Log {
80            address: Address::ZERO,
81            data: LogData::new_unchecked(
82                vec![], // Topics aren't checked
83                hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000babe000000000000beef").into()
84            )
85        };
86
87        let system_log = SystemConfigLog::new(log, false);
88        let update = OperatorFeeUpdate::try_from(&system_log).unwrap();
89
90        assert_eq!(update.operator_fee_scalar, 0xbabe_u32);
91        assert_eq!(update.operator_fee_constant, 0xbeef_u64);
92    }
93
94    #[test]
95    fn test_operator_fee_update_invalid_data_len() {
96        let log =
97            Log { address: Address::ZERO, data: LogData::new_unchecked(vec![], Bytes::default()) };
98        let system_log = SystemConfigLog::new(log, false);
99        let err = OperatorFeeUpdate::try_from(&system_log).unwrap_err();
100        assert_eq!(err, OperatorFeeUpdateError::InvalidDataLen(0));
101    }
102
103    #[test]
104    fn test_operator_fee_update_pointer_decoding_error() {
105        let log = Log {
106            address: Address::ZERO,
107            data: LogData::new_unchecked(
108                vec![
109                    CONFIG_UPDATE_TOPIC,
110                    CONFIG_UPDATE_EVENT_VERSION_0,
111                    B256::ZERO,
112                ],
113                hex!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000babe0000beef").into()
114            )
115        };
116
117        let system_log = SystemConfigLog::new(log, false);
118        let err = OperatorFeeUpdate::try_from(&system_log).unwrap_err();
119        assert_eq!(err, OperatorFeeUpdateError::PointerDecodingError);
120    }
121
122    #[test]
123    fn test_operator_fee_update_invalid_pointer_length() {
124        let log = Log {
125            address: Address::ZERO,
126            data: LogData::new_unchecked(
127                vec![
128                    CONFIG_UPDATE_TOPIC,
129                    CONFIG_UPDATE_EVENT_VERSION_0,
130                    B256::ZERO,
131                ],
132                hex!("000000000000000000000000000000000000000000000000000000000000002100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000babe0000beef").into()
133            )
134        };
135
136        let system_log = SystemConfigLog::new(log, false);
137        let err = OperatorFeeUpdate::try_from(&system_log).unwrap_err();
138        assert_eq!(err, OperatorFeeUpdateError::InvalidDataPointer(33));
139    }
140
141    #[test]
142    fn test_operator_fee_update_length_decoding_error() {
143        let log = Log {
144            address: Address::ZERO,
145            data: LogData::new_unchecked(
146                vec![
147                    CONFIG_UPDATE_TOPIC,
148                    CONFIG_UPDATE_EVENT_VERSION_0,
149                    B256::ZERO,
150                ],
151                hex!("0000000000000000000000000000000000000000000000000000000000000020FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000babe0000beef").into()
152            )
153        };
154
155        let system_log = SystemConfigLog::new(log, false);
156        let err = OperatorFeeUpdate::try_from(&system_log).unwrap_err();
157        assert_eq!(err, OperatorFeeUpdateError::LengthDecodingError);
158    }
159
160    #[test]
161    fn test_operator_fee_update_invalid_data_length() {
162        let log = Log {
163            address: Address::ZERO,
164            data: LogData::new_unchecked(
165                vec![
166                    CONFIG_UPDATE_TOPIC,
167                    CONFIG_UPDATE_EVENT_VERSION_0,
168                    B256::ZERO,
169                ],
170                hex!("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000babe0000beef").into()
171            )
172        };
173
174        let system_log = SystemConfigLog::new(log, false);
175        let err = OperatorFeeUpdate::try_from(&system_log).unwrap_err();
176        assert_eq!(err, OperatorFeeUpdateError::InvalidDataLength(33));
177    }
178}