degenbot_executor/
config.rs1use alloy::primitives::U256;
16
17use crate::encoders::MAX_INDEXED_ADDRESSES;
18
19pub const MAX_BRIBE_BIPS: u16 = 10_000;
21
22pub const MAX_CHECK_MODE: u8 = 3;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
27pub enum ConfigError {
28 #[error("check_mode must be 0–3, got {0}")]
30 CheckModeOutOfRange(u8),
31 #[error("bribe_bips must be 0–10000, got {0}")]
33 BribeBipsOutOfRange(u16),
34 #[error("bribe_recipient_idx must be 0–31, got {0}")]
36 BribeRecipientOutOfRange(u8),
37}
38
39pub fn pack_config(
51 check_mode: u8,
52 expected_value: U256,
53 bribe_bips: u16,
54 bribe_recipient_idx: u8,
55) -> Result<U256, ConfigError> {
56 if check_mode > MAX_CHECK_MODE {
57 return Err(ConfigError::CheckModeOutOfRange(check_mode));
58 }
59 if bribe_bips > MAX_BRIBE_BIPS {
60 return Err(ConfigError::BribeBipsOutOfRange(bribe_bips));
61 }
62 if bribe_recipient_idx as usize >= MAX_INDEXED_ADDRESSES {
65 return Err(ConfigError::BribeRecipientOutOfRange(bribe_recipient_idx));
66 }
67
68 Ok((expected_value << U256::from(32u64))
70 | (U256::from(bribe_recipient_idx) << U256::from(24u64))
71 | (U256::from(bribe_bips) << U256::from(8u64))
72 | U256::from(check_mode))
73}
74
75pub fn pack_expected_balance(check_mode: u8, expected_value: U256) -> Result<U256, ConfigError> {
85 pack_config(check_mode, expected_value, 0, 0)
86}
87
88#[cfg(test)]
89#[expect(clippy::unwrap_used)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn pack_default() {
95 assert_eq!(pack_config(0, U256::ZERO, 0, 0).unwrap(), U256::ZERO);
97 }
98
99 #[test]
100 fn pack_check_mode_only() {
101 assert_eq!(pack_config(1, U256::ZERO, 0, 0).unwrap(), U256::from(1u64));
103 }
104
105 #[test]
106 fn pack_bribe_bips_in_byte_2_3() {
107 assert_eq!(
109 pack_config(0, U256::ZERO, 100, 0).unwrap(),
110 U256::from(25_600u64)
111 );
112 }
113
114 #[test]
115 fn pack_recipient_in_byte_3() {
116 assert_eq!(
118 pack_config(0, U256::ZERO, 0, 5).unwrap(),
119 U256::from(83_886_080u64)
120 );
121 }
122
123 #[test]
124 fn pack_expected_value_high_bits() {
125 let v = U256::from(0x12345u64);
127 let packed = pack_config(0, v, 0, 0).unwrap();
128 assert_eq!(packed, v << U256::from(32u64));
129 }
130
131 #[test]
132 fn pack_all_fields_compose() {
133 let expected = U256::from(0xABCu64);
135 let expected_shifted = expected << U256::from(32u64);
136 let bribe = U256::from(500u64) << U256::from(8u64);
137 let recip = U256::from(3u64) << U256::from(24u64);
138 let mode = U256::from(2u64);
139 let want = expected_shifted | recip | bribe | mode;
140 assert_eq!(pack_config(2, expected, 500, 3).unwrap(), want);
141 }
142
143 #[test]
144 fn pack_expected_balance_alias_matches_zero_bribe() {
145 let expected = U256::from(0xDEAD_BEEFu64);
146 assert_eq!(
147 pack_expected_balance(1, expected).unwrap(),
148 pack_config(1, expected, 0, 0).unwrap(),
149 );
150 }
151
152 #[test]
153 fn reject_check_mode_out_of_range() {
154 assert_eq!(pack_config(3, U256::ZERO, 0, 0), Ok(U256::from(3u64)));
155 assert_eq!(
156 pack_config(4, U256::ZERO, 0, 0),
157 Err(ConfigError::CheckModeOutOfRange(4))
158 );
159 }
160
161 #[test]
162 fn reject_bribe_bips_out_of_range() {
163 assert_eq!(
164 pack_config(0, U256::ZERO, 10_001, 0),
165 Err(ConfigError::BribeBipsOutOfRange(10_001)),
166 );
167 }
168
169 #[test]
170 fn reject_recipient_out_of_range() {
171 assert_eq!(
173 pack_config(0, U256::ZERO, 0, 32),
174 Err(ConfigError::BribeRecipientOutOfRange(32)),
175 );
176 }
177
178 #[test]
179 fn accept_recipient_at_upper_bound() {
180 assert!(pack_config(0, U256::ZERO, 0, 31).is_ok());
182 }
183
184 #[test]
185 fn accept_bribe_at_upper_bound() {
186 assert!(pack_config(0, U256::ZERO, 10_000, 0).is_ok());
187 }
188
189 #[test]
190 fn pack_large_expected_value_no_overflow() {
191 let v = U256::from(1u64) << U256::from(200u64);
193 let packed = pack_config(1, v, 300, 2).unwrap();
194 let low = (2u64 << 24) | (300u64 << 8) | 1u64;
196 assert_eq!(packed & U256::from(u64::from(u32::MAX)), U256::from(low));
197 assert_eq!(packed >> U256::from(32u64), v);
198 }
199}