use alloy::primitives::U256;
use crate::encoders::MAX_INDEXED_ADDRESSES;
pub const MAX_BRIBE_BIPS: u16 = 10_000;
pub const MAX_CHECK_MODE: u8 = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum ConfigError {
#[error("check_mode must be 0–3, got {0}")]
CheckModeOutOfRange(u8),
#[error("bribe_bips must be 0–10000, got {0}")]
BribeBipsOutOfRange(u16),
#[error("bribe_recipient_idx must be 0–31, got {0}")]
BribeRecipientOutOfRange(u8),
}
pub fn pack_config(
check_mode: u8,
expected_value: U256,
bribe_bips: u16,
bribe_recipient_idx: u8,
) -> Result<U256, ConfigError> {
if check_mode > MAX_CHECK_MODE {
return Err(ConfigError::CheckModeOutOfRange(check_mode));
}
if bribe_bips > MAX_BRIBE_BIPS {
return Err(ConfigError::BribeBipsOutOfRange(bribe_bips));
}
if bribe_recipient_idx as usize >= MAX_INDEXED_ADDRESSES {
return Err(ConfigError::BribeRecipientOutOfRange(bribe_recipient_idx));
}
Ok((expected_value << U256::from(32u64))
| (U256::from(bribe_recipient_idx) << U256::from(24u64))
| (U256::from(bribe_bips) << U256::from(8u64))
| U256::from(check_mode))
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn pack_default() {
assert_eq!(pack_config(0, U256::ZERO, 0, 0).unwrap(), U256::ZERO);
}
#[test]
fn pack_check_mode_only() {
assert_eq!(pack_config(1, U256::ZERO, 0, 0).unwrap(), U256::from(1u64));
}
#[test]
fn pack_bribe_bips_in_byte_2_3() {
assert_eq!(
pack_config(0, U256::ZERO, 100, 0).unwrap(),
U256::from(25_600u64)
);
}
#[test]
fn pack_recipient_in_byte_3() {
assert_eq!(
pack_config(0, U256::ZERO, 0, 5).unwrap(),
U256::from(83_886_080u64)
);
}
#[test]
fn pack_expected_value_high_bits() {
let v = U256::from(0x12345u64);
let packed = pack_config(0, v, 0, 0).unwrap();
assert_eq!(packed, v << U256::from(32u64));
}
#[test]
fn pack_all_fields_compose() {
let expected = U256::from(0xABCu64);
let expected_shifted = expected << U256::from(32u64);
let bribe = U256::from(500u64) << U256::from(8u64);
let recip = U256::from(3u64) << U256::from(24u64);
let mode = U256::from(2u64);
let want = expected_shifted | recip | bribe | mode;
assert_eq!(pack_config(2, expected, 500, 3).unwrap(), want);
}
#[test]
fn reject_check_mode_out_of_range() {
assert_eq!(pack_config(3, U256::ZERO, 0, 0), Ok(U256::from(3u64)));
assert_eq!(
pack_config(4, U256::ZERO, 0, 0),
Err(ConfigError::CheckModeOutOfRange(4))
);
}
#[test]
fn reject_bribe_bips_out_of_range() {
assert_eq!(
pack_config(0, U256::ZERO, 10_001, 0),
Err(ConfigError::BribeBipsOutOfRange(10_001)),
);
}
#[test]
fn reject_recipient_out_of_range() {
assert_eq!(
pack_config(0, U256::ZERO, 0, 32),
Err(ConfigError::BribeRecipientOutOfRange(32)),
);
}
#[test]
fn accept_recipient_at_upper_bound() {
assert!(pack_config(0, U256::ZERO, 0, 31).is_ok());
}
#[test]
fn accept_bribe_at_upper_bound() {
assert!(pack_config(0, U256::ZERO, 10_000, 0).is_ok());
}
#[test]
fn pack_large_expected_value_no_overflow() {
let v = U256::from(1u64) << U256::from(200u64);
let packed = pack_config(1, v, 300, 2).unwrap();
let low = (2u64 << 24) | (300u64 << 8) | 1u64;
assert_eq!(packed & U256::from(u64::from(u32::MAX)), U256::from(low));
assert_eq!(packed >> U256::from(32u64), v);
}
}