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 #[derive(Debug)]
22 struct Call3Value {
23 address target;
24 bool allowFailure;
25 uint256 value;
26 bytes callData;
27 }
28
29 function aggregate3Value(Call3Value[] calls) external payable returns (bytes[] memory);
30}
31
32pub fn encode_multicall3_aggregate3(calls: &[Call]) -> Bytes {
45 let call3s: Vec<Call3> = calls
46 .iter()
47 .map(|c| Call3 { target: c.target, allowFailure: false, callData: c.calldata.clone() })
48 .collect();
49
50 aggregate3Call { calls: call3s }.abi_encode().into()
51}
52
53pub fn encode_multicall3_aggregate3_value(calls: &[Call]) -> Bytes {
70 let call3s: Vec<Call3Value> = calls
71 .iter()
72 .map(|c| Call3Value {
73 target: c.target,
74 allowFailure: false,
75 value: c.value,
76 callData: c.calldata.clone(),
77 })
78 .collect();
79
80 aggregate3ValueCall { calls: call3s }.abi_encode().into()
81}
82
83pub const MULTICALL3_ADDRESS: alloy_primitives::Address =
85 alloy_primitives::address!("0xcA11bde05977b3631167028862bE2a173976CA11");
86
87pub fn sign_extend_i24(val: alloy_primitives::aliases::I24) -> i32 {
98 let bytes = val.to_le_bytes::<3>();
99 let sign_byte = if bytes[2] & 0x80 != 0 { 0xFF_u8 } else { 0x00_u8 };
100 i32::from_le_bytes([bytes[0], bytes[1], bytes[2], sign_byte])
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::types::Call;
107 use alloy_primitives::{address, bytes, U256};
108
109 #[test]
110 fn encode_multicall3_aggregate3_two_calls() {
111 let calls = vec![
112 Call {
113 target: address!("0x0000000000000000000000000000000000000001"),
114 calldata: bytes!("aa"),
115 value: U256::ZERO,
116 },
117 Call {
118 target: address!("0x0000000000000000000000000000000000000002"),
119 calldata: bytes!("bb"),
120 value: U256::ZERO,
121 },
122 ];
123 let encoded = encode_multicall3_aggregate3(&calls);
124 assert_eq!(&encoded[..4], &[0x82, 0xad, 0x56, 0xcb]);
125 }
126
127 #[test]
128 fn encode_multicall3_aggregate3_round_trips_two_calls() {
129 let input = vec![
130 Call {
131 target: address!("0x0000000000000000000000000000000000000001"),
132 calldata: bytes!("aa"),
133 value: U256::ZERO,
134 },
135 Call {
136 target: address!("0x0000000000000000000000000000000000000002"),
137 calldata: bytes!("bb"),
138 value: U256::ZERO,
139 },
140 ];
141 let encoded = encode_multicall3_aggregate3(&input);
142 let decoded = aggregate3Call::abi_decode(&encoded).expect("abi_decode failed");
143 assert_eq!(decoded.calls.len(), 2);
144 assert_eq!(decoded.calls[0].target, input[0].target);
145 assert!(!decoded.calls[0].allowFailure);
146 assert_eq!(decoded.calls[0].callData, input[0].calldata);
147 assert_eq!(decoded.calls[1].target, input[1].target);
148 assert!(!decoded.calls[1].allowFailure);
149 assert_eq!(decoded.calls[1].callData, input[1].calldata);
150 }
151
152 #[test]
153 fn sign_extend_i24_positive_and_negative() {
154 use alloy_primitives::aliases::I24;
155
156 assert_eq!(sign_extend_i24(I24::ZERO), 0i32);
158
159 assert_eq!(sign_extend_i24(I24::try_from(100i32).unwrap()), 100i32);
161
162 assert_eq!(sign_extend_i24(I24::try_from(-100i32).unwrap()), -100i32);
164
165 assert_eq!(sign_extend_i24(I24::try_from(8_388_607i32).unwrap()), 8_388_607i32);
167
168 assert_eq!(sign_extend_i24(I24::try_from(-8_388_608i32).unwrap()), -8_388_608i32);
170
171 assert_eq!(sign_extend_i24(I24::try_from(887_272i32).unwrap()), 887_272i32);
173 assert_eq!(sign_extend_i24(I24::try_from(-887_272i32).unwrap()), -887_272i32);
174 }
175
176 #[test]
177 fn encode_multicall3_aggregate3_value_forwards_value_and_selector() {
178 let calls = vec![
179 Call {
180 target: address!("0x0000000000000000000000000000000000000001"),
181 calldata: bytes!("aa"),
182 value: U256::ZERO,
183 },
184 Call {
185 target: address!("0x0000000000000000000000000000000000000002"),
186 calldata: bytes!("bb"),
187 value: U256::from(1_000_000_000_000_000_000u64), },
189 ];
190 let encoded = encode_multicall3_aggregate3_value(&calls);
191
192 assert_eq!(&encoded[..4], &[0x17, 0x4d, 0xea, 0x71]);
194
195 let decoded = aggregate3ValueCall::abi_decode(&encoded).expect("abi_decode failed");
197 assert_eq!(decoded.calls.len(), 2);
198 assert!(!decoded.calls[0].allowFailure);
199 assert_eq!(decoded.calls[0].value, U256::ZERO);
200 assert_eq!(decoded.calls[1].value, U256::from(1_000_000_000_000_000_000u64));
201 assert_eq!(decoded.calls[1].callData, calls[1].calldata);
202 }
203}