alloy_provider/provider/multicall/
tuple.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
use super::{
    bindings::IMulticall3::Result as MulticallResult,
    inner_types::{Dynamic, Failure, MulticallError, Result},
};
use alloy_primitives::Bytes;
use alloy_sol_types::SolCall;

/// Sealed trait to prevent external implementations
mod private {
    #[allow(unnameable_types)]
    pub trait Sealed {}
}
use private::Sealed;

/// A trait for tuples that can have types pushed to them
#[doc(hidden)]
#[allow(unnameable_types)]
pub trait TuplePush<T> {
    /// The resulting type after pushing T
    type Pushed;
}

/// A trait for tuples of SolCalls that can be decoded
#[doc(hidden)]
pub trait CallTuple: Sealed {
    /// Flattened tuple consisting of the return values of each call.
    ///
    /// Each return value is wrapped in a [`Result`] in order to account for failures in calls when
    /// others succeed.
    ///
    /// - [`Result::Ok`] contains the decoded return value of the call.
    /// - [`Result::Err`] contains a [`Failure`] struct with the index of the call that failed and
    ///   the raw bytes returned on failure.
    ///
    /// For example,
    ///
    /// ```ignore
    /// use alloy_sol_types::sol;
    /// use alloy_primitives::Address;
    /// use alloy_provider::{CallItem, Provider, ProviderBuilder, Result, Failure};
    /// use crate::SomeContract::failureCall;
    /// sol! {
    ///     #[derive(Debug)]
    ///     #[sol(rpc)]
    ///     contract SomeContract {
    ///       function success() external;
    ///       function failure() external;
    ///     }
    /// }
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let target = Address::random();
    ///     let provider = ProviderBuilder::new().on_builtin("https://..").await.unwrap();    
    ///     let some_contract = SomeContract::new(target, &provider);
    ///     let allow_failure_call = CallItem::<failureCall>::new(target, some_contract.failure().input()).allow_failure(true); // This calls is allowed to fail so that the batch doesn't revert.
    ///
    ///     let multicall = provider.multicall().add(some_contract.success()).add_call(allow_failure_call);
    ///
    ///     let (success_result, failure_result) = multicall.aggregate3().await.unwrap();
    ///     match success_result {
    ///       Ok(success) => { println!("Success: {:?}", success) },
    ///       Err(failure) => { /* handle failure */ },
    ///     }
    ///
    ///     match failure_result {
    ///       Ok(success) => { /* handle success */ },
    ///       Err(failure) => { assert!(matches!(failure, Failure { idx: 1, return_data: _ })) },
    ///     }
    /// }
    /// ```
    type Returns;

    /// Flattened tuple consisting of the decoded return values of each call.
    type SuccessReturns;

    /// Decode the returns from a sequence of bytes
    ///
    /// To be used for calls where success is ensured i.e `allow_failure` for all calls is false.
    fn decode_returns(data: &[Bytes]) -> Result<Self::SuccessReturns>;

    /// Converts Returns to SuccessReturns if all results are Ok
    fn decode_return_results(results: &[MulticallResult]) -> Result<Self::Returns>;

    /// Converts Returns to SuccessReturns if all results are Ok
    fn try_into_success(results: Self::Returns) -> Result<Self::SuccessReturns>;
}

/// Type indicating that the [`MulticallBuilder`](crate::MulticallBuilder) is empty.
#[derive(Debug, Clone)]
pub struct Empty;

impl Sealed for Empty {}

impl<T: SolCall> TuplePush<T> for Empty {
    type Pushed = (T,);
}

impl CallTuple for Empty {
    type Returns = ();
    type SuccessReturns = ();
    fn decode_returns(_: &[Bytes]) -> Result<Self::SuccessReturns> {
        Ok(())
    }
    fn decode_return_results(_results: &[MulticallResult]) -> Result<Self::Returns> {
        Ok(())
    }
    fn try_into_success(_: Self::Returns) -> Result<Self::SuccessReturns> {
        Ok(())
    }
}

impl<D: SolCall> Sealed for Dynamic<D> {}

// Macro to implement for tuples of different sizes
macro_rules! impl_tuple {
    ($($idx:tt => $ty:ident),+) => {
        impl<$($ty: SolCall,)+> Sealed for ($($ty,)+) {}

        // Implement pushing a new type onto the tuple
        impl<T: SolCall, $($ty: SolCall,)+> TuplePush<T> for ($($ty,)+) {
            type Pushed = ($($ty,)+ T,);
        }

        // Implement decoding for the tuple
        impl<$($ty: SolCall,)+> CallTuple for ($($ty,)+) {
            // The Returns associated type is a tuple of each SolCall's Return type
            type Returns = ($(Result<$ty::Return, Failure>,)+);

            type SuccessReturns = ($($ty::Return,)+);

            fn decode_returns(data: &[Bytes]) -> Result<Self::SuccessReturns> {
                if data.len() != count!($($ty),+) {
                    return Err(MulticallError::NoReturnData);
                }

                // Decode each return value in order
                Ok(($($ty::abi_decode_returns(&data[$idx], false).map_err(MulticallError::DecodeError)?,)+))
            }

            fn decode_return_results(results: &[MulticallResult]) -> Result<Self::Returns> {
                if results.len() != count!($($ty),+) {
                    return Err(MulticallError::NoReturnData);
                }

                Ok(($(
                    match &results[$idx].success {
                        true => Ok($ty::abi_decode_returns(&results[$idx].returnData, false).map_err(MulticallError::DecodeError)?),
                        false => Err(Failure { idx: $idx, return_data: results[$idx].returnData.clone() }),
                    },
                )+))
            }

            fn try_into_success(results: Self::Returns) -> Result<Self::SuccessReturns> {
                Ok(($(
                    match results.$idx {
                        Ok(value) => value,
                        Err(failure) => return Err(MulticallError::CallFailed(failure.return_data)),
                    },
                )+))
            }
        }
    };
}

// Helper macro to count number of types
macro_rules! count {
    () => (0);
    ($x:tt $(,$xs:tt)*) => (1 + count!($($xs),*));
}

// Max CALL_LIMIT is 16
impl_tuple!(0 => T1);
impl_tuple!(0 => T1, 1 => T2);
impl_tuple!(0 => T1, 1 => T2, 2 => T3);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11, 11 => T12);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11, 11 => T12, 12 => T13);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11, 11 => T12, 12 => T13, 13 => T14);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11, 11 => T12, 12 => T13, 13 => T14, 14 => T15);
impl_tuple!(0 => T1, 1 => T2, 2 => T3, 3 => T4, 4 => T5, 5 => T6, 6 => T7, 7 => T8, 8 => T9, 9 => T10, 10 => T11, 11 => T12, 12 => T13, 13 => T14, 14 => T15, 15 => T16);