alloy_provider/provider/multicall/
inner_types.rs1use std::{fmt::Debug, marker::PhantomData};
2
3use super::{
4 bindings::IMulticall3::{Call, Call3, Call3Value},
5 CallTuple,
6};
7use alloy_primitives::{Address, Bytes, U256};
8use alloy_sol_types::SolCall;
9use thiserror::Error;
10
11pub type Result<T, E = MulticallError> = core::result::Result<T, E>;
13
14#[derive(Debug, Clone, PartialEq, Eq, Error)]
16#[error("Call failed at index {idx} with return data: {return_data:?}")]
17pub struct Failure {
18 pub idx: usize,
20 pub return_data: Bytes,
22}
23
24pub trait MulticallItem {
27 type Decoder: SolCall;
29
30 fn value(&self) -> U256;
32
33 fn target(&self) -> Address;
35 fn input(&self) -> Bytes;
37
38 fn into_call(self, allow_failure: bool) -> CallItem<Self::Decoder>
40 where
41 Self: Sized,
42 {
43 CallItem::<Self::Decoder>::from(self).allow_failure(allow_failure)
44 }
45}
46
47#[derive(Debug)]
49pub struct CallItemBuilder;
50
51impl CallItemBuilder {
52 #[expect(clippy::new_ret_no_self)]
54 pub fn new<Item: MulticallItem>(item: Item) -> CallItem<Item::Decoder> {
55 CallItem::new(item.target(), item.input())
56 }
57}
58
59#[derive(Clone)]
62pub struct CallItem<D: SolCall> {
63 target: Address,
64 input: Bytes,
65 allow_failure: bool,
66 value: U256,
67 decoder: PhantomData<D>,
68}
69
70impl<D: SolCall> Debug for CallItem<D> {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 f.debug_struct("CallItem")
73 .field("target", &self.target)
74 .field("allow_failure", &self.allow_failure)
75 .field("value", &self.value)
76 .field("input", &self.input)
77 .finish()
78 }
79}
80
81impl<D: SolCall> CallItem<D> {
82 pub const fn new(target: Address, input: Bytes) -> Self {
84 Self { target, input, allow_failure: false, value: U256::ZERO, decoder: PhantomData }
85 }
86
87 pub const fn allow_failure(mut self, allow_failure: bool) -> Self {
89 self.allow_failure = allow_failure;
90 self
91 }
92
93 pub const fn with_failure_allowed(self) -> Self {
95 self.allow_failure(true)
96 }
97
98 pub const fn value(mut self, value: U256) -> Self {
100 self.value = value;
101 self
102 }
103}
104impl<D: SolCall> CallInfoTrait for CallItem<D> {
105 fn to_call(&self) -> Call {
106 Call { target: self.target, callData: self.input.clone() }
107 }
108
109 fn to_call3(&self) -> Call3 {
110 Call3 {
111 target: self.target,
112 allowFailure: self.allow_failure,
113 callData: self.input.clone(),
114 }
115 }
116
117 fn to_call3_value(&self) -> Call3Value {
118 Call3Value {
119 target: self.target,
120 allowFailure: self.allow_failure,
121 callData: self.input.clone(),
122 value: self.value,
123 }
124 }
125}
126pub trait CallInfoTrait: std::fmt::Debug {
128 fn to_call(&self) -> Call;
130 fn to_call3(&self) -> Call3;
132 fn to_call3_value(&self) -> Call3Value;
134}
135
136impl<T, D> From<T> for CallItem<D>
137where
138 T: MulticallItem,
139 D: SolCall,
140{
141 fn from(value: T) -> Self {
148 Self::new(value.target(), value.input()).value(value.value())
149 }
150}
151
152#[derive(Debug)]
155pub struct Dynamic<D: SolCall>(PhantomData<fn(D) -> D>);
156
157impl<D: SolCall> CallTuple for Dynamic<D> {
158 type Returns = Vec<Result<D::Return, Failure>>;
159 type SuccessReturns = Vec<D::Return>;
160
161 fn decode_returns(data: &[Bytes]) -> Result<Self::SuccessReturns> {
162 data.iter().map(|d| D::abi_decode_returns(d).map_err(MulticallError::DecodeError)).collect()
163 }
164
165 fn decode_return_results(
166 results: &[super::bindings::IMulticall3::Result],
167 ) -> Result<Self::Returns> {
168 let mut ret = vec![];
169 for (idx, res) in results.iter().enumerate() {
170 if res.success {
171 ret.push(
172 D::abi_decode_returns(&res.returnData)
173 .map_err(|_| Failure { idx, return_data: res.returnData.clone() }),
174 )
175 } else {
176 ret.push(Err(Failure { idx, return_data: res.returnData.clone() }));
177 }
178 }
179
180 Ok(ret)
181 }
182
183 fn try_into_success(results: Self::Returns) -> Result<Self::SuccessReturns> {
184 let mut ret = vec![];
185 for res in results {
186 ret.push(res.map_err(|e| MulticallError::CallFailed(e.return_data))?);
187 }
188 Ok(ret)
189 }
190}
191
192#[derive(Debug, Error)]
194pub enum MulticallError {
195 #[error("batch contains a tx with a value, try using .send() instead")]
197 ValueTx,
198 #[error("could not decode")]
200 DecodeError(alloy_sol_types::Error),
201 #[error("no return data")]
203 NoReturnData,
204 #[error("call failed when success was assured, this occurs when try_into_success is called on a failed call")]
206 CallFailed(Bytes),
207 #[error("Transport error: {0}")]
209 TransportError(#[from] alloy_transport::TransportError),
210}