alloy_provider/provider/multicall/
mod.rs

1//! A Multicall Builder
2
3use crate::Provider;
4use alloy_network::{Network, TransactionBuilder};
5use alloy_primitives::{address, Address, BlockNumber, Bytes, B256, U256};
6use alloy_rpc_types_eth::{state::StateOverride, BlockId};
7use alloy_sol_types::SolCall;
8use bindings::IMulticall3::{
9    blockAndAggregateCall, blockAndAggregateReturn, tryBlockAndAggregateCall,
10    tryBlockAndAggregateReturn, Call, Call3, Call3Value,
11};
12
13/// Multicall bindings
14pub mod bindings;
15use crate::provider::multicall::bindings::IMulticall3::{
16    aggregate3Call, aggregate3ValueCall, aggregateCall, getBasefeeCall, getBlockHashCall,
17    getBlockNumberCall, getChainIdCall, getCurrentBlockCoinbaseCall, getCurrentBlockDifficultyCall,
18    getCurrentBlockGasLimitCall, getCurrentBlockTimestampCall, getEthBalanceCall,
19    getLastBlockHashCall, tryAggregateCall,
20};
21
22mod inner_types;
23pub use inner_types::{
24    CallInfoTrait, CallItem, CallItemBuilder, Dynamic, Failure, MulticallError, MulticallItem,
25    Result,
26};
27
28mod tuple;
29use tuple::TuplePush;
30pub use tuple::{CallTuple, Empty};
31
32/// Default address for the Multicall3 contract on most chains. See: <https://github.com/mds1/multicall>
33pub const MULTICALL3_ADDRESS: Address = address!("0xcA11bde05977b3631167028862bE2a173976CA11");
34
35/// A Multicall3 builder
36///
37/// This builder implements a simple API interface to build and execute multicalls using the
38/// [`IMultiCall3`](crate::bindings::IMulticall3) contract which is available on 270+
39/// chains.
40///
41/// ## Example
42///
43/// ```ignore
44/// use alloy_primitives::address;
45/// use alloy_provider::{MulticallBuilder, Provider, ProviderBuilder};
46/// use alloy_sol_types::sol;
47///
48/// sol! {
49///    #[sol(rpc)]
50///    #[derive(Debug, PartialEq)]
51///    interface ERC20 {
52///        function totalSupply() external view returns (uint256 totalSupply);
53///        function balanceOf(address owner) external view returns (uint256 balance);
54///    }
55/// }
56///
57/// #[tokio::main]
58/// async fn main() {
59///     let weth = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
60///     let provider = ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
61///     let erc20 = ERC20::new(weth, &provider);
62///
63///     let ts_call = erc20.totalSupply();
64///     let balance_call = erc20.balanceOf(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));
65///
66///     let multicall = provider.multicall().add(ts_call).add(balance_call);
67///
68///     let (total_supply, balance) = multicall.aggregate().await.unwrap();
69///
70///     println!("Total Supply: {:?}, Balance: {:?}", total_supply, balance);
71/// }
72/// ```
73#[derive(Debug)]
74pub struct MulticallBuilder<T: CallTuple, P: Provider<N>, N: Network> {
75    /// Batched calls
76    calls: Vec<Call3Value>,
77    /// The provider to use
78    provider: P,
79    /// The [`BlockId`] to use for the call
80    block: Option<BlockId>,
81    /// The [`StateOverride`] for the call
82    state_override: Option<StateOverride>,
83    /// This is the address of the [`IMulticall3`](crate::bindings::IMulticall3)
84    /// contract.
85    ///
86    /// By default it is set to [`MULTICALL3_ADDRESS`].
87    address: Address,
88    _pd: std::marker::PhantomData<(T, N)>,
89}
90
91impl<P, N> MulticallBuilder<Empty, P, N>
92where
93    P: Provider<N>,
94    N: Network,
95{
96    /// Instantiate a new [`MulticallBuilder`]
97    pub fn new(provider: P) -> Self {
98        Self {
99            calls: Vec::new(),
100            provider,
101            _pd: Default::default(),
102            block: None,
103            state_override: None,
104            address: MULTICALL3_ADDRESS,
105        }
106    }
107}
108
109impl<D: SolCall + 'static, P, N> MulticallBuilder<Dynamic<D>, P, N>
110where
111    P: Provider<N>,
112    N: Network,
113{
114    /// Instantiate a new [`MulticallBuilder`] that restricts the calls to a specific call type.
115    ///
116    /// Multicalls made using this builder return a vector of the decoded return values.
117    ///
118    /// An example would be trying to fetch multiple ERC20 balances of an address.
119    ///
120    /// ## Example
121    ///
122    /// ```ignore
123    /// use alloy_primitives::address;
124    /// use alloy_provider::{MulticallBuilder, Provider, ProviderBuilder};
125    /// use alloy_sol_types::sol;
126    ///
127    /// sol! {
128    ///   #[sol(rpc)]
129    ///   #[derive(Debug, PartialEq)]
130    ///   interface ERC20 {
131    ///     function balanceOf(address owner) external view returns (uint256 balance);
132    ///   }
133    /// }
134    ///
135    /// #[tokio::main]
136    /// async fn main() {
137    ///    let weth = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
138    ///    let usdc = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
139    ///     
140    ///    let provider = ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
141    ///    let weth = ERC20::new(weth, &provider);
142    ///    let usdc = ERC20::new(usdc, &provider);
143    ///
144    ///    let owner = address!("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
145    ///
146    ///    let mut erc20_balances = MulticallBuilder::new_dynamic(provider);
147    ///
148    ///    for token in &[weth, usdc] {
149    ///        erc20_balances = erc20_balances.add_dynamic(token.balanceOf(owner));
150    ///    }
151    ///
152    ///    let balances: Vec<ERC20::balanceOfReturn> = erc20_balances.aggregate().await.unwrap();
153    ///
154    ///    let weth_bal = &balances[0];
155    ///    let usdc_bal = &balances[1];
156    ///    println!("WETH Balance: {:?}, USDC Balance: {:?}", weth_bal, usdc_bal);
157    /// }
158    pub fn new_dynamic(provider: P) -> Self {
159        Self {
160            calls: Vec::new(),
161            provider,
162            block: None,
163            state_override: None,
164            address: MULTICALL3_ADDRESS,
165            _pd: Default::default(),
166        }
167    }
168
169    /// Add a dynamic call to the builder
170    pub fn add_dynamic(mut self, item: impl MulticallItem<Decoder = D>) -> Self {
171        let target = item.target();
172        let input = item.input();
173
174        let call = CallItem::<D>::new(target, input);
175
176        self.calls.push(call.to_call3_value());
177        self
178    }
179
180    /// Add a dynamic [`CallItem`] to the builder
181    pub fn add_call_dynamic(mut self, call: CallItem<D>) -> Self {
182        self.calls.push(call.to_call3_value());
183        self
184    }
185
186    /// Extend the builder with a sequence of calls
187    pub fn extend(
188        mut self,
189        items: impl IntoIterator<Item = impl MulticallItem<Decoder = D>>,
190    ) -> Self {
191        for item in items {
192            self = self.add_dynamic(item);
193        }
194        self
195    }
196
197    /// Extend the builder with a sequence of [`CallItem`]s
198    pub fn extend_calls(mut self, calls: impl IntoIterator<Item = CallItem<D>>) -> Self {
199        for call in calls {
200            self = self.add_call_dynamic(call);
201        }
202        self
203    }
204}
205
206impl<T, P, N> MulticallBuilder<T, &P, N>
207where
208    T: CallTuple,
209    P: Provider<N> + Clone,
210    N: Network,
211{
212    /// Clones the underlying provider and returns a new [`MulticallBuilder`].
213    pub fn with_cloned_provider(&self) -> MulticallBuilder<Empty, P, N> {
214        MulticallBuilder {
215            calls: Vec::new(),
216            provider: self.provider.clone(),
217            block: None,
218            state_override: None,
219            address: MULTICALL3_ADDRESS,
220            _pd: Default::default(),
221        }
222    }
223}
224
225impl<T, P, N> MulticallBuilder<T, P, N>
226where
227    T: CallTuple,
228    P: Provider<N>,
229    N: Network,
230{
231    /// Set the address of the multicall3 contract
232    ///
233    /// Default is [`MULTICALL3_ADDRESS`].
234    pub const fn address(mut self, address: Address) -> Self {
235        self.address = address;
236        self
237    }
238
239    /// Sets the block to be used for the call.
240    pub const fn block(mut self, block: BlockId) -> Self {
241        self.block = Some(block);
242        self
243    }
244
245    /// Set the state overrides for the call.
246    pub fn overrides(mut self, state_override: impl Into<StateOverride>) -> Self {
247        self.state_override = Some(state_override.into());
248        self
249    }
250
251    /// Appends a [`SolCall`] to the stack.
252    #[expect(clippy::should_implement_trait)]
253    pub fn add<Item: MulticallItem>(self, item: Item) -> MulticallBuilder<T::Pushed, P, N>
254    where
255        Item::Decoder: 'static,
256        T: TuplePush<Item::Decoder>,
257        <T as TuplePush<Item::Decoder>>::Pushed: CallTuple,
258    {
259        let target = item.target();
260        let input = item.input();
261
262        let call = CallItem::<Item::Decoder>::new(target, input);
263
264        self.add_call(call)
265    }
266
267    /// Appends a [`CallItem`] to the stack.
268    pub fn add_call<D>(mut self, call: CallItem<D>) -> MulticallBuilder<T::Pushed, P, N>
269    where
270        D: SolCall + 'static,
271        T: TuplePush<D>,
272        <T as TuplePush<D>>::Pushed: CallTuple,
273    {
274        self.calls.push(call.to_call3_value());
275        MulticallBuilder {
276            calls: self.calls,
277            provider: self.provider,
278            block: self.block,
279            state_override: self.state_override,
280            address: self.address,
281            _pd: Default::default(),
282        }
283    }
284
285    /// Calls the `aggregate` function
286    ///
287    /// Requires that all calls succeed, else reverts.
288    ///
289    /// ## Solidity Function Signature
290    ///
291    /// ```ignore
292    /// sol! {
293    ///     function aggregate(Call[] memory calls) external returns (uint256 blockNumber, bytes[] memory returnData);
294    /// }
295    /// ```
296    ///
297    /// ## Returns
298    ///
299    /// - `returnData`: A tuple of the decoded return values for the calls
300    ///
301    /// One can obtain the block context such as block number and block hash by using the
302    /// [MulticallBuilder::block_and_aggregate] function.
303    ///
304    /// ## Example
305    ///
306    /// ```ignore
307    /// use alloy_primitives::address;
308    /// use alloy_provider::{MulticallBuilder, Provider, ProviderBuilder};
309    /// use alloy_sol_types::sol;
310    ///
311    /// sol! {
312    ///    #[sol(rpc)]
313    ///    #[derive(Debug, PartialEq)]
314    ///    interface ERC20 {
315    ///        function totalSupply() external view returns (uint256 totalSupply);
316    ///        function balanceOf(address owner) external view returns (uint256 balance);
317    ///    }
318    /// }
319    ///
320    /// #[tokio::main]
321    /// async fn main() {
322    ///     let weth = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
323    ///     let provider = ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
324    ///     let erc20 = ERC20::new(weth, &provider);
325    ///
326    ///     let ts_call = erc20.totalSupply();
327    ///     let balance_call = erc20.balanceOf(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));
328    ///
329    ///     let multicall = provider.multicall().add(ts_call).add(balance_call);
330    ///
331    ///     let (total_supply, balance) = multicall.aggregate().await.unwrap();
332    ///
333    ///     println!("Total Supply: {:?}, Balance: {:?}", total_supply, balance);
334    /// }
335    /// ```
336    pub async fn aggregate(&self) -> Result<T::SuccessReturns> {
337        let calls = self
338            .calls
339            .iter()
340            .map(|c| Call { target: c.target, callData: c.callData.clone() })
341            .collect::<Vec<_>>();
342        let call = aggregateCall { calls: calls.to_vec() };
343        let output = self.build_and_call(call, None).await?;
344        T::decode_returns(&output.returnData)
345    }
346
347    /// Call the `tryAggregate` function
348    ///
349    /// Allows for calls to fail by setting `require_success` to false.
350    ///
351    /// ## Solidity Function Signature
352    ///
353    /// ```ignore
354    /// sol! {
355    ///     function tryAggregate(bool requireSuccess, Call[] calldata calls) external payable returns (Result[] memory returnData);
356    /// }
357    /// ```
358    ///
359    /// ## Returns
360    ///
361    /// - A tuple of the decoded return values for the calls.
362    /// - Each return value is wrapped in a [`Result`] struct.
363    /// - The [`Result::Ok`] variant contains the decoded return value.
364    /// - The [`Result::Err`] variant contains the [`Failure`] struct which holds the
365    ///   index(-position) of the call and the returned data as [`Bytes`].
366    ///
367    /// ## Example
368    ///
369    /// ```ignore
370    /// use alloy_primitives::address;
371    /// use alloy_provider::{MulticallBuilder, Provider, ProviderBuilder};
372    /// use alloy_sol_types::sol;
373    ///
374    /// sol! {
375    ///    #[sol(rpc)]
376    ///    #[derive(Debug, PartialEq)]
377    ///    interface ERC20 {
378    ///        function totalSupply() external view returns (uint256 totalSupply);
379    ///        function balanceOf(address owner) external view returns (uint256 balance);
380    ///    }
381    /// }
382    ///
383    /// #[tokio::main]
384    /// async fn main() {
385    ///     let weth = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
386    ///     let provider = ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
387    ///     let erc20 = ERC20::new(weth, &provider);
388    ///
389    ///     let ts_call = erc20.totalSupply();
390    ///     let balance_call = erc20.balanceOf(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));
391    ///
392    ///     let multicall = provider.multicall().add(ts_call).add(balance_call);
393    ///
394    ///     let (total_supply, balance) = multicall.try_aggregate(true).await.unwrap();
395    ///
396    ///     assert!(total_supply.is_ok());
397    ///     assert!(balance.is_ok());
398    /// }
399    /// ```
400    pub async fn try_aggregate(&self, require_success: bool) -> Result<T::Returns> {
401        let calls = &self
402            .calls
403            .iter()
404            .map(|c| Call { target: c.target, callData: c.callData.clone() })
405            .collect::<Vec<_>>();
406        let call = tryAggregateCall { requireSuccess: require_success, calls: calls.to_vec() };
407        let output = self.build_and_call(call, None).await?;
408        T::decode_return_results(&output)
409    }
410
411    /// Call the `aggregate3` function
412    ///
413    /// Doesn't require that all calls succeed, reverts only if a call with `allowFailure` set to
414    /// false, fails.
415    ///
416    /// By default, adding a call via [`MulticallBuilder::add`] sets `allow_failure` to false.
417    ///
418    /// You can add a call that allows failure by using [`MulticallBuilder::add_call`], and setting
419    /// `allow_failure` to true in [`CallItem`].
420    ///
421    /// ## Solidity Function Signature
422    ///
423    /// ```ignore
424    /// sol! {
425    ///     function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData);
426    /// }
427    /// ```
428    ///
429    /// ## Returns
430    ///
431    /// - A tuple of the decoded return values for the calls.
432    /// - Each return value is wrapped in a [`Result`] struct.
433    /// - The [`Result::Ok`] variant contains the decoded return value.
434    /// - The [`Result::Err`] variant contains the [`Failure`] struct which holds the
435    ///   index(-position) of the call and the returned data as [`Bytes`].
436    pub async fn aggregate3(&self) -> Result<T::Returns> {
437        let calls = self
438            .calls
439            .iter()
440            .map(|c| Call3 {
441                target: c.target,
442                callData: c.callData.clone(),
443                allowFailure: c.allowFailure,
444            })
445            .collect::<Vec<_>>();
446        let call = aggregate3Call { calls: calls.to_vec() };
447        let output = self.build_and_call(call, None).await?;
448        T::decode_return_results(&output)
449    }
450
451    /// Call the `aggregate3Value` function
452    ///
453    /// Similar to `aggregate3` allows for calls to fail. Moreover, it allows for calling into
454    /// `payable` functions with the `value` parameter.
455    ///
456    /// One can set the `value` field in the [`CallItem`] struct and use
457    /// [`MulticallBuilder::add_call`] to add it to the stack.
458    ///
459    /// It is important to note the `aggregate3Value` only succeeds when `msg.value` is _strictly_
460    /// equal to the sum of the values of all calls. Summing up the values of all calls and setting
461    /// it in the transaction request is handled internally by the builder.
462    ///
463    /// ## Solidity Function Signature
464    ///
465    /// ```ignore
466    /// sol! {
467    ///    function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData);
468    /// }
469    /// ```
470    ///
471    /// ## Returns
472    ///
473    /// - A tuple of the decoded return values for the calls.
474    /// - Each return value is wrapped in a [`Result`] struct.
475    /// - The [`Result::Ok`] variant contains the decoded return value.
476    /// - The [`Result::Err`] variant contains the [`Failure`] struct which holds the
477    ///   index(-position) of the call and the returned data as [`Bytes`].
478    pub async fn aggregate3_value(&self) -> Result<T::Returns> {
479        let total_value = self.calls.iter().map(|c| c.value).fold(U256::ZERO, |acc, x| acc + x);
480        let call = aggregate3ValueCall { calls: self.calls.to_vec() };
481        let output = self.build_and_call(call, Some(total_value)).await?;
482        T::decode_return_results(&output)
483    }
484
485    /// Call the `blockAndAggregate` function
486    pub async fn block_and_aggregate(&self) -> Result<(u64, B256, T::SuccessReturns)> {
487        let calls = self
488            .calls
489            .iter()
490            .map(|c| Call { target: c.target, callData: c.callData.clone() })
491            .collect::<Vec<_>>();
492        let call = blockAndAggregateCall { calls: calls.to_vec() };
493        let output = self.build_and_call(call, None).await?;
494        let blockAndAggregateReturn { blockNumber, blockHash, returnData } = output;
495        let result = T::decode_return_results(&returnData)?;
496        Ok((blockNumber.to::<u64>(), blockHash, T::try_into_success(result)?))
497    }
498
499    /// Call the `tryBlockAndAggregate` function
500    pub async fn try_block_and_aggregate(
501        &self,
502        require_success: bool,
503    ) -> Result<(u64, B256, T::Returns)> {
504        let calls = self
505            .calls
506            .iter()
507            .map(|c| Call { target: c.target, callData: c.callData.clone() })
508            .collect::<Vec<_>>();
509        let call =
510            tryBlockAndAggregateCall { requireSuccess: require_success, calls: calls.to_vec() };
511        let output = self.build_and_call(call, None).await?;
512        let tryBlockAndAggregateReturn { blockNumber, blockHash, returnData } = output;
513        Ok((blockNumber.to::<u64>(), blockHash, T::decode_return_results(&returnData)?))
514    }
515
516    /// Helper fn to build a tx and call the multicall contract
517    ///
518    /// ## Params
519    ///
520    /// - `call_type`: The [`SolCall`] being made.
521    /// - `value`: Total value to send with the call in case of `aggregate3Value` request.
522    async fn build_and_call<M: SolCall>(
523        &self,
524        call_type: M,
525        value: Option<U256>,
526    ) -> Result<M::Return> {
527        let call = call_type.abi_encode();
528        let mut tx = N::TransactionRequest::default()
529            .with_to(self.address)
530            .with_input(Bytes::from_iter(call));
531
532        if let Some(value) = value {
533            tx.set_value(value);
534        }
535
536        let mut eth_call = self.provider.root().call(tx);
537
538        if let Some(block) = self.block {
539            eth_call = eth_call.block(block);
540        }
541
542        if let Some(overrides) = self.state_override.clone() {
543            eth_call = eth_call.overrides(overrides);
544        }
545
546        let res = eth_call.await.map_err(MulticallError::TransportError)?;
547        M::abi_decode_returns(&res).map_err(MulticallError::DecodeError)
548    }
549
550    /// Add a call to get the block hash from a block number
551    pub fn get_block_hash(self, number: BlockNumber) -> MulticallBuilder<T::Pushed, P, N>
552    where
553        T: TuplePush<getBlockHashCall>,
554        T::Pushed: CallTuple,
555    {
556        let call = CallItem::<getBlockHashCall>::new(
557            self.address,
558            getBlockHashCall { blockNumber: U256::from(number) }.abi_encode().into(),
559        );
560        self.add_call(call)
561    }
562
563    /// Add a call to get the coinbase of the current block
564    pub fn get_current_block_coinbase(self) -> MulticallBuilder<T::Pushed, P, N>
565    where
566        T: TuplePush<getCurrentBlockCoinbaseCall>,
567        T::Pushed: CallTuple,
568    {
569        let call = CallItem::<getCurrentBlockCoinbaseCall>::new(
570            self.address,
571            getCurrentBlockCoinbaseCall {}.abi_encode().into(),
572        );
573        self.add_call(call)
574    }
575
576    /// Add a call to get the current block number
577    pub fn get_block_number(self) -> MulticallBuilder<T::Pushed, P, N>
578    where
579        T: TuplePush<getBlockNumberCall>,
580        T::Pushed: CallTuple,
581    {
582        let call = CallItem::<getBlockNumberCall>::new(
583            self.address,
584            getBlockNumberCall {}.abi_encode().into(),
585        );
586        self.add_call(call)
587    }
588
589    /// Add a call to get the current block difficulty
590    pub fn get_current_block_difficulty(self) -> MulticallBuilder<T::Pushed, P, N>
591    where
592        T: TuplePush<getCurrentBlockDifficultyCall>,
593        T::Pushed: CallTuple,
594    {
595        let call = CallItem::<getCurrentBlockDifficultyCall>::new(
596            self.address,
597            getCurrentBlockDifficultyCall {}.abi_encode().into(),
598        );
599        self.add_call(call)
600    }
601
602    /// Add a call to get the current block gas limit
603    pub fn get_current_block_gas_limit(self) -> MulticallBuilder<T::Pushed, P, N>
604    where
605        T: TuplePush<getCurrentBlockGasLimitCall>,
606        T::Pushed: CallTuple,
607    {
608        let call = CallItem::<getCurrentBlockGasLimitCall>::new(
609            self.address,
610            getCurrentBlockGasLimitCall {}.abi_encode().into(),
611        );
612        self.add_call(call)
613    }
614
615    /// Add a call to get the current block timestamp
616    pub fn get_current_block_timestamp(self) -> MulticallBuilder<T::Pushed, P, N>
617    where
618        T: TuplePush<getCurrentBlockTimestampCall>,
619        T::Pushed: CallTuple,
620    {
621        let call = CallItem::<getCurrentBlockTimestampCall>::new(
622            self.address,
623            getCurrentBlockTimestampCall {}.abi_encode().into(),
624        );
625        self.add_call(call)
626    }
627
628    /// Add a call to get the chain id
629    pub fn get_chain_id(self) -> MulticallBuilder<T::Pushed, P, N>
630    where
631        T: TuplePush<getChainIdCall>,
632        T::Pushed: CallTuple,
633    {
634        let call =
635            CallItem::<getChainIdCall>::new(self.address, getChainIdCall {}.abi_encode().into());
636        self.add_call(call)
637    }
638
639    /// Add a call to get the base fee
640    pub fn get_base_fee(self) -> MulticallBuilder<T::Pushed, P, N>
641    where
642        T: TuplePush<getBasefeeCall>,
643        T::Pushed: CallTuple,
644    {
645        let call =
646            CallItem::<getBasefeeCall>::new(self.address, getBasefeeCall {}.abi_encode().into());
647        self.add_call(call)
648    }
649
650    /// Add a call to get the eth balance of an address
651    pub fn get_eth_balance(self, address: Address) -> MulticallBuilder<T::Pushed, P, N>
652    where
653        T: TuplePush<getEthBalanceCall>,
654        T::Pushed: CallTuple,
655    {
656        let call = CallItem::<getEthBalanceCall>::new(
657            self.address,
658            getEthBalanceCall { addr: address }.abi_encode().into(),
659        );
660        self.add_call(call)
661    }
662
663    /// Add a call to get the last block hash
664    pub fn get_last_block_hash(self) -> MulticallBuilder<T::Pushed, P, N>
665    where
666        T: TuplePush<getLastBlockHashCall>,
667        T::Pushed: CallTuple,
668    {
669        let call = CallItem::<getLastBlockHashCall>::new(
670            self.address,
671            getLastBlockHashCall {}.abi_encode().into(),
672        );
673        self.add_call(call)
674    }
675
676    /// Returns an [`Empty`] builder
677    ///
678    /// Retains previously set provider, address, block and state_override settings.
679    pub fn clear(self) -> MulticallBuilder<Empty, P, N> {
680        MulticallBuilder {
681            calls: Vec::new(),
682            provider: self.provider,
683            block: self.block,
684            state_override: self.state_override,
685            address: self.address,
686            _pd: Default::default(),
687        }
688    }
689
690    /// Get the number of calls in the builder
691    pub fn len(&self) -> usize {
692        self.calls.len()
693    }
694
695    /// Check if the builder is empty
696    pub fn is_empty(&self) -> bool {
697        self.calls.is_empty()
698    }
699}