use alloy_primitives::keccak256;
use crate::error::CowError;
fn selector(sig: &str) -> [u8; 4] {
let h = keccak256(sig.as_bytes());
[h[0], h[1], h[2], h[3]]
}
fn u256_be(v: u64) -> [u8; 32] {
let mut out = [0u8; 32];
out[24..].copy_from_slice(&v.to_be_bytes());
out
}
fn pad_to(buf: &mut Vec<u8>, written: usize) {
let rem = written % 32;
if rem != 0 {
buf.resize(buf.len() + (32 - rem), 0);
}
}
pub fn set_pre_signature_calldata(order_uid: &str, signed: bool) -> Result<Vec<u8>, CowError> {
let uid_bytes = decode_uid(order_uid)?;
let padded_len = padded32(uid_bytes.len());
let mut buf = Vec::with_capacity(4 + 2 * 32 + 32 + padded_len);
buf.extend_from_slice(&selector("setPreSignature(bytes,bool)"));
buf.extend_from_slice(&u256_be(64)); buf.extend_from_slice(&u256_be(u64::from(signed)));
buf.extend_from_slice(&u256_be(uid_bytes.len() as u64));
buf.extend_from_slice(&uid_bytes);
pad_to(&mut buf, uid_bytes.len());
Ok(buf)
}
pub fn invalidate_order_calldata(order_uid: &str) -> Result<Vec<u8>, CowError> {
let uid_bytes = decode_uid(order_uid)?;
let padded_len = padded32(uid_bytes.len());
let mut buf = Vec::with_capacity(4 + 32 + 32 + padded_len);
buf.extend_from_slice(&selector("invalidateOrder(bytes)"));
buf.extend_from_slice(&u256_be(32)); buf.extend_from_slice(&u256_be(uid_bytes.len() as u64));
buf.extend_from_slice(&uid_bytes);
pad_to(&mut buf, uid_bytes.len());
Ok(buf)
}
fn decode_uid(uid: &str) -> Result<Vec<u8>, CowError> {
let stripped = uid.trim_start_matches("0x");
alloy_primitives::hex::decode(stripped)
.map_err(|_e| CowError::Api { status: 0, body: format!("invalid orderUid: {uid}") })
}
const fn padded32(n: usize) -> usize {
if n.is_multiple_of(32) { n } else { n + (32 - n % 32) }
}
#[cfg(test)]
mod tests {
use super::*;
fn dummy_uid_56() -> String {
"0x".to_owned() + &"ab".repeat(56)
}
#[test]
fn set_pre_signature_calldata_valid() {
let uid = dummy_uid_56();
let data = set_pre_signature_calldata(&uid, true).unwrap_or_default();
assert_eq!(data.len(), 164);
assert_eq!(&data[..4], &selector("setPreSignature(bytes,bool)"));
}
#[test]
fn set_pre_signature_calldata_false() {
let uid = dummy_uid_56();
let data = set_pre_signature_calldata(&uid, false).unwrap_or_default();
assert_eq!(data.len(), 164);
assert_eq!(data[4 + 32 + 31], 0);
}
#[test]
fn set_pre_signature_calldata_true() {
let uid = dummy_uid_56();
let data = set_pre_signature_calldata(&uid, true).unwrap_or_default();
assert_eq!(data[4 + 32 + 31], 1);
}
#[test]
fn invalidate_order_calldata_valid() {
let uid = dummy_uid_56();
let data = invalidate_order_calldata(&uid).unwrap_or_default();
assert_eq!(data.len(), 132);
assert_eq!(&data[..4], &selector("invalidateOrder(bytes)"));
}
#[test]
fn calldata_rejects_invalid_hex() {
assert!(set_pre_signature_calldata("not_hex", true).is_err());
assert!(invalidate_order_calldata("0xZZZZ").is_err());
}
#[test]
fn calldata_works_without_0x_prefix() {
let uid = "ab".repeat(56);
assert!(set_pre_signature_calldata(&uid, true).is_ok());
assert!(invalidate_order_calldata(&uid).is_ok());
}
#[test]
fn calldata_empty_uid() {
let data = invalidate_order_calldata("0x").unwrap_or_default();
assert_eq!(data.len(), 68);
}
#[test]
fn padded32_rounds_up() {
assert_eq!(padded32(0), 0);
assert_eq!(padded32(1), 32);
assert_eq!(padded32(31), 32);
assert_eq!(padded32(32), 32);
assert_eq!(padded32(33), 64);
assert_eq!(padded32(64), 64);
}
}