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