1use crate::types::Call;
8use alloy_primitives::Bytes;
9use alloy_sol_types::{sol, SolCall};
10
11sol! {
12 #[derive(Debug)]
13 struct Call3 {
14 address target;
15 bool allowFailure;
16 bytes callData;
17 }
18
19 function aggregate3(Call3[] calls) external payable returns (bytes[] memory);
20}
21
22pub fn encode_multicall3_aggregate3(calls: &[Call]) -> Bytes {
35 let call3s: Vec<Call3> = calls
36 .iter()
37 .map(|c| Call3 { target: c.target, allowFailure: false, callData: c.calldata.clone() })
38 .collect();
39
40 aggregate3Call { calls: call3s }.abi_encode().into()
41}
42
43pub const MULTICALL3_ADDRESS: alloy_primitives::Address =
45 alloy_primitives::address!("0xcA11bde05977b3631167028862bE2a173976CA11");
46
47pub fn sign_extend_i24(val: alloy_primitives::aliases::I24) -> i32 {
58 let bytes = val.to_le_bytes::<3>();
59 let sign_byte = if bytes[2] & 0x80 != 0 { 0xFF_u8 } else { 0x00_u8 };
60 i32::from_le_bytes([bytes[0], bytes[1], bytes[2], sign_byte])
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::types::Call;
67 use alloy_primitives::{address, bytes, U256};
68
69 #[test]
70 fn encode_multicall3_aggregate3_two_calls() {
71 let calls = vec![
72 Call {
73 target: address!("0x0000000000000000000000000000000000000001"),
74 calldata: bytes!("aa"),
75 value: U256::ZERO,
76 },
77 Call {
78 target: address!("0x0000000000000000000000000000000000000002"),
79 calldata: bytes!("bb"),
80 value: U256::ZERO,
81 },
82 ];
83 let encoded = encode_multicall3_aggregate3(&calls);
84 assert_eq!(&encoded[..4], &[0x82, 0xad, 0x56, 0xcb]);
85 }
86
87 #[test]
88 fn encode_multicall3_aggregate3_round_trips_two_calls() {
89 let input = vec![
90 Call {
91 target: address!("0x0000000000000000000000000000000000000001"),
92 calldata: bytes!("aa"),
93 value: U256::ZERO,
94 },
95 Call {
96 target: address!("0x0000000000000000000000000000000000000002"),
97 calldata: bytes!("bb"),
98 value: U256::ZERO,
99 },
100 ];
101 let encoded = encode_multicall3_aggregate3(&input);
102 let decoded = aggregate3Call::abi_decode(&encoded).expect("abi_decode failed");
103 assert_eq!(decoded.calls.len(), 2);
104 assert_eq!(decoded.calls[0].target, input[0].target);
105 assert!(!decoded.calls[0].allowFailure);
106 assert_eq!(decoded.calls[0].callData, input[0].calldata);
107 assert_eq!(decoded.calls[1].target, input[1].target);
108 assert!(!decoded.calls[1].allowFailure);
109 assert_eq!(decoded.calls[1].callData, input[1].calldata);
110 }
111
112 #[test]
113 fn sign_extend_i24_positive_and_negative() {
114 use alloy_primitives::aliases::I24;
115
116 assert_eq!(sign_extend_i24(I24::ZERO), 0i32);
118
119 assert_eq!(sign_extend_i24(I24::try_from(100i32).unwrap()), 100i32);
121
122 assert_eq!(sign_extend_i24(I24::try_from(-100i32).unwrap()), -100i32);
124
125 assert_eq!(sign_extend_i24(I24::try_from(8_388_607i32).unwrap()), 8_388_607i32);
127
128 assert_eq!(sign_extend_i24(I24::try_from(-8_388_608i32).unwrap()), -8_388_608i32);
130
131 assert_eq!(sign_extend_i24(I24::try_from(887_272i32).unwrap()), 887_272i32);
133 assert_eq!(sign_extend_i24(I24::try_from(-887_272i32).unwrap()), -887_272i32);
134 }
135}