alloy_contract/
call.rs

1use crate::{CallDecoder, Error, EthCall, Result};
2use alloy_consensus::SignableTransaction;
3use alloy_dyn_abi::{DynSolValue, JsonAbiExt};
4use alloy_json_abi::Function;
5use alloy_network::{
6    eip2718::Encodable2718, Ethereum, IntoWallet, Network, TransactionBuilder,
7    TransactionBuilder4844, TransactionBuilder7594, TransactionBuilder7702,
8    TransactionBuilderError, TxSigner,
9};
10use alloy_network_primitives::ReceiptResponse;
11use alloy_primitives::{Address, Bytes, ChainId, Signature, TxKind, U256};
12use alloy_provider::{PendingTransactionBuilder, Provider};
13use alloy_rpc_types_eth::{
14    state::StateOverride, AccessList, BlobTransactionSidecar, BlobTransactionSidecarEip7594,
15    BlockId, SignedAuthorization,
16};
17use alloy_sol_types::SolCall;
18use std::marker::PhantomData;
19
20// NOTE: The `T` generic here is kept to mitigate breakage with the `sol!` macro.
21// It should always be `()` and has no effect on the implementation.
22
23/// [`CallBuilder`] using a [`SolCall`] type as the call decoder.
24// NOTE: please avoid changing this type due to its use in the `sol!` macro.
25pub type SolCallBuilder<P, C, N = Ethereum> = CallBuilder<P, PhantomData<C>, N>;
26
27/// [`CallBuilder`] using a [`Function`] as the call decoder.
28pub type DynCallBuilder<P, N = Ethereum> = CallBuilder<P, Function, N>;
29
30/// [`CallBuilder`] that does not have a call decoder.
31pub type RawCallBuilder<P, N = Ethereum> = CallBuilder<P, (), N>;
32
33/// A builder for sending a transaction via `eth_sendTransaction`, or calling a contract via
34/// `eth_call`.
35///
36/// The builder can be `.await`ed directly, which is equivalent to invoking [`call`].
37/// Prefer using [`call`] when possible, as `await`ing the builder directly will consume it, and
38/// currently also boxes the future due to type system limitations.
39///
40/// A call builder can currently be instantiated in the following ways:
41/// - by [`sol!`][sol]-generated contract structs' methods (through the `#[sol(rpc)]` attribute)
42///   ([`SolCallBuilder`]);
43/// - by [`ContractInstance`](crate::ContractInstance)'s methods ([`DynCallBuilder`]);
44/// - using [`CallBuilder::new_raw`] ([`RawCallBuilder`]).
45///
46/// Each method represents a different way to decode the output of the contract call.
47///
48/// [`call`]: CallBuilder::call
49///
50/// # Note
51///
52/// This will set [state overrides](https://geth.ethereum.org/docs/rpc/ns-eth#3-object---state-override-set)
53/// for `eth_call`, but this is not supported by all clients.
54///
55/// # Examples
56///
57/// Using [`sol!`][sol]:
58///
59/// ```no_run
60/// # async fn test<P: alloy_provider::Provider>(provider: P) -> Result<(), Box<dyn std::error::Error>> {
61/// use alloy_contract::SolCallBuilder;
62/// use alloy_primitives::{Address, U256};
63/// use alloy_sol_types::sol;
64///
65/// sol! {
66///     #[sol(rpc)] // <-- Important!
67///     contract MyContract {
68///         function doStuff(uint a, bool b) public returns(address c, bytes32 d);
69///     }
70/// }
71///
72/// # stringify!(
73/// let provider = ...;
74/// # );
75/// let address = Address::ZERO;
76/// let contract = MyContract::new(address, &provider);
77///
78/// // Through `contract.<function_name>(args...)`
79/// let a = U256::ZERO;
80/// let b = true;
81/// let builder: SolCallBuilder<_, MyContract::doStuffCall, _> = contract.doStuff(a, b);
82/// let MyContract::doStuffReturn { c: _, d: _ } = builder.call().await?;
83///
84/// // Through `contract.call_builder(&<FunctionCall { args... }>)`:
85/// // (note that this is discouraged because it's inherently less type-safe)
86/// let call = MyContract::doStuffCall { a, b };
87/// let builder: SolCallBuilder<_, MyContract::doStuffCall, _> = contract.call_builder(&call);
88/// let MyContract::doStuffReturn { c: _, d: _ } = builder.call().await?;
89/// # Ok(())
90/// # }
91/// ```
92///
93/// Using [`ContractInstance`](crate::ContractInstance):
94///
95/// ```no_run
96/// # async fn test<P: alloy_provider::Provider>(provider: P, dynamic_abi: alloy_json_abi::JsonAbi) -> Result<(), Box<dyn std::error::Error>> {
97/// use alloy_primitives::{Address, Bytes, U256};
98/// use alloy_dyn_abi::DynSolValue;
99/// use alloy_contract::{CallBuilder, ContractInstance, DynCallBuilder, Interface, RawCallBuilder};
100///
101/// # stringify!(
102/// let dynamic_abi: JsonAbi = ...;
103/// # );
104/// let interface = Interface::new(dynamic_abi);
105///
106/// # stringify!(
107/// let provider = ...;
108/// # );
109/// let address = Address::ZERO;
110/// let contract: ContractInstance<_, _> = interface.connect(address, &provider);
111///
112/// // Build and call the function:
113/// let call_builder: DynCallBuilder<_, _> = contract.function("doStuff", &[U256::ZERO.into(), true.into()])?;
114/// let result: Vec<DynSolValue> = call_builder.call().await?;
115///
116/// // You can also decode the output manually. Get the raw bytes:
117/// let raw_result: Bytes = call_builder.call_raw().await?;
118/// // Or, equivalently:
119/// let raw_builder: RawCallBuilder<_, _> = call_builder.clone().clear_decoder();
120/// let raw_result: Bytes = raw_builder.call().await?;
121/// // Decode the raw bytes:
122/// let decoded_result: Vec<DynSolValue> = call_builder.decode_output(raw_result)?;
123/// # Ok(())
124/// # }
125/// ```
126///
127/// [sol]: alloy_sol_types::sol
128#[derive(Clone)]
129#[must_use = "call builders do nothing unless you `.call`, `.send`, or `.await` them"]
130pub struct CallBuilder<P, D, N: Network = Ethereum> {
131    pub(crate) request: N::TransactionRequest,
132    block: BlockId,
133    state: Option<StateOverride>,
134    /// The provider.
135    // NOTE: This is public due to usage in `sol!`, please avoid changing it.
136    pub provider: P,
137    decoder: D,
138}
139
140impl<P, D, N: Network> CallBuilder<P, D, N> {
141    /// Converts the call builder to the inner transaction request
142    pub fn into_transaction_request(self) -> N::TransactionRequest {
143        self.request
144    }
145
146    /// Builds and returns a RLP-encoded unsigned transaction from the call that can be signed.
147    ///
148    /// # Examples
149    ///
150    /// ```no_run
151    /// # use alloy_provider::ProviderBuilder;
152    /// # use alloy_sol_types::sol;
153    ///
154    /// sol! {
155    ///     #[sol(rpc, bytecode = "0x")]
156    ///    contract Counter {
157    ///        uint128 public counter;
158    ///
159    ///        function increment() external {
160    ///            counter += 1;
161    ///        }
162    ///    }
163    /// }
164    ///
165    /// #[tokio::main]
166    /// async fn main() {
167    ///     let provider = ProviderBuilder::new().connect_anvil_with_wallet();
168    ///
169    ///     let my_contract = Counter::deploy(provider).await.unwrap();
170    ///
171    ///     let call = my_contract.increment();
172    ///
173    ///     let unsigned_raw_tx: Vec<u8> = call.build_unsigned_raw_transaction().unwrap();
174    ///
175    ///     assert!(!unsigned_raw_tx.is_empty())
176    /// }
177    /// ```
178    pub fn build_unsigned_raw_transaction(self) -> Result<Vec<u8>, TransactionBuilderError<N>>
179    where
180        N::UnsignedTx: SignableTransaction<Signature>,
181    {
182        let tx = self.request.build_unsigned().map_err(|e| e.error)?;
183        Ok(tx.encoded_for_signing())
184    }
185
186    /// Build a RLP-encoded signed raw transaction for the call that can be sent to the network
187    /// using [`Provider::send_raw_transaction`].
188    ///
189    /// # Examples
190    ///
191    /// ```no_run
192    /// # use alloy_provider::{ProviderBuilder, Provider};
193    /// # use alloy_sol_types::sol;
194    /// # use alloy_signer_local::PrivateKeySigner;
195    ///
196    /// sol! {
197    ///    #[sol(rpc, bytecode = "0x")]
198    ///   contract Counter {
199    ///      uint128 public counter;
200    ///
201    ///     function increment() external {
202    ///        counter += 1;
203    ///    }
204    ///  }
205    /// }
206    ///
207    /// #[tokio::main]
208    /// async fn main() {
209    ///     let provider = ProviderBuilder::new().connect_anvil_with_wallet();
210    ///
211    ///     let my_contract = Counter::deploy(&provider).await.unwrap();
212    ///
213    ///     let call = my_contract.increment();
214    ///
215    ///     let pk_signer: PrivateKeySigner = "0x..".parse().unwrap();
216    ///     let signed_raw_tx: Vec<u8> = call.build_raw_transaction(pk_signer).await.unwrap();
217    ///
218    ///     let tx = provider.send_raw_transaction(&signed_raw_tx).await.unwrap();
219    /// }
220    /// ```
221    pub async fn build_raw_transaction<S>(
222        self,
223        signer: S,
224    ) -> Result<Vec<u8>, TransactionBuilderError<N>>
225    where
226        S: TxSigner<Signature> + IntoWallet<N>,
227    {
228        let tx = self.request.build(&signer.into_wallet()).await?;
229        Ok(tx.encoded_2718())
230    }
231}
232
233impl<P, D, N: Network> AsRef<N::TransactionRequest> for CallBuilder<P, D, N> {
234    fn as_ref(&self) -> &N::TransactionRequest {
235        &self.request
236    }
237}
238
239// See [`ContractInstance`].
240impl<P: Provider<N>, N: Network> DynCallBuilder<P, N> {
241    pub(crate) fn new_dyn(
242        provider: P,
243        address: &Address,
244        function: &Function,
245        args: &[DynSolValue],
246    ) -> Result<Self> {
247        Ok(Self::new_inner_call(
248            provider,
249            function.abi_encode_input(args)?.into(),
250            function.clone(),
251        )
252        .to(*address))
253    }
254}
255
256#[doc(hidden)]
257impl<'a, P: Provider<N>, C: SolCall, N: Network> SolCallBuilder<&'a P, C, N> {
258    // `sol!` macro constructor, see `#[sol(rpc)]`. Not public API.
259    // NOTE: please avoid changing this function due to its use in the `sol!` macro.
260    pub fn new_sol(provider: &'a P, address: &Address, call: &C) -> Self {
261        Self::new_inner_call(provider, call.abi_encode().into(), PhantomData::<C>).to(*address)
262    }
263}
264
265impl<P: Provider<N>, D, N: Network> CallBuilder<P, D, N> {
266    /// Clears the decoder, returning a raw call builder.
267    #[inline]
268    pub fn clear_decoder(self) -> RawCallBuilder<P, N> {
269        RawCallBuilder {
270            request: self.request,
271            block: self.block,
272            state: self.state,
273            provider: self.provider,
274            decoder: (),
275        }
276    }
277}
278
279impl<P: Provider<N>, N: Network> RawCallBuilder<P, N> {
280    /// Sets the decoder to the provided [`SolCall`].
281    ///
282    /// Converts the raw call builder into a sol call builder.
283    ///
284    /// Note that generally you would want to instantiate a sol call builder directly using the
285    /// `sol!` macro, but this method is provided for flexibility, for example to convert a raw
286    /// deploy call builder into a sol call builder.
287    ///
288    /// # Examples
289    ///
290    /// Decode a return value from a constructor:
291    ///
292    /// ```no_run
293    /// # use alloy_sol_types::sol;
294    /// sol! {
295    ///     // NOTE: This contract is not meant to be deployed on-chain, but rather
296    ///     // used in a static call with its creation code as the call data.
297    ///     #[sol(rpc, bytecode = "34601457602a60e052600161010052604060e0f35b5f80fdfe")]
298    ///     contract MyContract {
299    ///         // The type returned by the constructor.
300    ///         #[derive(Debug, PartialEq)]
301    ///         struct MyStruct {
302    ///             uint64 a;
303    ///             bool b;
304    ///         }
305    ///
306    ///         constructor() {
307    ///             MyStruct memory s = MyStruct(42, true);
308    ///             bytes memory returnData = abi.encode(s);
309    ///             assembly {
310    ///                 return(add(returnData, 0x20), mload(returnData))
311    ///             }
312    ///         }
313    ///
314    ///         // A shim that represents the return value of the constructor.
315    ///         function constructorReturn() external view returns (MyStruct memory s);
316    ///     }
317    /// }
318    ///
319    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
320    /// # stringify!(
321    /// let provider = ...;
322    /// # );
323    /// # let provider = alloy_provider::ProviderBuilder::new().connect_anvil();
324    /// let call_builder = MyContract::deploy_builder(&provider)
325    ///     .with_sol_decoder::<MyContract::constructorReturnCall>();
326    /// let result = call_builder.call().await?;
327    /// assert_eq!(result, MyContract::MyStruct { a: 42, b: true });
328    /// # Ok(())
329    /// # }
330    /// ```
331    #[inline]
332    pub fn with_sol_decoder<C: SolCall>(self) -> SolCallBuilder<P, C, N> {
333        SolCallBuilder {
334            request: self.request,
335            block: self.block,
336            state: self.state,
337            provider: self.provider,
338            decoder: PhantomData::<C>,
339        }
340    }
341}
342
343impl<P: Provider<N>, N: Network> RawCallBuilder<P, N> {
344    /// Creates a new call builder with the provided provider and ABI encoded input.
345    ///
346    /// Will not decode the output of the call, meaning that [`call`](Self::call) will behave the
347    /// same as [`call_raw`](Self::call_raw).
348    #[inline]
349    pub fn new_raw(provider: P, input: Bytes) -> Self {
350        Self::new_inner_call(provider, input, ())
351    }
352
353    /// Creates a new call builder with the provided provider and contract deploy code.
354    ///
355    /// Will not decode the output of the call, meaning that [`call`](Self::call) will behave the
356    /// same as [`call_raw`](Self::call_raw).
357    // NOTE: please avoid changing this function due to its use in the `sol!` macro.
358    pub fn new_raw_deploy(provider: P, input: Bytes) -> Self {
359        Self::new_inner_deploy(provider, input, ())
360    }
361}
362
363impl<P: Provider<N>, D: CallDecoder, N: Network> CallBuilder<P, D, N> {
364    fn new_inner_deploy(provider: P, input: Bytes, decoder: D) -> Self {
365        Self {
366            request: <N::TransactionRequest>::default().with_deploy_code(input),
367            decoder,
368            provider,
369            block: BlockId::default(),
370            state: None,
371        }
372    }
373
374    fn new_inner_call(provider: P, input: Bytes, decoder: D) -> Self {
375        Self {
376            request: <N::TransactionRequest>::default().with_input(input),
377            decoder,
378            provider,
379            block: BlockId::default(),
380            state: None,
381        }
382    }
383
384    /// Sets the `chain_id` field in the transaction to the provided value
385    pub fn chain_id(mut self, chain_id: ChainId) -> Self {
386        self.request.set_chain_id(chain_id);
387        self
388    }
389
390    /// Sets the `from` field in the transaction to the provided value.
391    pub fn from(mut self, from: Address) -> Self {
392        self.request.set_from(from);
393        self
394    }
395
396    /// Sets the transaction request to the provided tx kind.
397    pub fn kind(mut self, to: TxKind) -> Self {
398        self.request.set_kind(to);
399        self
400    }
401
402    /// Sets the `to` field in the transaction to the provided address.
403    pub fn to(mut self, to: Address) -> Self {
404        self.request.set_to(to);
405        self
406    }
407
408    /// Sets the `sidecar` field in the transaction to the provided value.
409    pub fn sidecar(mut self, blob_sidecar: BlobTransactionSidecar) -> Self
410    where
411        N::TransactionRequest: TransactionBuilder4844,
412    {
413        self.request.set_blob_sidecar(blob_sidecar);
414        self
415    }
416
417    /// Sets the EIP-7594 `sidecar` field in the transaction to the provided value.
418    pub fn sidecar_7594(mut self, sidecar: BlobTransactionSidecarEip7594) -> Self
419    where
420        N::TransactionRequest: TransactionBuilder7594,
421    {
422        self.request.set_blob_sidecar_7594(sidecar);
423        self
424    }
425
426    /// Sets the `gas` field in the transaction to the provided value
427    pub fn gas(mut self, gas: u64) -> Self {
428        self.request.set_gas_limit(gas);
429        self
430    }
431
432    /// Sets the `gas_price` field in the transaction to the provided value
433    /// If the internal transaction is an EIP-1559 one, then it sets both
434    /// `max_fee_per_gas` and `max_priority_fee_per_gas` to the same value
435    pub fn gas_price(mut self, gas_price: u128) -> Self {
436        self.request.set_gas_price(gas_price);
437        self
438    }
439
440    /// Sets the `max_fee_per_gas` in the transaction to the provide value
441    pub fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
442        self.request.set_max_fee_per_gas(max_fee_per_gas);
443        self
444    }
445
446    /// Sets the `max_priority_fee_per_gas` in the transaction to the provide value
447    pub fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
448        self.request.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
449        self
450    }
451
452    /// Sets the `max_fee_per_blob_gas` in the transaction to the provided value
453    pub fn max_fee_per_blob_gas(mut self, max_fee_per_blob_gas: u128) -> Self
454    where
455        N::TransactionRequest: TransactionBuilder4844,
456    {
457        self.request.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
458        self
459    }
460
461    /// Sets the `access_list` in the transaction to the provided value
462    pub fn access_list(mut self, access_list: AccessList) -> Self {
463        self.request.set_access_list(access_list);
464        self
465    }
466
467    /// Sets the `authorization_list` in the transaction to the provided value
468    pub fn authorization_list(mut self, authorization_list: Vec<SignedAuthorization>) -> Self
469    where
470        N::TransactionRequest: TransactionBuilder7702,
471    {
472        self.request.set_authorization_list(authorization_list);
473        self
474    }
475
476    /// Sets the `value` field in the transaction to the provided value
477    pub fn value(mut self, value: U256) -> Self {
478        self.request.set_value(value);
479        self
480    }
481
482    /// Sets the `nonce` field in the transaction to the provided value
483    pub fn nonce(mut self, nonce: u64) -> Self {
484        self.request.set_nonce(nonce);
485        self
486    }
487
488    /// Applies a function to the internal transaction request.
489    pub fn map<F>(mut self, f: F) -> Self
490    where
491        F: FnOnce(N::TransactionRequest) -> N::TransactionRequest,
492    {
493        self.request = f(self.request);
494        self
495    }
496
497    /// Sets the `block` field for sending the tx to the chain
498    pub const fn block(mut self, block: BlockId) -> Self {
499        self.block = block;
500        self
501    }
502
503    /// Sets the [state override set](https://geth.ethereum.org/docs/rpc/ns-eth#3-object---state-override-set).
504    ///
505    /// # Note
506    ///
507    /// Not all client implementations will support this as a parameter to `eth_call`.
508    pub fn state(mut self, state: impl Into<StateOverride>) -> Self {
509        self.state = Some(state.into());
510        self
511    }
512
513    /// Returns the underlying transaction's ABI-encoded data.
514    pub fn calldata(&self) -> &Bytes {
515        self.request.input().expect("set in the constructor")
516    }
517
518    /// Returns the estimated gas cost for the underlying transaction to be executed
519    /// If [`state overrides`](Self::state) are set, they will be applied to the gas estimation.
520    pub async fn estimate_gas(&self) -> Result<u64> {
521        let mut estimate = self.provider.estimate_gas(self.request.clone());
522        if let Some(state) = self.state.clone() {
523            estimate = estimate.overrides(state);
524        }
525        estimate.block(self.block).await.map_err(Into::into)
526    }
527
528    /// Queries the blockchain via an `eth_call` without submitting a transaction to the network.
529    /// If [`state overrides`](Self::state) are set, they will be applied to the call.
530    ///
531    /// Returns the decoded the output by using the provided decoder.
532    /// If this is not desired, use [`call_raw`](Self::call_raw) to get the raw output data.
533    #[doc(alias = "eth_call")]
534    #[doc(alias = "call_with_overrides")]
535    pub fn call(&self) -> EthCall<'_, D, N> {
536        self.call_raw().with_decoder(&self.decoder)
537    }
538
539    /// Queries the blockchain via an `eth_call` without submitting a transaction to the network.
540    /// If [`state overrides`](Self::state) are set, they will be applied to the call.
541    ///
542    /// Does not decode the output of the call, returning the raw output data instead.
543    ///
544    /// See [`call`](Self::call) for more information.
545    pub fn call_raw(&self) -> EthCall<'_, (), N> {
546        let call = self.provider.call(self.request.clone()).block(self.block);
547        let call = match self.state.clone() {
548            Some(state) => call.overrides(state),
549            None => call,
550        };
551        call.into()
552    }
553
554    /// Decodes the output of a contract function using the provided decoder.
555    #[inline]
556    pub fn decode_output(&self, data: Bytes) -> Result<D::CallOutput> {
557        self.decoder.abi_decode_output(data)
558    }
559
560    /// Broadcasts the underlying transaction to the network as a deployment transaction, returning
561    /// the address of the deployed contract after the transaction has been confirmed.
562    ///
563    /// Returns an error if the transaction is not a deployment transaction, or if the contract
564    /// address is not found in the deployment transaction’s receipt.
565    ///
566    /// For more fine-grained control over the deployment process, use [`send`](Self::send) instead.
567    ///
568    /// Note that the deployment address can be pre-calculated if the `from` address and `nonce` are
569    /// known using [`calculate_create_address`](Self::calculate_create_address).
570    pub async fn deploy(&self) -> Result<Address> {
571        if !self.request.kind().is_some_and(|to| to.is_create()) {
572            return Err(Error::NotADeploymentTransaction);
573        }
574        let pending_tx = self.send().await?;
575        let receipt = pending_tx.get_receipt().await?;
576        receipt.contract_address().ok_or(Error::ContractNotDeployed)
577    }
578
579    /// Broadcasts the underlying transaction to the network as a deployment transaction and waits
580    /// for the receipt, returning the address of the deployed contract.
581    ///
582    /// This uses `eth_sendRawTransactionSync` ([EIP-7966](https://eips.ethereum.org/EIPS/eip-7966)),
583    /// which returns the transaction receipt in the same request rather than just the transaction
584    /// hash.
585    ///
586    /// Returns an error if the transaction is not a deployment transaction, or if the contract
587    /// address is not found in the deployment transaction's receipt.
588    ///
589    /// For more fine-grained control over the deployment process, use
590    /// [`deploy`](Self::deploy) instead.
591    ///
592    /// Note that the deployment address can be pre-calculated if the `from` address and `nonce` are
593    /// known using [`calculate_create_address`](Self::calculate_create_address).
594    ///
595    /// # Note
596    ///
597    /// Not all providers and clients support `eth_sendRawTransactionSync` yet.
598    pub async fn deploy_sync(&self) -> Result<Address> {
599        if !self.request.kind().is_some_and(|to| to.is_create()) {
600            return Err(Error::NotADeploymentTransaction);
601        }
602        let receipt = self.send_sync().await?;
603        receipt.contract_address().ok_or(Error::ContractNotDeployed)
604    }
605
606    /// Broadcasts the underlying transaction to the network.
607    ///
608    /// Returns a builder for configuring the pending transaction watcher.
609    /// See [`Provider::send_transaction`] for more information.
610    pub async fn send(&self) -> Result<PendingTransactionBuilder<N>> {
611        Ok(self.provider.send_transaction(self.request.clone()).await?)
612    }
613
614    /// Broadcasts the underlying transaction to the network and waits for the receipt.
615    ///
616    /// This uses `eth_sendRawTransactionSync` ([EIP-7966](https://eips.ethereum.org/EIPS/eip-7966)),
617    /// which returns the transaction receipt in the same request rather than just the transaction
618    /// hash.
619    ///
620    /// Returns the transaction receipt if the transaction was confirmed.
621    ///
622    /// See [`Provider::send_transaction_sync`] for more information.
623    ///
624    /// # Note
625    ///
626    /// Not all providers and clients support `eth_sendRawTransactionSync` yet.
627    pub async fn send_sync(&self) -> Result<N::ReceiptResponse> {
628        Ok(self.provider.send_transaction_sync(self.request.clone()).await?)
629    }
630
631    /// Calculates the address that will be created by the transaction, if any.
632    ///
633    /// Returns `None` if the transaction is not a contract creation (the `to` field is set), or if
634    /// the `from` or `nonce` fields are not set.
635    pub fn calculate_create_address(&self) -> Option<Address> {
636        self.request.calculate_create_address()
637    }
638}
639
640impl<P: Clone, D, N: Network> CallBuilder<&P, D, N> {
641    /// Clones the provider and returns a new builder with the cloned provider.
642    pub fn with_cloned_provider(self) -> CallBuilder<P, D, N> {
643        CallBuilder {
644            request: self.request,
645            block: self.block,
646            state: self.state,
647            provider: self.provider.clone(),
648            decoder: self.decoder,
649        }
650    }
651}
652
653impl<P, D: CallDecoder, N: Network> std::fmt::Debug for CallBuilder<P, D, N> {
654    #[inline]
655    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
656        f.debug_struct("CallBuilder")
657            .field("request", &self.request)
658            .field("block", &self.block)
659            .field("state", &self.state)
660            .field("decoder", &self.decoder.as_debug_field())
661            .finish()
662    }
663}
664
665#[cfg(test)]
666mod tests {
667    use super::*;
668    use alloy_consensus::Transaction;
669    use alloy_network::EthereumWallet;
670    use alloy_node_bindings::Anvil;
671    use alloy_primitives::{address, b256, bytes, hex, utils::parse_units, B256};
672    use alloy_provider::{Provider, ProviderBuilder, WalletProvider};
673    use alloy_rpc_types_eth::{AccessListItem, Authorization};
674    use alloy_signer_local::PrivateKeySigner;
675    use alloy_sol_types::sol;
676    use futures::Future;
677
678    #[test]
679    fn empty_constructor() {
680        sol! {
681            #[sol(rpc, bytecode = "6942")]
682            contract EmptyConstructor {
683                constructor();
684            }
685        }
686
687        let provider = ProviderBuilder::new().connect_anvil();
688        let call_builder = EmptyConstructor::deploy_builder(&provider);
689        assert_eq!(*call_builder.calldata(), bytes!("6942"));
690    }
691
692    sol! {
693        // Solc: 0.8.24+commit.e11b9ed9.Linux.g++
694        // Command: solc a.sol --bin --via-ir --optimize --optimize-runs 1
695        #[sol(rpc, bytecode = "60803461006357601f61014838819003918201601f19168301916001600160401b038311848410176100675780849260209460405283398101031261006357518015158091036100635760ff80195f54169116175f5560405160cc908161007c8239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60808060405260043610156011575f80fd5b5f3560e01c9081638bf1799f14607a575063b09a261614602f575f80fd5b346076576040366003190112607657602435801515810360765715606f57604060015b81516004356001600160a01b0316815260ff919091166020820152f35b60405f6052565b5f80fd5b346076575f36600319011260765760209060ff5f541615158152f3fea264697066735822122043709781c9bdc30c530978abf5db25a4b4ccfebf989baafd2ba404519a7f7e8264736f6c63430008180033")]
696        contract MyContract {
697            bool public myState;
698
699            constructor(bool myState_) {
700                myState = myState_;
701            }
702
703            function doStuff(uint a, bool b) external pure returns(address c, bytes32 d) {
704                return (address(uint160(a)), bytes32(uint256(b ? 1 : 0)));
705            }
706        }
707    }
708
709    sol! {
710        // Solc: 0.8.24+commit.e11b9ed9.Linux.g++
711        // Command: solc counter.sol --bin --via-ir --optimize --optimize-runs 1
712        #[sol(rpc, bytecode = "608080604052346100155760d4908161001a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c90816361bc221a14607e575063d09de08a14602f575f80fd5b34607a575f366003190112607a575f546001600160801b038082166001018181116066576001600160801b03199092169116175f55005b634e487b7160e01b5f52601160045260245ffd5b5f80fd5b34607a575f366003190112607a575f546001600160801b03168152602090f3fea26469706673582212208b360e442c4bb2a4bbdec007ee24588c7a88e0aa52ac39efac748e5e23eff69064736f6c63430008180033")]
713        contract Counter {
714            uint128 public counter;
715
716            function increment() external {
717                counter += 1;
718            }
719        }
720    }
721
722    /// Creates a new call_builder to test field modifications, taken from [call_encoding]
723    fn build_call_builder() -> CallBuilder<impl Provider, PhantomData<MyContract::doStuffCall>> {
724        let provider = ProviderBuilder::new().connect_anvil();
725        let contract = MyContract::new(Address::ZERO, provider);
726        let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
727        call_builder
728    }
729
730    #[test]
731    fn change_chain_id() {
732        let call_builder = build_call_builder().chain_id(1337);
733        assert_eq!(
734            call_builder.request.chain_id.expect("chain_id should be set"),
735            1337,
736            "chain_id of request should be '1337'"
737        );
738    }
739
740    #[test]
741    fn change_max_fee_per_gas() {
742        let call_builder = build_call_builder().max_fee_per_gas(42);
743        assert_eq!(
744            call_builder.request.max_fee_per_gas.expect("max_fee_per_gas should be set"),
745            42,
746            "max_fee_per_gas of request should be '42'"
747        );
748    }
749
750    #[test]
751    fn change_max_priority_fee_per_gas() {
752        let call_builder = build_call_builder().max_priority_fee_per_gas(45);
753        assert_eq!(
754            call_builder
755                .request
756                .max_priority_fee_per_gas
757                .expect("max_priority_fee_per_gas should be set"),
758            45,
759            "max_priority_fee_per_gas of request should be '45'"
760        );
761    }
762
763    #[test]
764    fn change_max_fee_per_blob_gas() {
765        let call_builder = build_call_builder().max_fee_per_blob_gas(50);
766        assert_eq!(
767            call_builder.request.max_fee_per_blob_gas.expect("max_fee_per_blob_gas should be set"),
768            50,
769            "max_fee_per_blob_gas of request should be '50'"
770        );
771    }
772
773    #[test]
774    fn change_authorization_list() {
775        let authorization_list = vec![SignedAuthorization::new_unchecked(
776            Authorization { chain_id: U256::from(1337), address: Address::ZERO, nonce: 0 },
777            0,
778            U256::ZERO,
779            U256::ZERO,
780        )];
781        let call_builder = build_call_builder().authorization_list(authorization_list.clone());
782        assert_eq!(
783            call_builder.request.authorization_list.expect("authorization_list should be set"),
784            authorization_list,
785            "Authorization list of the transaction should have been set to our authorization list"
786        );
787    }
788
789    #[test]
790    fn change_access_list() {
791        let access_list = AccessList::from(vec![AccessListItem {
792            address: Address::ZERO,
793            storage_keys: vec![B256::ZERO],
794        }]);
795        let call_builder = build_call_builder().access_list(access_list.clone());
796        assert_eq!(
797            call_builder.request.access_list.expect("access_list should be set"),
798            access_list,
799            "Access list of the transaction should have been set to our access list"
800        )
801    }
802
803    #[test]
804    fn call_encoding() {
805        let provider = ProviderBuilder::new().connect_anvil();
806        let contract = MyContract::new(Address::ZERO, &&provider).with_cloned_provider();
807        let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
808        assert_eq!(
809            *call_builder.calldata(),
810            bytes!(
811                "b09a2616"
812                "0000000000000000000000000000000000000000000000000000000000000000"
813                "0000000000000000000000000000000000000000000000000000000000000001"
814            ),
815        );
816        // Box the future to assert its concrete output type.
817        let _future: Box<dyn Future<Output = Result<MyContract::doStuffReturn>> + Send> =
818            Box::new(async move { call_builder.call().await });
819    }
820
821    #[test]
822    fn deploy_encoding() {
823        let provider = ProviderBuilder::new().connect_anvil();
824        let bytecode = &MyContract::BYTECODE[..];
825        let call_builder = MyContract::deploy_builder(&provider, false);
826        assert_eq!(
827            call_builder.calldata()[..],
828            [
829                bytecode,
830                &hex!("0000000000000000000000000000000000000000000000000000000000000000")[..]
831            ]
832            .concat(),
833        );
834        let call_builder = MyContract::deploy_builder(&provider, true);
835        assert_eq!(
836            call_builder.calldata()[..],
837            [
838                bytecode,
839                &hex!("0000000000000000000000000000000000000000000000000000000000000001")[..]
840            ]
841            .concat(),
842        );
843    }
844
845    #[tokio::test(flavor = "multi_thread")]
846    async fn deploy_and_call() {
847        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
848
849        let expected_address = provider.default_signer_address().create(0);
850        let my_contract = MyContract::deploy(provider, true).await.unwrap();
851        assert_eq!(*my_contract.address(), expected_address);
852
853        let my_state_builder = my_contract.myState();
854        assert_eq!(my_state_builder.calldata()[..], MyContract::myStateCall {}.abi_encode(),);
855        let my_state = my_state_builder.call().await.unwrap();
856        assert!(my_state);
857
858        let do_stuff_builder = my_contract.doStuff(U256::from(0x69), true);
859        assert_eq!(
860            do_stuff_builder.calldata()[..],
861            MyContract::doStuffCall { a: U256::from(0x69), b: true }.abi_encode(),
862        );
863        let result: MyContract::doStuffReturn = do_stuff_builder.call().await.unwrap();
864        assert_eq!(result.c, address!("0000000000000000000000000000000000000069"));
865        assert_eq!(
866            result.d,
867            b256!("0000000000000000000000000000000000000000000000000000000000000001"),
868        );
869    }
870
871    #[tokio::test(flavor = "multi_thread")]
872    async fn deploy_and_call_with_priority() {
873        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
874        let counter_contract = Counter::deploy(provider.clone()).await.unwrap();
875        let max_fee_per_gas: U256 = parse_units("50", "gwei").unwrap().into();
876        let max_priority_fee_per_gas: U256 = parse_units("0.1", "gwei").unwrap().into();
877        let receipt = counter_contract
878            .increment()
879            .max_fee_per_gas(max_fee_per_gas.to())
880            .max_priority_fee_per_gas(max_priority_fee_per_gas.to())
881            .send()
882            .await
883            .expect("Could not send transaction")
884            .get_receipt()
885            .await
886            .expect("Could not get the receipt");
887        let transaction_hash = receipt.transaction_hash;
888        let transaction = provider
889            .get_transaction_by_hash(transaction_hash)
890            .await
891            .expect("failed to fetch tx")
892            .expect("tx not included");
893        assert_eq!(
894            transaction.max_fee_per_gas(),
895            max_fee_per_gas.to::<u128>(),
896            "max_fee_per_gas of the transaction should be set to the right value"
897        );
898        assert_eq!(
899            transaction
900                .max_priority_fee_per_gas()
901                .expect("max_priority_fee_per_gas of the transaction should be set"),
902            max_priority_fee_per_gas.to::<u128>(),
903            "max_priority_fee_per_gas of the transaction should be set to the right value"
904        )
905    }
906
907    #[tokio::test(flavor = "multi_thread")]
908    async fn deploy_and_call_with_priority_sync() {
909        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
910        let counter_address =
911            Counter::deploy_builder(provider.clone()).deploy_sync().await.unwrap();
912        let counter_contract = Counter::new(counter_address, provider.clone());
913        let max_fee_per_gas: U256 = parse_units("50", "gwei").unwrap().into();
914        let max_priority_fee_per_gas: U256 = parse_units("0.1", "gwei").unwrap().into();
915        let receipt = counter_contract
916            .increment()
917            .max_fee_per_gas(max_fee_per_gas.to())
918            .max_priority_fee_per_gas(max_priority_fee_per_gas.to())
919            .send_sync()
920            .await
921            .expect("Could not send transaction");
922        let transaction_hash = receipt.transaction_hash;
923        let transaction = provider
924            .get_transaction_by_hash(transaction_hash)
925            .await
926            .expect("failed to fetch tx")
927            .expect("tx not included");
928        assert_eq!(
929            transaction.max_fee_per_gas(),
930            max_fee_per_gas.to::<u128>(),
931            "max_fee_per_gas of the transaction should be set to the right value"
932        );
933        assert_eq!(
934            transaction
935                .max_priority_fee_per_gas()
936                .expect("max_priority_fee_per_gas of the transaction should be set"),
937            max_priority_fee_per_gas.to::<u128>(),
938            "max_priority_fee_per_gas of the transaction should be set to the right value"
939        )
940    }
941
942    sol! {
943        #[sol(rpc, bytecode = "6080604052348015600e575f80fd5b506101448061001c5f395ff3fe60806040526004361061001d575f3560e01c8063785d04f514610021575b5f80fd5b61003461002f3660046100d5565b610036565b005b5f816001600160a01b0316836040515f6040518083038185875af1925050503d805f811461007f576040519150601f19603f3d011682016040523d82523d5f602084013e610084565b606091505b50509050806100d05760405162461bcd60e51b81526020600482015260146024820152734661696c656420746f2073656e64206d6f6e657960601b604482015260640160405180910390fd5b505050565b5f80604083850312156100e6575f80fd5b8235915060208301356001600160a01b0381168114610103575f80fd5b80915050925092905056fea2646970667358221220188e65dcedbc4bd68fdebc795292d5a9bf643385f138383969a28f796ff8858664736f6c63430008190033")]
944        contract SendMoney {
945            function send(uint256 amount, address target) external payable {
946                (bool success, ) = target.call{value: amount}("");
947                require(success, "Failed to send money");
948            }
949        }
950    }
951
952    // <https://github.com/alloy-rs/alloy/issues/1942>
953    #[tokio::test]
954    async fn fill_eth_call() {
955        let anvil = Anvil::new().spawn();
956        let pk: PrivateKeySigner =
957            "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
958
959        let wallet = EthereumWallet::new(pk);
960
961        let wallet_provider =
962            ProviderBuilder::new().wallet(wallet).connect_http(anvil.endpoint_url());
963
964        let contract = SendMoney::deploy(wallet_provider.clone()).await.unwrap();
965
966        let tx = contract
967            .send(U256::from(1000000), Address::with_last_byte(1))
968            .into_transaction_request()
969            .value(U256::from(1000000));
970
971        assert!(tx.from.is_none());
972
973        let std_provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
974        let should_fail = std_provider.estimate_gas(tx.clone()).await.is_err();
975
976        assert!(should_fail);
977
978        let gas = wallet_provider.estimate_gas(tx).await.unwrap();
979
980        assert_eq!(gas, 56555);
981    }
982
983    #[test]
984    fn change_sidecar_7594() {
985        use alloy_consensus::Blob;
986
987        let sidecar =
988            BlobTransactionSidecarEip7594::new(vec![Blob::repeat_byte(0xAB)], vec![], vec![]);
989        let call_builder = build_call_builder().sidecar_7594(sidecar.clone());
990
991        let set_sidecar = call_builder
992            .request
993            .sidecar
994            .expect("sidecar should be set")
995            .into_eip7594()
996            .expect("sidecar should be EIP-7594 variant");
997
998        assert_eq!(set_sidecar, sidecar, "EIP-7594 sidecar should match the one we set");
999    }
1000
1001    #[tokio::test]
1002    async fn decode_eth_call_ret_bytes() {
1003        sol! {
1004            #[derive(Debug, PartialEq)]
1005            #[sol(rpc, bytecode = "0x6080604052348015600e575f5ffd5b506101578061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80630d1d2c641461002d575b5f5ffd5b61003561004b565b6040516100429190610108565b60405180910390f35b61005361007b565b6040518060400160405280602a67ffffffffffffffff16815260200160011515815250905090565b60405180604001604052805f67ffffffffffffffff1681526020015f151581525090565b5f67ffffffffffffffff82169050919050565b6100bb8161009f565b82525050565b5f8115159050919050565b6100d5816100c1565b82525050565b604082015f8201516100ef5f8501826100b2565b50602082015161010260208501826100cc565b50505050565b5f60408201905061011b5f8301846100db565b9291505056fea264697066735822122039acc87c027f3bddf6806ff9914411d4245bdc708bca36a07138a37b1b98573464736f6c634300081c0033")]
1006            contract RetStruct {
1007                struct MyStruct {
1008                    uint64 a;
1009                    bool b;
1010                }
1011
1012                function retStruct() external pure returns (MyStruct memory) {
1013                    return MyStruct(42, true);
1014                }
1015            }
1016        }
1017
1018        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1019
1020        let contract = RetStruct::deploy(provider.clone()).await.unwrap();
1021
1022        let tx = contract.retStruct().into_transaction_request();
1023
1024        let result =
1025            provider.call(tx).decode_resp::<RetStruct::retStructCall>().await.unwrap().unwrap();
1026
1027        assert_eq!(result, RetStruct::MyStruct { a: 42, b: true });
1028    }
1029}