use alloy_primitives::{Address, B256, keccak256};
use super::types::{ConditionalOrderParams, ProofStruct};
fn selector(sig: &str) -> [u8; 4] {
let h = keccak256(sig.as_bytes());
[h[0], h[1], h[2], h[3]]
}
#[must_use]
pub fn create_with_context_calldata(
params: &ConditionalOrderParams,
factory: Address,
factory_data: &[u8],
dispatch: bool,
) -> Vec<u8> {
let sel = selector("createWithContext((address,bytes32,bytes),address,bytes,bool)");
let static_input = ¶ms.static_input;
let static_input_padded_len = padded32(static_input.len());
let tuple_size = 3 * 32 + 32 + static_input_padded_len;
let factory_data_padded_len = padded32(factory_data.len());
let factory_data_enc_size = 32 + factory_data_padded_len;
let offset_tuple: u64 = 4 * 32;
let offset_factory_data: u64 = offset_tuple + tuple_size as u64;
let total = 4 + 4 * 32 + tuple_size + factory_data_enc_size;
let mut buf = Vec::with_capacity(total);
buf.extend_from_slice(&sel);
buf.extend_from_slice(&u256_be(offset_tuple)); buf.extend_from_slice(&pad_address(factory.as_slice())); buf.extend_from_slice(&u256_be(offset_factory_data)); buf.extend_from_slice(&u256_be(u64::from(dispatch)));
buf.extend_from_slice(&pad_address(params.handler.as_slice()));
buf.extend_from_slice(params.salt.as_slice());
buf.extend_from_slice(&u256_be(96u64)); buf.extend_from_slice(&u256_be(static_input.len() as u64));
buf.extend_from_slice(static_input);
pad_to(&mut buf, static_input.len());
buf.extend_from_slice(&u256_be(factory_data.len() as u64));
buf.extend_from_slice(factory_data);
pad_to(&mut buf, factory_data.len());
buf
}
#[must_use]
pub fn set_root_calldata(
root: B256,
proof: &ProofStruct,
params: &ConditionalOrderParams,
) -> Vec<u8> {
let sel = selector("setRoot(bytes32,(uint256,bytes),(address,bytes32,bytes))");
let proof_enc = encode_proof_struct(proof);
let params_enc = encode_params_tuple(params);
let proof_offset: u64 = 3 * 32;
let params_offset: u64 = proof_offset + proof_enc.len() as u64;
let mut buf = Vec::with_capacity(4 + 3 * 32 + proof_enc.len() + params_enc.len());
buf.extend_from_slice(&sel);
buf.extend_from_slice(root.as_slice());
buf.extend_from_slice(&u256_be(proof_offset));
buf.extend_from_slice(&u256_be(params_offset));
buf.extend_from_slice(&proof_enc);
buf.extend_from_slice(¶ms_enc);
buf
}
#[must_use]
pub fn set_root_with_context_calldata(
root: B256,
proof: &ProofStruct,
params: &ConditionalOrderParams,
factory: Address,
factory_data: &[u8],
) -> Vec<u8> {
let sel = selector(
"setRootWithContext(bytes32,(uint256,bytes),(address,bytes32,bytes),address,bytes)",
);
let proof_enc = encode_proof_struct(proof);
let params_enc = encode_params_tuple(params);
let factory_data_padded_len = padded32(factory_data.len());
let factory_data_enc_size = 32 + factory_data_padded_len;
let proof_offset: u64 = 5 * 32;
let params_offset: u64 = proof_offset + proof_enc.len() as u64;
let factory_data_offset: u64 = params_offset + params_enc.len() as u64;
let mut buf =
Vec::with_capacity(4 + 5 * 32 + proof_enc.len() + params_enc.len() + factory_data_enc_size);
buf.extend_from_slice(&sel);
buf.extend_from_slice(root.as_slice());
buf.extend_from_slice(&u256_be(proof_offset));
buf.extend_from_slice(&u256_be(params_offset));
buf.extend_from_slice(&pad_address(factory.as_slice()));
buf.extend_from_slice(&u256_be(factory_data_offset));
buf.extend_from_slice(&proof_enc);
buf.extend_from_slice(¶ms_enc);
buf.extend_from_slice(&u256_be(factory_data.len() as u64));
buf.extend_from_slice(factory_data);
pad_to(&mut buf, factory_data.len());
buf
}
#[must_use]
pub fn remove_calldata(order_id: B256) -> Vec<u8> {
let sel = selector("remove(bytes32)");
let mut buf = Vec::with_capacity(36);
buf.extend_from_slice(&sel);
buf.extend_from_slice(order_id.as_slice());
buf
}
#[must_use]
pub fn create_calldata(
params: &ConditionalOrderParams,
proof: &[[B256; 2]],
offchain_input: &[u8],
) -> Vec<u8> {
let sel = selector("create((address,bytes32,bytes),bytes32[2][],bytes)");
let static_input = ¶ms.static_input;
let static_input_padded_len = padded32(static_input.len());
let tuple_size = 3 * 32 + 32 + static_input_padded_len;
let proof_size = 32 + proof.len() * 64;
let offchain_padded_len = padded32(offchain_input.len());
let offchain_enc_size = 32 + offchain_padded_len;
let offset_tuple: u64 = 3 * 32; let offset_proof: u64 = offset_tuple + tuple_size as u64;
let offset_offchain: u64 = offset_proof + proof_size as u64;
let total = 4 + 3 * 32 + tuple_size + proof_size + offchain_enc_size;
let mut buf = Vec::with_capacity(total);
buf.extend_from_slice(&sel);
buf.extend_from_slice(&u256_be(offset_tuple));
buf.extend_from_slice(&u256_be(offset_proof));
buf.extend_from_slice(&u256_be(offset_offchain));
buf.extend_from_slice(&pad_address(params.handler.as_slice()));
buf.extend_from_slice(params.salt.as_slice());
buf.extend_from_slice(&u256_be(96u64)); buf.extend_from_slice(&u256_be(static_input.len() as u64));
buf.extend_from_slice(static_input);
pad_to(&mut buf, static_input.len());
buf.extend_from_slice(&u256_be(proof.len() as u64));
for pair in proof {
buf.extend_from_slice(pair[0].as_slice());
buf.extend_from_slice(pair[1].as_slice());
}
buf.extend_from_slice(&u256_be(offchain_input.len() as u64));
buf.extend_from_slice(offchain_input);
pad_to(&mut buf, offchain_input.len());
buf
}
fn encode_proof_struct(proof: &ProofStruct) -> Vec<u8> {
let data_padded_len = padded32(proof.data.len());
let mut out = Vec::with_capacity(64 + 32 + data_padded_len);
out.extend_from_slice(&u256_be(proof.location as u64));
out.extend_from_slice(&u256_be(64u64)); out.extend_from_slice(&u256_be(proof.data.len() as u64));
out.extend_from_slice(&proof.data);
pad_to(&mut out, proof.data.len());
out
}
fn encode_params_tuple(params: &ConditionalOrderParams) -> Vec<u8> {
let si = ¶ms.static_input;
let si_padded_len = padded32(si.len());
let mut out = Vec::with_capacity(3 * 32 + 32 + si_padded_len);
out.extend_from_slice(&pad_address(params.handler.as_slice()));
out.extend_from_slice(params.salt.as_slice());
out.extend_from_slice(&u256_be(96u64)); out.extend_from_slice(&u256_be(si.len() as u64));
out.extend_from_slice(si);
pad_to(&mut out, si.len());
out
}
fn pad_address(bytes: &[u8]) -> [u8; 32] {
let mut out = [0u8; 32];
out[12..].copy_from_slice(bytes);
out
}
fn u256_be(v: u64) -> [u8; 32] {
let mut out = [0u8; 32];
out[24..].copy_from_slice(&v.to_be_bytes());
out
}
const fn padded32(n: usize) -> usize {
if n.is_multiple_of(32) { n } else { n + (32 - n % 32) }
}
fn pad_to(buf: &mut Vec<u8>, written: usize) {
let rem = written % 32;
if rem != 0 {
buf.resize(buf.len() + (32 - rem), 0);
}
}
#[cfg(test)]
mod tests {
use crate::composable::types::ProofLocation;
use super::*;
fn dummy_params() -> ConditionalOrderParams {
ConditionalOrderParams {
handler: Address::ZERO,
salt: B256::ZERO,
static_input: vec![0xaau8; 32],
}
}
#[test]
fn set_root_calldata_has_correct_selector() {
let expected_sel = {
let h = keccak256(b"setRoot(bytes32,(uint256,bytes),(address,bytes32,bytes))");
[h[0], h[1], h[2], h[3]]
};
let proof = ProofStruct { location: ProofLocation::Private, data: vec![] };
let cd = set_root_calldata(B256::ZERO, &proof, &dummy_params());
assert_eq!(&cd[..4], expected_sel);
assert_eq!(cd.len(), 4 + 3 * 32 + 96 + 160);
}
#[test]
fn set_root_with_context_calldata_has_correct_selector() {
let expected_sel = {
let h = keccak256(
b"setRootWithContext(bytes32,(uint256,bytes),(address,bytes32,bytes),address,bytes)",
);
[h[0], h[1], h[2], h[3]]
};
let proof = ProofStruct { location: ProofLocation::Private, data: vec![] };
let cd =
set_root_with_context_calldata(B256::ZERO, &proof, &dummy_params(), Address::ZERO, &[]);
assert_eq!(&cd[..4], expected_sel);
assert_eq!(cd.len(), 4 + 5 * 32 + 96 + 160 + 32);
}
#[test]
fn set_root_calldata_embeds_location() {
let proof = ProofStruct { location: ProofLocation::Ipfs, data: vec![0x01, 0x02] };
let cd = set_root_calldata(B256::ZERO, &proof, &dummy_params());
let loc_slot = &cd[100..132];
assert_eq!(loc_slot[31], 5u8); }
#[test]
fn create_with_context_calldata_has_correct_selector() {
let expected_sel = {
let h = keccak256(b"createWithContext((address,bytes32,bytes),address,bytes,bool)");
[h[0], h[1], h[2], h[3]]
};
let cd = create_with_context_calldata(&dummy_params(), Address::ZERO, &[], true);
assert_eq!(&cd[..4], expected_sel);
}
#[test]
fn create_with_context_calldata_dispatch_true() {
let cd = create_with_context_calldata(&dummy_params(), Address::ZERO, &[], true);
assert_eq!(cd[4 + 3 * 32 + 31], 1u8);
}
#[test]
fn create_with_context_calldata_dispatch_false() {
let cd = create_with_context_calldata(&dummy_params(), Address::ZERO, &[], false);
assert_eq!(cd[4 + 3 * 32 + 31], 0u8);
}
#[test]
fn create_with_context_calldata_with_factory_data() {
let factory_data = vec![0xffu8; 40];
let cd = create_with_context_calldata(
&dummy_params(),
Address::repeat_byte(0x11),
&factory_data,
false,
);
assert!(cd.windows(40).any(|w| w == &*factory_data));
}
#[test]
fn create_with_context_calldata_length_aligned() {
let cd = create_with_context_calldata(&dummy_params(), Address::ZERO, &[0xab; 5], true);
assert_eq!((cd.len() - 4) % 32, 0);
}
#[test]
fn remove_calldata_has_correct_selector() {
let expected_sel = {
let h = keccak256(b"remove(bytes32)");
[h[0], h[1], h[2], h[3]]
};
let cd = remove_calldata(B256::ZERO);
assert_eq!(&cd[..4], expected_sel);
}
#[test]
fn remove_calldata_has_correct_length() {
let cd = remove_calldata(B256::ZERO);
assert_eq!(cd.len(), 36); }
#[test]
fn remove_calldata_embeds_order_id() {
let id = B256::new([0xabu8; 32]);
let cd = remove_calldata(id);
assert_eq!(&cd[4..36], id.as_slice());
}
#[test]
fn create_calldata_has_correct_selector() {
let expected_sel = {
let h = keccak256(b"create((address,bytes32,bytes),bytes32[2][],bytes)");
[h[0], h[1], h[2], h[3]]
};
let cd = create_calldata(&dummy_params(), &[], &[]);
assert_eq!(&cd[..4], expected_sel);
}
#[test]
fn create_calldata_empty_proof_and_offchain() {
let cd = create_calldata(&dummy_params(), &[], &[]);
assert_eq!((cd.len() - 4) % 32, 0);
}
#[test]
fn create_calldata_with_proof_pairs() {
let pair = [B256::new([0x11u8; 32]), B256::new([0x22u8; 32])];
let cd = create_calldata(&dummy_params(), &[pair], &[]);
assert!(cd.windows(32).any(|w| w == pair[0].as_slice()));
assert!(cd.windows(32).any(|w| w == pair[1].as_slice()));
}
#[test]
fn create_calldata_with_offchain_input() {
let offchain = vec![0xddu8; 17];
let cd = create_calldata(&dummy_params(), &[], &offchain);
assert!(cd.windows(17).any(|w| w == &*offchain));
assert_eq!((cd.len() - 4) % 32, 0);
}
#[test]
fn padded32_multiples() {
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);
}
#[test]
fn u256_be_encodes_correctly() {
let w = u256_be(1);
assert_eq!(w[31], 1);
assert_eq!(w[..31], [0u8; 31]);
let w = u256_be(256);
assert_eq!(w[30], 1);
assert_eq!(w[31], 0);
}
#[test]
fn pad_address_correct() {
let addr = Address::repeat_byte(0xff);
let padded = pad_address(addr.as_slice());
assert_eq!(&padded[..12], &[0u8; 12]);
assert_eq!(&padded[12..], addr.as_slice());
}
#[test]
fn pad_to_aligns_buffer() {
let mut buf = vec![0u8; 10];
pad_to(&mut buf, 10);
assert_eq!(buf.len(), 32);
let mut buf2 = vec![0u8; 32];
pad_to(&mut buf2, 32);
assert_eq!(buf2.len(), 32); }
#[test]
fn selector_is_deterministic() {
let s1 = selector("remove(bytes32)");
let s2 = selector("remove(bytes32)");
assert_eq!(s1, s2);
}
#[test]
fn set_root_with_context_calldata_with_factory_data() {
let proof = ProofStruct { location: ProofLocation::Emitted, data: vec![0xcc; 10] };
let factory_data = vec![0xffu8; 20];
let cd = set_root_with_context_calldata(
B256::new([0x01u8; 32]),
&proof,
&dummy_params(),
Address::repeat_byte(0x22),
&factory_data,
);
assert_eq!((cd.len() - 4) % 32, 0);
assert!(cd.windows(20).any(|w| w == &*factory_data));
}
}