use crate::types::Call;
use alloy_primitives::Bytes;
use alloy_sol_types::{sol, SolCall};
sol! {
#[derive(Debug)]
struct Call3 {
address target;
bool allowFailure;
bytes callData;
}
function aggregate3(Call3[] calls) external payable returns (bytes[] memory);
}
pub fn encode_multicall3_aggregate3(calls: &[Call]) -> Bytes {
let call3s: Vec<Call3> = calls
.iter()
.map(|c| Call3 { target: c.target, allowFailure: false, callData: c.calldata.clone() })
.collect();
aggregate3Call { calls: call3s }.abi_encode().into()
}
pub const MULTICALL3_ADDRESS: alloy_primitives::Address =
alloy_primitives::address!("0xcA11bde05977b3631167028862bE2a173976CA11");
pub fn sign_extend_i24(val: alloy_primitives::aliases::I24) -> i32 {
let bytes = val.to_le_bytes::<3>();
let sign_byte = if bytes[2] & 0x80 != 0 { 0xFF_u8 } else { 0x00_u8 };
i32::from_le_bytes([bytes[0], bytes[1], bytes[2], sign_byte])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::Call;
use alloy_primitives::{address, bytes, U256};
#[test]
fn encode_multicall3_aggregate3_two_calls() {
let calls = vec![
Call {
target: address!("0x0000000000000000000000000000000000000001"),
calldata: bytes!("aa"),
value: U256::ZERO,
},
Call {
target: address!("0x0000000000000000000000000000000000000002"),
calldata: bytes!("bb"),
value: U256::ZERO,
},
];
let encoded = encode_multicall3_aggregate3(&calls);
assert_eq!(&encoded[..4], &[0x82, 0xad, 0x56, 0xcb]);
}
#[test]
fn encode_multicall3_aggregate3_round_trips_two_calls() {
let input = vec![
Call {
target: address!("0x0000000000000000000000000000000000000001"),
calldata: bytes!("aa"),
value: U256::ZERO,
},
Call {
target: address!("0x0000000000000000000000000000000000000002"),
calldata: bytes!("bb"),
value: U256::ZERO,
},
];
let encoded = encode_multicall3_aggregate3(&input);
let decoded = aggregate3Call::abi_decode(&encoded).expect("abi_decode failed");
assert_eq!(decoded.calls.len(), 2);
assert_eq!(decoded.calls[0].target, input[0].target);
assert!(!decoded.calls[0].allowFailure);
assert_eq!(decoded.calls[0].callData, input[0].calldata);
assert_eq!(decoded.calls[1].target, input[1].target);
assert!(!decoded.calls[1].allowFailure);
assert_eq!(decoded.calls[1].callData, input[1].calldata);
}
#[test]
fn sign_extend_i24_positive_and_negative() {
use alloy_primitives::aliases::I24;
assert_eq!(sign_extend_i24(I24::ZERO), 0i32);
assert_eq!(sign_extend_i24(I24::try_from(100i32).unwrap()), 100i32);
assert_eq!(sign_extend_i24(I24::try_from(-100i32).unwrap()), -100i32);
assert_eq!(sign_extend_i24(I24::try_from(8_388_607i32).unwrap()), 8_388_607i32);
assert_eq!(sign_extend_i24(I24::try_from(-8_388_608i32).unwrap()), -8_388_608i32);
assert_eq!(sign_extend_i24(I24::try_from(887_272i32).unwrap()), 887_272i32);
assert_eq!(sign_extend_i24(I24::try_from(-887_272i32).unwrap()), -887_272i32);
}
}