alloy_provider/provider/multicall/mod.rs
1//! A Multicall Builder
2
3use crate::{PendingTransactionBuilder, Provider};
4use alloy_network::{Network, TransactionBuilder};
5use alloy_primitives::{address, Address, BlockNumber, Bytes, B256, U256};
6use alloy_rpc_types_eth::{state::StateOverride, BlockId, TransactionInputKind};
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/// # Examples
42///
43/// ```ignore (missing alloy-contract)
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 =
61/// ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
62/// let erc20 = ERC20::new(weth, &provider);
63///
64/// let ts_call = erc20.totalSupply();
65/// let balance_call = erc20.balanceOf(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));
66///
67/// let multicall = provider.multicall().add(ts_call).add(balance_call);
68///
69/// let (total_supply, balance) = multicall.aggregate().await.unwrap();
70/// println!("Total Supply: {total_supply}, Balance: {balance}");
71///
72/// // Or dynamically:
73/// let mut dynamic_multicall = provider.multicall().dynamic();
74/// let addresses = vec![
75/// address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"),
76/// address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96046"),
77/// ];
78/// for &address in &addresses {
79/// dynamic_multicall = dynamic_multicall.add_dynamic(erc20.balanceOf(address));
80/// }
81/// let balances: Vec<_> = dynamic_multicall.aggregate().await.unwrap();
82/// println!("Balances: {:#?}", balances);
83/// }
84/// ```
85#[derive(Debug)]
86pub struct MulticallBuilder<T: CallTuple, P: Provider<N>, N: Network> {
87 /// Batched calls
88 calls: Vec<Call3Value>,
89 /// The provider to use
90 provider: P,
91 /// The [`BlockId`] to use for the call
92 block: Option<BlockId>,
93 /// The [`StateOverride`] for the call
94 state_override: Option<StateOverride>,
95 /// This is the address of the [`IMulticall3`](crate::bindings::IMulticall3)
96 /// contract.
97 ///
98 /// By default it is set to [`MULTICALL3_ADDRESS`].
99 address: Address,
100 /// The input kind supported by this builder
101 input_kind: TransactionInputKind,
102 _pd: std::marker::PhantomData<(T, N)>,
103}
104
105impl<P, N> MulticallBuilder<Empty, P, N>
106where
107 P: Provider<N>,
108 N: Network,
109{
110 /// Instantiate a new [`MulticallBuilder`]
111 pub fn new(provider: P) -> Self {
112 Self {
113 calls: Vec::new(),
114 provider,
115 _pd: Default::default(),
116 block: None,
117 state_override: None,
118 address: MULTICALL3_ADDRESS,
119 input_kind: TransactionInputKind::default(),
120 }
121 }
122
123 /// Converts an empty [`MulticallBuilder`] into a dynamic one
124 pub fn dynamic<D: SolCall + 'static>(self) -> MulticallBuilder<Dynamic<D>, P, N> {
125 MulticallBuilder {
126 calls: self.calls,
127 provider: self.provider,
128 block: self.block,
129 state_override: self.state_override,
130 address: self.address,
131 input_kind: self.input_kind,
132 _pd: Default::default(),
133 }
134 }
135}
136
137impl<D: SolCall + 'static, P, N> MulticallBuilder<Dynamic<D>, P, N>
138where
139 P: Provider<N>,
140 N: Network,
141{
142 /// Instantiate a new [`MulticallBuilder`] that restricts the calls to a specific call type.
143 ///
144 /// Multicalls made using this builder return a vector of the decoded return values.
145 ///
146 /// An example would be trying to fetch multiple ERC20 balances of an address.
147 ///
148 /// This is equivalent to `provider.multicall().dynamic()`.
149 ///
150 /// # Examples
151 ///
152 /// ```ignore (missing alloy-contract)
153 /// use alloy_primitives::address;
154 /// use alloy_provider::{MulticallBuilder, Provider, ProviderBuilder};
155 /// use alloy_sol_types::sol;
156 ///
157 /// sol! {
158 /// #[sol(rpc)]
159 /// #[derive(Debug, PartialEq)]
160 /// interface ERC20 {
161 /// function balanceOf(address owner) external view returns (uint256 balance);
162 /// }
163 /// }
164 ///
165 /// #[tokio::main]
166 /// async fn main() {
167 /// let weth = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
168 /// let usdc = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
169 ///
170 /// let provider = ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
171 /// let weth = ERC20::new(weth, &provider);
172 /// let usdc = ERC20::new(usdc, &provider);
173 ///
174 /// let owner = address!("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
175 ///
176 /// let mut erc20_balances = MulticallBuilder::new_dynamic(provider);
177 /// // Or:
178 /// let mut erc20_balances = provider.multicall().dynamic();
179 ///
180 /// for token in &[weth, usdc] {
181 /// erc20_balances = erc20_balances.add_dynamic(token.balanceOf(owner));
182 /// }
183 ///
184 /// let balances: Vec<ERC20::balanceOfReturn> = erc20_balances.aggregate().await.unwrap();
185 ///
186 /// let weth_bal = &balances[0];
187 /// let usdc_bal = &balances[1];
188 /// println!("WETH Balance: {:?}, USDC Balance: {:?}", weth_bal, usdc_bal);
189 /// }
190 pub fn new_dynamic(provider: P) -> Self {
191 MulticallBuilder::new(provider).dynamic()
192 }
193
194 /// Add a dynamic call to the builder
195 ///
196 /// The call will have `allowFailure` set to `false`. To allow failure, use
197 /// [`Self::add_call_dynamic`], potentially converting a [`MulticallItem`] to a fallible
198 /// [`CallItem`] with [`MulticallItem::into_call`].
199 pub fn add_dynamic(mut self, item: impl MulticallItem<Decoder = D>) -> Self {
200 let call: CallItem<D> = item.into();
201
202 self.calls.push(call.to_call3_value());
203 self
204 }
205
206 /// Add a dynamic [`CallItem`] to the builder
207 pub fn add_call_dynamic(mut self, call: CallItem<D>) -> Self {
208 self.calls.push(call.to_call3_value());
209 self
210 }
211
212 /// Extend the builder with a sequence of calls
213 pub fn extend(
214 mut self,
215 items: impl IntoIterator<Item = impl MulticallItem<Decoder = D>>,
216 ) -> Self {
217 for item in items {
218 self = self.add_dynamic(item);
219 }
220 self
221 }
222
223 /// Extend the builder with a sequence of [`CallItem`]s
224 pub fn extend_calls(mut self, calls: impl IntoIterator<Item = CallItem<D>>) -> Self {
225 for call in calls {
226 self = self.add_call_dynamic(call);
227 }
228 self
229 }
230}
231
232impl<T, P, N> MulticallBuilder<T, &P, N>
233where
234 T: CallTuple,
235 P: Provider<N> + Clone,
236 N: Network,
237{
238 /// Clones the underlying provider and returns a new [`MulticallBuilder`].
239 pub fn with_cloned_provider(&self) -> MulticallBuilder<Empty, P, N> {
240 MulticallBuilder {
241 calls: Vec::new(),
242 provider: self.provider.clone(),
243 block: None,
244 state_override: None,
245 address: MULTICALL3_ADDRESS,
246 input_kind: TransactionInputKind::default(),
247 _pd: Default::default(),
248 }
249 }
250}
251
252impl<T, P, N> MulticallBuilder<T, P, N>
253where
254 T: CallTuple,
255 P: Provider<N>,
256 N: Network,
257{
258 /// Set the address of the multicall3 contract
259 ///
260 /// Default is [`MULTICALL3_ADDRESS`].
261 pub const fn address(mut self, address: Address) -> Self {
262 self.address = address;
263 self
264 }
265
266 /// Sets the block to be used for the call.
267 pub const fn block(mut self, block: BlockId) -> Self {
268 self.block = Some(block);
269 self
270 }
271
272 /// Set the state overrides for the call.
273 pub fn overrides(mut self, state_override: impl Into<StateOverride>) -> Self {
274 self.state_override = Some(state_override.into());
275 self
276 }
277
278 /// Appends a [`SolCall`] to the stack.
279 ///
280 /// The call will have `allowFailure` set to `false`. To allow failure, use [`Self::add_call`],
281 /// potentially converting a [`MulticallItem`] to a fallible [`CallItem`] with
282 /// [`MulticallItem::into_call`].
283 #[expect(clippy::should_implement_trait)]
284 pub fn add<Item: MulticallItem>(self, item: Item) -> MulticallBuilder<T::Pushed, P, N>
285 where
286 Item::Decoder: 'static,
287 T: TuplePush<Item::Decoder>,
288 <T as TuplePush<Item::Decoder>>::Pushed: CallTuple,
289 {
290 let call: CallItem<Item::Decoder> = item.into();
291 self.add_call(call)
292 }
293
294 /// Appends a [`CallItem`] to the stack.
295 pub fn add_call<D>(mut self, call: CallItem<D>) -> MulticallBuilder<T::Pushed, P, N>
296 where
297 D: SolCall + 'static,
298 T: TuplePush<D>,
299 <T as TuplePush<D>>::Pushed: CallTuple,
300 {
301 self.calls.push(call.to_call3_value());
302 MulticallBuilder {
303 calls: self.calls,
304 provider: self.provider,
305 block: self.block,
306 state_override: self.state_override,
307 address: self.address,
308 input_kind: self.input_kind,
309 _pd: Default::default(),
310 }
311 }
312 /// Creates the [`aggregate3ValueCall`]
313 fn to_aggregate3_value_call(&self) -> aggregate3ValueCall {
314 aggregate3ValueCall { calls: self.calls.to_vec() }
315 }
316
317 /// Creates the [`blockAndAggregateCall`]
318 fn to_block_and_aggregate_call(&self) -> blockAndAggregateCall {
319 let calls = self
320 .calls
321 .iter()
322 .map(|c| Call { target: c.target, callData: c.callData.clone() })
323 .collect::<Vec<_>>();
324 blockAndAggregateCall { calls }
325 }
326
327 /// Creates the [`tryBlockAndAggregateCall`]
328 fn to_try_block_and_aggregate_call(&self, require_success: bool) -> tryBlockAndAggregateCall {
329 let calls = self
330 .calls
331 .iter()
332 .map(|c| Call { target: c.target, callData: c.callData.clone() })
333 .collect::<Vec<_>>();
334 tryBlockAndAggregateCall { requireSuccess: require_success, calls }
335 }
336
337 /// Calls the `aggregate` function
338 ///
339 /// Requires that all calls succeed, else reverts.
340 ///
341 /// ## Solidity Function Signature
342 ///
343 /// ```ignore
344 /// sol! {
345 /// function aggregate(Call[] memory calls) external returns (uint256 blockNumber, bytes[] memory returnData);
346 /// }
347 /// ```
348 ///
349 /// ## Returns
350 ///
351 /// - `returnData`: A tuple of the decoded return values for the calls
352 ///
353 /// One can obtain the block context such as block number and block hash by using the
354 /// [MulticallBuilder::block_and_aggregate] function.
355 ///
356 /// # Examples
357 ///
358 /// ```ignore (missing alloy-contract)
359 /// use alloy_primitives::address;
360 /// use alloy_provider::{MulticallBuilder, Provider, ProviderBuilder};
361 /// use alloy_sol_types::sol;
362 ///
363 /// sol! {
364 /// #[sol(rpc)]
365 /// #[derive(Debug, PartialEq)]
366 /// interface ERC20 {
367 /// function totalSupply() external view returns (uint256 totalSupply);
368 /// function balanceOf(address owner) external view returns (uint256 balance);
369 /// }
370 /// }
371 ///
372 /// #[tokio::main]
373 /// async fn main() {
374 /// let weth = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
375 /// let provider = ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
376 /// let erc20 = ERC20::new(weth, &provider);
377 ///
378 /// let ts_call = erc20.totalSupply();
379 /// let balance_call = erc20.balanceOf(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));
380 ///
381 /// let multicall = provider.multicall().add(ts_call).add(balance_call);
382 ///
383 /// let (total_supply, balance) = multicall.aggregate().await.unwrap();
384 ///
385 /// println!("Total Supply: {:?}, Balance: {:?}", total_supply, balance);
386 /// }
387 /// ```
388 pub async fn aggregate(&self) -> Result<T::SuccessReturns> {
389 let output = self.build_and_call(self.to_aggregate_call(), None).await?;
390 T::decode_returns(&output.returnData)
391 }
392
393 /// Sends the `aggregate` function as a transaction.
394 pub async fn send_aggregate(&self) -> Result<PendingTransactionBuilder<N>> {
395 self.build_and_send(self.to_aggregate_call(), None).await
396 }
397
398 /// Encodes the calls for the `aggregate` function and returns the populated transaction
399 /// request.
400 pub fn to_aggregate_request(&self) -> N::TransactionRequest {
401 self.build_request(self.to_aggregate_call(), None)
402 }
403
404 /// Creates the [`aggregate3Call`].
405 fn to_aggregate_call(&self) -> aggregateCall {
406 let calls = self
407 .calls
408 .iter()
409 .map(|c| Call { target: c.target, callData: c.callData.clone() })
410 .collect::<Vec<_>>();
411 aggregateCall { calls: calls.to_vec() }
412 }
413
414 /// Call the `tryAggregate` function
415 ///
416 /// Allows for calls to fail by setting `require_success` to false.
417 ///
418 /// ## Solidity Function Signature
419 ///
420 /// ```ignore
421 /// sol! {
422 /// function tryAggregate(bool requireSuccess, Call[] calldata calls) external payable returns (Result[] memory returnData);
423 /// }
424 /// ```
425 ///
426 /// ## Returns
427 ///
428 /// - A tuple of the decoded return values for the calls.
429 /// - Each return value is wrapped in a [`Result`] struct.
430 /// - The [`Result::Ok`] variant contains the decoded return value.
431 /// - The [`Result::Err`] variant contains the [`Failure`] struct which holds the
432 /// index(-position) of the call and the returned data as [`Bytes`].
433 ///
434 /// # Examples
435 ///
436 /// ```ignore
437 /// use alloy_primitives::address;
438 /// use alloy_provider::{MulticallBuilder, Provider, ProviderBuilder};
439 /// use alloy_sol_types::sol;
440 ///
441 /// sol! {
442 /// #[sol(rpc)]
443 /// #[derive(Debug, PartialEq)]
444 /// interface ERC20 {
445 /// function totalSupply() external view returns (uint256 totalSupply);
446 /// function balanceOf(address owner) external view returns (uint256 balance);
447 /// }
448 /// }
449 ///
450 /// #[tokio::main]
451 /// async fn main() {
452 /// let weth = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
453 /// let provider = ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
454 /// let erc20 = ERC20::new(weth, &provider);
455 ///
456 /// let ts_call = erc20.totalSupply();
457 /// let balance_call = erc20.balanceOf(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));
458 ///
459 /// let multicall = provider.multicall().add(ts_call).add(balance_call);
460 ///
461 /// let (total_supply, balance) = multicall.try_aggregate(true).await.unwrap();
462 ///
463 /// assert!(total_supply.is_ok());
464 /// assert!(balance.is_ok());
465 /// }
466 /// ```
467 pub async fn try_aggregate(&self, require_success: bool) -> Result<T::Returns> {
468 let output = self.build_and_call(self.to_try_aggregate_call(require_success), None).await?;
469 T::decode_return_results(&output)
470 }
471 /// Sends the `tryAggregate` function as a transaction
472 pub async fn send_try_aggregate(
473 &self,
474 require_success: bool,
475 ) -> Result<PendingTransactionBuilder<N>> {
476 self.build_and_send(self.to_try_aggregate_call(require_success), None).await
477 }
478
479 /// Encodes the calls for the `tryAggregateCall` function and returns the populated transaction
480 /// request.
481 pub fn to_try_aggregate_request(&self, require_success: bool) -> N::TransactionRequest {
482 self.build_request(self.to_try_aggregate_call(require_success), None)
483 }
484
485 /// Creates the [`tryAggregateCall`].
486 fn to_try_aggregate_call(&self, require_success: bool) -> tryAggregateCall {
487 let calls = &self
488 .calls
489 .iter()
490 .map(|c| Call { target: c.target, callData: c.callData.clone() })
491 .collect::<Vec<_>>();
492 tryAggregateCall { requireSuccess: require_success, calls: calls.to_vec() }
493 }
494
495 /// Call the `aggregate3` function
496 ///
497 /// Doesn't require that all calls succeed, reverts only if a call with `allowFailure` set to
498 /// false, fails.
499 ///
500 /// By default, adding a call via [`MulticallBuilder::add`] sets `allow_failure` to false.
501 ///
502 /// You can add a call that allows failure by using [`MulticallBuilder::add_call`], and setting
503 /// `allow_failure` to true in [`CallItem`].
504 ///
505 /// ## Solidity Function Signature
506 ///
507 /// ```ignore
508 /// sol! {
509 /// function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData);
510 /// }
511 /// ```
512 ///
513 /// ## Returns
514 ///
515 /// - A tuple of the decoded return values for the calls.
516 /// - Each return value is wrapped in a [`Result`] struct.
517 /// - The [`Result::Ok`] variant contains the decoded return value.
518 /// - The [`Result::Err`] variant contains the [`Failure`] struct which holds the
519 /// index(-position) of the call and the returned data as [`Bytes`].
520 pub async fn aggregate3(&self) -> Result<T::Returns> {
521 let call = self.to_aggregate3_call();
522 let output = self.build_and_call(call, None).await?;
523 T::decode_return_results(&output)
524 }
525
526 /// Sends the `aggregate3` function as a transaction
527 pub async fn send_aggregate3(&self) -> Result<PendingTransactionBuilder<N>> {
528 self.build_and_send(self.to_aggregate3_call(), None).await
529 }
530
531 /// Encodes the calls for the `aggregate3` function and returns the populated transaction
532 /// request.
533 pub fn to_aggregate3_request(&self) -> N::TransactionRequest {
534 self.build_request(self.to_aggregate3_call(), None)
535 }
536
537 /// Sends the `aggregate3Value` function as a transaction
538 pub async fn send_aggregate3_value(&self) -> Result<PendingTransactionBuilder<N>> {
539 let total_value = self.calls.iter().map(|c| c.value).fold(U256::ZERO, |acc, x| acc + x);
540 let call = self.to_aggregate3_value_call();
541 self.build_and_send(call, Some(total_value)).await
542 }
543
544 /// Creates the [`aggregate3Call`]
545 fn to_aggregate3_call(&self) -> aggregate3Call {
546 let calls = self
547 .calls
548 .iter()
549 .map(|c| Call3 {
550 target: c.target,
551 callData: c.callData.clone(),
552 allowFailure: c.allowFailure,
553 })
554 .collect::<Vec<_>>();
555 aggregate3Call { calls: calls.to_vec() }
556 }
557
558 /// Call the `aggregate3Value` function
559 ///
560 /// Similar to `aggregate3` allows for calls to fail. Moreover, it allows for calling into
561 /// `payable` functions with the `value` parameter.
562 ///
563 /// One can set the `value` field in the [`CallItem`] struct and use
564 /// [`MulticallBuilder::add_call`] to add it to the stack.
565 ///
566 /// It is important to note the `aggregate3Value` only succeeds when `msg.value` is _strictly_
567 /// equal to the sum of the values of all calls. Summing up the values of all calls and setting
568 /// it in the transaction request is handled internally by the builder.
569 ///
570 /// ## Solidity Function Signature
571 ///
572 /// ```ignore
573 /// sol! {
574 /// function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData);
575 /// }
576 /// ```
577 ///
578 /// ## Returns
579 ///
580 /// - A tuple of the decoded return values for the calls.
581 /// - Each return value is wrapped in a [`Result`] struct.
582 /// - The [`Result::Ok`] variant contains the decoded return value.
583 /// - The [`Result::Err`] variant contains the [`Failure`] struct which holds the
584 /// index(-position) of the call and the returned data as [`Bytes`].
585 pub async fn aggregate3_value(&self) -> Result<T::Returns> {
586 let total_value = self.calls.iter().map(|c| c.value).fold(U256::ZERO, |acc, x| acc + x);
587 let call = aggregate3ValueCall { calls: self.calls.to_vec() };
588 let output = self.build_and_call(call, Some(total_value)).await?;
589 T::decode_return_results(&output)
590 }
591
592 /// Call the `blockAndAggregate` function
593 pub async fn block_and_aggregate(&self) -> Result<(u64, B256, T::SuccessReturns)> {
594 let calls = self
595 .calls
596 .iter()
597 .map(|c| Call { target: c.target, callData: c.callData.clone() })
598 .collect::<Vec<_>>();
599 let call = blockAndAggregateCall { calls: calls.to_vec() };
600 let output = self.build_and_call(call, None).await?;
601 let blockAndAggregateReturn { blockNumber, blockHash, returnData } = output;
602 let result = T::decode_return_results(&returnData)?;
603 Ok((blockNumber.to::<u64>(), blockHash, T::try_into_success(result)?))
604 }
605 /// Sends the `blockAndAggregate` function as a transaction
606 pub async fn send_block_and_aggregate(&self) -> Result<PendingTransactionBuilder<N>> {
607 let call = self.to_block_and_aggregate_call();
608 self.build_and_send(call, None).await
609 }
610
611 /// Call the `tryBlockAndAggregate` function
612 pub async fn try_block_and_aggregate(
613 &self,
614 require_success: bool,
615 ) -> Result<(u64, B256, T::Returns)> {
616 let calls = self
617 .calls
618 .iter()
619 .map(|c| Call { target: c.target, callData: c.callData.clone() })
620 .collect::<Vec<_>>();
621 let call =
622 tryBlockAndAggregateCall { requireSuccess: require_success, calls: calls.to_vec() };
623 let output = self.build_and_call(call, None).await?;
624 let tryBlockAndAggregateReturn { blockNumber, blockHash, returnData } = output;
625 Ok((blockNumber.to::<u64>(), blockHash, T::decode_return_results(&returnData)?))
626 }
627
628 /// Sends the `tryBlockAndAggregate` function as a transaction
629 pub async fn send_try_block_and_aggregate(
630 &self,
631 require_success: bool,
632 ) -> Result<PendingTransactionBuilder<N>> {
633 let call = self.to_try_block_and_aggregate_call(require_success);
634 self.build_and_send(call, None).await
635 }
636
637 /// Helper for building the transaction request for the given call type input.
638 fn build_request<M: SolCall>(
639 &self,
640 call_type: M,
641 value: Option<U256>,
642 ) -> N::TransactionRequest {
643 let call = call_type.abi_encode();
644 let mut tx = N::TransactionRequest::default()
645 .with_to(self.address)
646 .with_input_kind(Bytes::from_iter(call), self.input_kind);
647
648 if let Some(value) = value {
649 tx.set_value(value);
650 }
651 tx
652 }
653
654 /// Helper fn to build a tx and call the multicall contract
655 ///
656 /// ## Params
657 ///
658 /// - `call_type`: The [`SolCall`] being made.
659 /// - `value`: Total value to send with the call in case of `aggregate3Value` request.
660 async fn build_and_call<M: SolCall>(
661 &self,
662 call_type: M,
663 value: Option<U256>,
664 ) -> Result<M::Return> {
665 let tx = self.build_request(call_type, value);
666
667 let mut eth_call = self.provider.root().call(tx);
668
669 if let Some(block) = self.block {
670 eth_call = eth_call.block(block);
671 }
672
673 if let Some(overrides) = self.state_override.clone() {
674 eth_call = eth_call.overrides(overrides);
675 }
676
677 let res = eth_call.await.map_err(MulticallError::TransportError)?;
678 M::abi_decode_returns(&res).map_err(MulticallError::DecodeError)
679 }
680
681 async fn build_and_send<M: SolCall>(
682 &self,
683 call_type: M,
684 value: Option<U256>,
685 ) -> Result<PendingTransactionBuilder<N>> {
686 let tx = self.build_request(call_type, value);
687
688 let pending_tx =
689 self.provider.send_transaction(tx).await.map_err(MulticallError::TransportError)?;
690
691 Ok(pending_tx)
692 }
693
694 /// Add a call to get the block hash from a block number
695 pub fn get_block_hash(self, number: BlockNumber) -> MulticallBuilder<T::Pushed, P, N>
696 where
697 T: TuplePush<getBlockHashCall>,
698 T::Pushed: CallTuple,
699 {
700 let call = CallItem::<getBlockHashCall>::new(
701 self.address,
702 getBlockHashCall { blockNumber: U256::from(number) }.abi_encode().into(),
703 );
704 self.add_call(call)
705 }
706
707 /// Add a call to get the coinbase of the current block
708 pub fn get_current_block_coinbase(self) -> MulticallBuilder<T::Pushed, P, N>
709 where
710 T: TuplePush<getCurrentBlockCoinbaseCall>,
711 T::Pushed: CallTuple,
712 {
713 let call = CallItem::<getCurrentBlockCoinbaseCall>::new(
714 self.address,
715 getCurrentBlockCoinbaseCall {}.abi_encode().into(),
716 );
717 self.add_call(call)
718 }
719
720 /// Add a call to get the current block number
721 pub fn get_block_number(self) -> MulticallBuilder<T::Pushed, P, N>
722 where
723 T: TuplePush<getBlockNumberCall>,
724 T::Pushed: CallTuple,
725 {
726 let call = CallItem::<getBlockNumberCall>::new(
727 self.address,
728 getBlockNumberCall {}.abi_encode().into(),
729 );
730 self.add_call(call)
731 }
732
733 /// Add a call to get the current block difficulty
734 pub fn get_current_block_difficulty(self) -> MulticallBuilder<T::Pushed, P, N>
735 where
736 T: TuplePush<getCurrentBlockDifficultyCall>,
737 T::Pushed: CallTuple,
738 {
739 let call = CallItem::<getCurrentBlockDifficultyCall>::new(
740 self.address,
741 getCurrentBlockDifficultyCall {}.abi_encode().into(),
742 );
743 self.add_call(call)
744 }
745
746 /// Add a call to get the current block gas limit
747 pub fn get_current_block_gas_limit(self) -> MulticallBuilder<T::Pushed, P, N>
748 where
749 T: TuplePush<getCurrentBlockGasLimitCall>,
750 T::Pushed: CallTuple,
751 {
752 let call = CallItem::<getCurrentBlockGasLimitCall>::new(
753 self.address,
754 getCurrentBlockGasLimitCall {}.abi_encode().into(),
755 );
756 self.add_call(call)
757 }
758
759 /// Add a call to get the current block timestamp
760 pub fn get_current_block_timestamp(self) -> MulticallBuilder<T::Pushed, P, N>
761 where
762 T: TuplePush<getCurrentBlockTimestampCall>,
763 T::Pushed: CallTuple,
764 {
765 let call = CallItem::<getCurrentBlockTimestampCall>::new(
766 self.address,
767 getCurrentBlockTimestampCall {}.abi_encode().into(),
768 );
769 self.add_call(call)
770 }
771
772 /// Add a call to get the chain id
773 pub fn get_chain_id(self) -> MulticallBuilder<T::Pushed, P, N>
774 where
775 T: TuplePush<getChainIdCall>,
776 T::Pushed: CallTuple,
777 {
778 let call =
779 CallItem::<getChainIdCall>::new(self.address, getChainIdCall {}.abi_encode().into());
780 self.add_call(call)
781 }
782
783 /// Add a call to get the base fee
784 pub fn get_base_fee(self) -> MulticallBuilder<T::Pushed, P, N>
785 where
786 T: TuplePush<getBasefeeCall>,
787 T::Pushed: CallTuple,
788 {
789 let call =
790 CallItem::<getBasefeeCall>::new(self.address, getBasefeeCall {}.abi_encode().into());
791 self.add_call(call)
792 }
793
794 /// Add a call to get the eth balance of an address
795 pub fn get_eth_balance(self, address: Address) -> MulticallBuilder<T::Pushed, P, N>
796 where
797 T: TuplePush<getEthBalanceCall>,
798 T::Pushed: CallTuple,
799 {
800 let call = CallItem::<getEthBalanceCall>::new(
801 self.address,
802 getEthBalanceCall { addr: address }.abi_encode().into(),
803 );
804 self.add_call(call)
805 }
806
807 /// Add a call to get the last block hash
808 pub fn get_last_block_hash(self) -> MulticallBuilder<T::Pushed, P, N>
809 where
810 T: TuplePush<getLastBlockHashCall>,
811 T::Pushed: CallTuple,
812 {
813 let call = CallItem::<getLastBlockHashCall>::new(
814 self.address,
815 getLastBlockHashCall {}.abi_encode().into(),
816 );
817 self.add_call(call)
818 }
819
820 /// Returns an [`Empty`] builder
821 ///
822 /// Retains previously set provider, address, block and state_override settings.
823 pub fn clear(self) -> MulticallBuilder<Empty, P, N> {
824 MulticallBuilder {
825 calls: Vec::new(),
826 provider: self.provider,
827 block: self.block,
828 state_override: self.state_override,
829 address: self.address,
830 input_kind: self.input_kind,
831 _pd: Default::default(),
832 }
833 }
834
835 /// Get the number of calls in the builder
836 pub fn len(&self) -> usize {
837 self.calls.len()
838 }
839
840 /// Check if the builder is empty
841 pub fn is_empty(&self) -> bool {
842 self.calls.is_empty()
843 }
844
845 /// Set the input kind for this builder
846 pub const fn with_input_kind(mut self, input_kind: TransactionInputKind) -> Self {
847 self.input_kind = input_kind;
848 self
849 }
850
851 /// Get the input kind for this builder
852 pub const fn input_kind(&self) -> TransactionInputKind {
853 self.input_kind
854 }
855}