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    /// Clears the decoder, returning a raw call builder.
256    #[inline]
257    pub fn clear_decoder(self) -> RawCallBuilder<P, N> {
258        RawCallBuilder {
259            request: self.request,
260            block: self.block,
261            state: self.state,
262            provider: self.provider,
263            decoder: (),
264        }
265    }
266}
267
268#[doc(hidden)]
269impl<'a, P: Provider<N>, C: SolCall, N: Network> SolCallBuilder<&'a P, C, N> {
270    // `sol!` macro constructor, see `#[sol(rpc)]`. Not public API.
271    // NOTE: please avoid changing this function due to its use in the `sol!` macro.
272    pub fn new_sol(provider: &'a P, address: &Address, call: &C) -> Self {
273        Self::new_inner_call(provider, call.abi_encode().into(), PhantomData::<C>).to(*address)
274    }
275}
276
277impl<P: Provider<N>, C: SolCall, N: Network> SolCallBuilder<P, C, N> {
278    /// Clears the decoder, returning a raw call builder.
279    #[inline]
280    pub fn clear_decoder(self) -> RawCallBuilder<P, N> {
281        RawCallBuilder {
282            request: self.request,
283            block: self.block,
284            state: self.state,
285            provider: self.provider,
286            decoder: (),
287        }
288    }
289}
290
291impl<P: Provider<N>, N: Network> RawCallBuilder<P, N> {
292    /// Sets the decoder to the provided [`SolCall`].
293    ///
294    /// Converts the raw call builder into a sol call builder.
295    ///
296    /// Note that generally you would want to instantiate a sol call builder directly using the
297    /// `sol!` macro, but this method is provided for flexibility, for example to convert a raw
298    /// deploy call builder into a sol call builder.
299    ///
300    /// # Examples
301    ///
302    /// Decode a return value from a constructor:
303    ///
304    /// ```no_run
305    /// # use alloy_sol_types::sol;
306    /// sol! {
307    ///     // NOTE: This contract is not meant to be deployed on-chain, but rather
308    ///     // used in a static call with its creation code as the call data.
309    ///     #[sol(rpc, bytecode = "34601457602a60e052600161010052604060e0f35b5f80fdfe")]
310    ///     contract MyContract {
311    ///         // The type returned by the constructor.
312    ///         #[derive(Debug, PartialEq)]
313    ///         struct MyStruct {
314    ///             uint64 a;
315    ///             bool b;
316    ///         }
317    ///
318    ///         constructor() {
319    ///             MyStruct memory s = MyStruct(42, true);
320    ///             bytes memory returnData = abi.encode(s);
321    ///             assembly {
322    ///                 return(add(returnData, 0x20), mload(returnData))
323    ///             }
324    ///         }
325    ///
326    ///         // A shim that represents the return value of the constructor.
327    ///         function constructorReturn() external view returns (MyStruct memory s);
328    ///     }
329    /// }
330    ///
331    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
332    /// # stringify!(
333    /// let provider = ...;
334    /// # );
335    /// # let provider = alloy_provider::ProviderBuilder::new().connect_anvil();
336    /// let call_builder = MyContract::deploy_builder(&provider)
337    ///     .with_sol_decoder::<MyContract::constructorReturnCall>();
338    /// let result = call_builder.call().await?;
339    /// assert_eq!(result, MyContract::MyStruct { a: 42, b: true });
340    /// # Ok(())
341    /// # }
342    /// ```
343    #[inline]
344    pub fn with_sol_decoder<C: SolCall>(self) -> SolCallBuilder<P, C, N> {
345        SolCallBuilder {
346            request: self.request,
347            block: self.block,
348            state: self.state,
349            provider: self.provider,
350            decoder: PhantomData::<C>,
351        }
352    }
353}
354
355impl<P: Provider<N>, N: Network> RawCallBuilder<P, N> {
356    /// Creates a new call builder with the provided provider and ABI encoded input.
357    ///
358    /// Will not decode the output of the call, meaning that [`call`](Self::call) will behave the
359    /// same as [`call_raw`](Self::call_raw).
360    #[inline]
361    pub fn new_raw(provider: P, input: Bytes) -> Self {
362        Self::new_inner_call(provider, input, ())
363    }
364
365    /// Creates a new call builder with the provided provider and contract deploy code.
366    ///
367    /// Will not decode the output of the call, meaning that [`call`](Self::call) will behave the
368    /// same as [`call_raw`](Self::call_raw).
369    // NOTE: please avoid changing this function due to its use in the `sol!` macro.
370    pub fn new_raw_deploy(provider: P, input: Bytes) -> Self {
371        Self::new_inner_deploy(provider, input, ())
372    }
373}
374
375impl<P: Provider<N>, D: CallDecoder, N: Network> CallBuilder<P, D, N> {
376    fn new_inner_deploy(provider: P, input: Bytes, decoder: D) -> Self {
377        Self {
378            request: <N::TransactionRequest>::default().with_deploy_code(input),
379            decoder,
380            provider,
381            block: BlockId::default(),
382            state: None,
383        }
384    }
385
386    fn new_inner_call(provider: P, input: Bytes, decoder: D) -> Self {
387        Self {
388            request: <N::TransactionRequest>::default().with_input(input),
389            decoder,
390            provider,
391            block: BlockId::default(),
392            state: None,
393        }
394    }
395
396    /// Sets the `chain_id` field in the transaction to the provided value
397    pub fn chain_id(mut self, chain_id: ChainId) -> Self {
398        self.request.set_chain_id(chain_id);
399        self
400    }
401
402    /// Sets the `from` field in the transaction to the provided value.
403    pub fn from(mut self, from: Address) -> Self {
404        self.request.set_from(from);
405        self
406    }
407
408    /// Sets the transaction request to the provided tx kind.
409    pub fn kind(mut self, to: TxKind) -> Self {
410        self.request.set_kind(to);
411        self
412    }
413
414    /// Sets the `to` field in the transaction to the provided address.
415    pub fn to(mut self, to: Address) -> Self {
416        self.request.set_to(to);
417        self
418    }
419
420    /// Sets the `sidecar` field in the transaction to the provided value.
421    pub fn sidecar(mut self, blob_sidecar: BlobTransactionSidecar) -> Self
422    where
423        N::TransactionRequest: TransactionBuilder4844,
424    {
425        self.request.set_blob_sidecar(blob_sidecar);
426        self
427    }
428
429    /// Sets the EIP-7594 `sidecar` field in the transaction to the provided value.
430    pub fn sidecar_7594(mut self, sidecar: BlobTransactionSidecarEip7594) -> Self
431    where
432        N::TransactionRequest: TransactionBuilder7594,
433    {
434        self.request.set_blob_sidecar_7594(sidecar);
435        self
436    }
437
438    /// Sets the `gas` field in the transaction to the provided value
439    pub fn gas(mut self, gas: u64) -> Self {
440        self.request.set_gas_limit(gas);
441        self
442    }
443
444    /// Sets the `gas_price` field in the transaction to the provided value
445    /// If the internal transaction is an EIP-1559 one, then it sets both
446    /// `max_fee_per_gas` and `max_priority_fee_per_gas` to the same value
447    pub fn gas_price(mut self, gas_price: u128) -> Self {
448        self.request.set_gas_price(gas_price);
449        self
450    }
451
452    /// Sets the `max_fee_per_gas` in the transaction to the provide value
453    pub fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
454        self.request.set_max_fee_per_gas(max_fee_per_gas);
455        self
456    }
457
458    /// Sets the `max_priority_fee_per_gas` in the transaction to the provide value
459    pub fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
460        self.request.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
461        self
462    }
463
464    /// Sets the `max_fee_per_blob_gas` in the transaction to the provided value
465    pub fn max_fee_per_blob_gas(mut self, max_fee_per_blob_gas: u128) -> Self
466    where
467        N::TransactionRequest: TransactionBuilder4844,
468    {
469        self.request.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
470        self
471    }
472
473    /// Sets the `access_list` in the transaction to the provided value
474    pub fn access_list(mut self, access_list: AccessList) -> Self {
475        self.request.set_access_list(access_list);
476        self
477    }
478
479    /// Sets the `authorization_list` in the transaction to the provided value
480    pub fn authorization_list(mut self, authorization_list: Vec<SignedAuthorization>) -> Self
481    where
482        N::TransactionRequest: TransactionBuilder7702,
483    {
484        self.request.set_authorization_list(authorization_list);
485        self
486    }
487
488    /// Sets the `value` field in the transaction to the provided value
489    pub fn value(mut self, value: U256) -> Self {
490        self.request.set_value(value);
491        self
492    }
493
494    /// Sets the `nonce` field in the transaction to the provided value
495    pub fn nonce(mut self, nonce: u64) -> Self {
496        self.request.set_nonce(nonce);
497        self
498    }
499
500    /// Applies a function to the internal transaction request.
501    pub fn map<F>(mut self, f: F) -> Self
502    where
503        F: FnOnce(N::TransactionRequest) -> N::TransactionRequest,
504    {
505        self.request = f(self.request);
506        self
507    }
508
509    /// Sets the `block` field for sending the tx to the chain
510    pub const fn block(mut self, block: BlockId) -> Self {
511        self.block = block;
512        self
513    }
514
515    /// Sets the [state override set](https://geth.ethereum.org/docs/rpc/ns-eth#3-object---state-override-set).
516    ///
517    /// # Note
518    ///
519    /// Not all client implementations will support this as a parameter to `eth_call`.
520    pub fn state(mut self, state: impl Into<StateOverride>) -> Self {
521        self.state = Some(state.into());
522        self
523    }
524
525    /// Returns the underlying transaction's ABI-encoded data.
526    pub fn calldata(&self) -> &Bytes {
527        self.request.input().expect("set in the constructor")
528    }
529
530    /// Returns the estimated gas cost for the underlying transaction to be executed
531    /// If [`state overrides`](Self::state) are set, they will be applied to the gas estimation.
532    pub async fn estimate_gas(&self) -> Result<u64> {
533        let mut estimate = self.provider.estimate_gas(self.request.clone());
534        if let Some(state) = self.state.clone() {
535            estimate = estimate.overrides(state);
536        }
537        estimate.block(self.block).await.map_err(Into::into)
538    }
539
540    /// Queries the blockchain via an `eth_call` without submitting a transaction to the network.
541    /// If [`state overrides`](Self::state) are set, they will be applied to the call.
542    ///
543    /// Returns the decoded the output by using the provided decoder.
544    /// If this is not desired, use [`call_raw`](Self::call_raw) to get the raw output data.
545    #[doc(alias = "eth_call")]
546    #[doc(alias = "call_with_overrides")]
547    pub fn call(&self) -> EthCall<'_, D, N> {
548        self.call_raw().with_decoder(&self.decoder)
549    }
550
551    /// Queries the blockchain via an `eth_call` without submitting a transaction to the network.
552    /// If [`state overrides`](Self::state) are set, they will be applied to the call.
553    ///
554    /// Does not decode the output of the call, returning the raw output data instead.
555    ///
556    /// See [`call`](Self::call) for more information.
557    pub fn call_raw(&self) -> EthCall<'_, (), N> {
558        let call = self.provider.call(self.request.clone()).block(self.block);
559        let call = match self.state.clone() {
560            Some(state) => call.overrides(state),
561            None => call,
562        };
563        call.into()
564    }
565
566    /// Decodes the output of a contract function using the provided decoder.
567    #[inline]
568    pub fn decode_output(&self, data: Bytes) -> Result<D::CallOutput> {
569        self.decoder.abi_decode_output(data)
570    }
571
572    /// Broadcasts the underlying transaction to the network as a deployment transaction, returning
573    /// the address of the deployed contract after the transaction has been confirmed.
574    ///
575    /// Returns an error if the transaction is not a deployment transaction, or if the contract
576    /// address is not found in the deployment transaction’s receipt.
577    ///
578    /// For more fine-grained control over the deployment process, use [`send`](Self::send) instead.
579    ///
580    /// Note that the deployment address can be pre-calculated if the `from` address and `nonce` are
581    /// known using [`calculate_create_address`](Self::calculate_create_address).
582    pub async fn deploy(&self) -> Result<Address> {
583        if !self.request.kind().is_some_and(|to| to.is_create()) {
584            return Err(Error::NotADeploymentTransaction);
585        }
586        let pending_tx = self.send().await?;
587        let receipt = pending_tx.get_receipt().await?;
588        receipt.contract_address().ok_or(Error::ContractNotDeployed)
589    }
590
591    /// Broadcasts the underlying transaction to the network as a deployment transaction and waits
592    /// for the receipt, returning the address of the deployed contract.
593    ///
594    /// This uses `eth_sendRawTransactionSync` ([EIP-7966](https://eips.ethereum.org/EIPS/eip-7966)),
595    /// which returns the transaction receipt in the same request rather than just the transaction
596    /// hash.
597    ///
598    /// Returns an error if the transaction is not a deployment transaction, or if the contract
599    /// address is not found in the deployment transaction's receipt.
600    ///
601    /// For more fine-grained control over the deployment process, use
602    /// [`deploy`](Self::deploy) instead.
603    ///
604    /// Note that the deployment address can be pre-calculated if the `from` address and `nonce` are
605    /// known using [`calculate_create_address`](Self::calculate_create_address).
606    ///
607    /// # Note
608    ///
609    /// Not all providers and clients support `eth_sendRawTransactionSync` yet.
610    pub async fn deploy_sync(&self) -> Result<Address> {
611        if !self.request.kind().is_some_and(|to| to.is_create()) {
612            return Err(Error::NotADeploymentTransaction);
613        }
614        let receipt = self.send_sync().await?;
615        receipt.contract_address().ok_or(Error::ContractNotDeployed)
616    }
617
618    /// Broadcasts the underlying transaction to the network.
619    ///
620    /// Returns a builder for configuring the pending transaction watcher.
621    /// See [`Provider::send_transaction`] for more information.
622    pub async fn send(&self) -> Result<PendingTransactionBuilder<N>> {
623        Ok(self.provider.send_transaction(self.request.clone()).await?)
624    }
625
626    /// Broadcasts the underlying transaction to the network and waits for the receipt.
627    ///
628    /// This uses `eth_sendRawTransactionSync` ([EIP-7966](https://eips.ethereum.org/EIPS/eip-7966)),
629    /// which returns the transaction receipt in the same request rather than just the transaction
630    /// hash.
631    ///
632    /// Returns the transaction receipt if the transaction was confirmed.
633    ///
634    /// See [`Provider::send_transaction_sync`] for more information.
635    ///
636    /// # Note
637    ///
638    /// Not all providers and clients support `eth_sendRawTransactionSync` yet.
639    pub async fn send_sync(&self) -> Result<N::ReceiptResponse> {
640        Ok(self.provider.send_transaction_sync(self.request.clone()).await?)
641    }
642
643    /// Calculates the address that will be created by the transaction, if any.
644    ///
645    /// Returns `None` if the transaction is not a contract creation (the `to` field is set), or if
646    /// the `from` or `nonce` fields are not set.
647    pub fn calculate_create_address(&self) -> Option<Address> {
648        self.request.calculate_create_address()
649    }
650}
651
652impl<P: Clone, D, N: Network> CallBuilder<&P, D, N> {
653    /// Clones the provider and returns a new builder with the cloned provider.
654    pub fn with_cloned_provider(self) -> CallBuilder<P, D, N> {
655        CallBuilder {
656            request: self.request,
657            block: self.block,
658            state: self.state,
659            provider: self.provider.clone(),
660            decoder: self.decoder,
661        }
662    }
663}
664
665impl<P, D: CallDecoder, N: Network> std::fmt::Debug for CallBuilder<P, D, N> {
666    #[inline]
667    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
668        f.debug_struct("CallBuilder")
669            .field("request", &self.request)
670            .field("block", &self.block)
671            .field("state", &self.state)
672            .field("decoder", &self.decoder.as_debug_field())
673            .finish()
674    }
675}
676
677#[cfg(test)]
678mod tests {
679    use super::*;
680    use alloy_consensus::Transaction;
681    use alloy_network::EthereumWallet;
682    use alloy_node_bindings::Anvil;
683    use alloy_primitives::{address, b256, bytes, hex, utils::parse_units, B256};
684    use alloy_provider::{Provider, ProviderBuilder, WalletProvider};
685    use alloy_rpc_types_eth::{AccessListItem, Authorization};
686    use alloy_signer_local::PrivateKeySigner;
687    use alloy_sol_types::sol;
688    use futures::Future;
689
690    #[test]
691    fn empty_constructor() {
692        sol! {
693            #[sol(rpc, bytecode = "6942")]
694            contract EmptyConstructor {
695                constructor();
696            }
697        }
698
699        let provider = ProviderBuilder::new().connect_anvil();
700        let call_builder = EmptyConstructor::deploy_builder(&provider);
701        assert_eq!(*call_builder.calldata(), bytes!("6942"));
702    }
703
704    sol! {
705        // Solc: 0.8.24+commit.e11b9ed9.Linux.g++
706        // Command: solc a.sol --bin --via-ir --optimize --optimize-runs 1
707        #[sol(rpc, bytecode = "60803461006357601f61014838819003918201601f19168301916001600160401b038311848410176100675780849260209460405283398101031261006357518015158091036100635760ff80195f54169116175f5560405160cc908161007c8239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60808060405260043610156011575f80fd5b5f3560e01c9081638bf1799f14607a575063b09a261614602f575f80fd5b346076576040366003190112607657602435801515810360765715606f57604060015b81516004356001600160a01b0316815260ff919091166020820152f35b60405f6052565b5f80fd5b346076575f36600319011260765760209060ff5f541615158152f3fea264697066735822122043709781c9bdc30c530978abf5db25a4b4ccfebf989baafd2ba404519a7f7e8264736f6c63430008180033")]
708        contract MyContract {
709            bool public myState;
710
711            constructor(bool myState_) {
712                myState = myState_;
713            }
714
715            function doStuff(uint a, bool b) external pure returns(address c, bytes32 d) {
716                return (address(uint160(a)), bytes32(uint256(b ? 1 : 0)));
717            }
718        }
719    }
720
721    sol! {
722        // Solc: 0.8.24+commit.e11b9ed9.Linux.g++
723        // Command: solc counter.sol --bin --via-ir --optimize --optimize-runs 1
724        #[sol(rpc, bytecode = "608080604052346100155760d4908161001a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c90816361bc221a14607e575063d09de08a14602f575f80fd5b34607a575f366003190112607a575f546001600160801b038082166001018181116066576001600160801b03199092169116175f55005b634e487b7160e01b5f52601160045260245ffd5b5f80fd5b34607a575f366003190112607a575f546001600160801b03168152602090f3fea26469706673582212208b360e442c4bb2a4bbdec007ee24588c7a88e0aa52ac39efac748e5e23eff69064736f6c63430008180033")]
725        contract Counter {
726            uint128 public counter;
727
728            function increment() external {
729                counter += 1;
730            }
731        }
732    }
733
734    /// Creates a new call_builder to test field modifications, taken from [call_encoding]
735    fn build_call_builder() -> CallBuilder<impl Provider, PhantomData<MyContract::doStuffCall>> {
736        let provider = ProviderBuilder::new().connect_anvil();
737        let contract = MyContract::new(Address::ZERO, provider);
738        let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
739        call_builder
740    }
741
742    #[test]
743    fn change_chain_id() {
744        let call_builder = build_call_builder().chain_id(1337);
745        assert_eq!(
746            call_builder.request.chain_id.expect("chain_id should be set"),
747            1337,
748            "chain_id of request should be '1337'"
749        );
750    }
751
752    #[test]
753    fn change_max_fee_per_gas() {
754        let call_builder = build_call_builder().max_fee_per_gas(42);
755        assert_eq!(
756            call_builder.request.max_fee_per_gas.expect("max_fee_per_gas should be set"),
757            42,
758            "max_fee_per_gas of request should be '42'"
759        );
760    }
761
762    #[test]
763    fn change_max_priority_fee_per_gas() {
764        let call_builder = build_call_builder().max_priority_fee_per_gas(45);
765        assert_eq!(
766            call_builder
767                .request
768                .max_priority_fee_per_gas
769                .expect("max_priority_fee_per_gas should be set"),
770            45,
771            "max_priority_fee_per_gas of request should be '45'"
772        );
773    }
774
775    #[test]
776    fn change_max_fee_per_blob_gas() {
777        let call_builder = build_call_builder().max_fee_per_blob_gas(50);
778        assert_eq!(
779            call_builder.request.max_fee_per_blob_gas.expect("max_fee_per_blob_gas should be set"),
780            50,
781            "max_fee_per_blob_gas of request should be '50'"
782        );
783    }
784
785    #[test]
786    fn change_authorization_list() {
787        let authorization_list = vec![SignedAuthorization::new_unchecked(
788            Authorization { chain_id: U256::from(1337), address: Address::ZERO, nonce: 0 },
789            0,
790            U256::ZERO,
791            U256::ZERO,
792        )];
793        let call_builder = build_call_builder().authorization_list(authorization_list.clone());
794        assert_eq!(
795            call_builder.request.authorization_list.expect("authorization_list should be set"),
796            authorization_list,
797            "Authorization list of the transaction should have been set to our authorization list"
798        );
799    }
800
801    #[test]
802    fn change_access_list() {
803        let access_list = AccessList::from(vec![AccessListItem {
804            address: Address::ZERO,
805            storage_keys: vec![B256::ZERO],
806        }]);
807        let call_builder = build_call_builder().access_list(access_list.clone());
808        assert_eq!(
809            call_builder.request.access_list.expect("access_list should be set"),
810            access_list,
811            "Access list of the transaction should have been set to our access list"
812        )
813    }
814
815    #[test]
816    fn call_encoding() {
817        let provider = ProviderBuilder::new().connect_anvil();
818        let contract = MyContract::new(Address::ZERO, &&provider).with_cloned_provider();
819        let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
820        assert_eq!(
821            *call_builder.calldata(),
822            bytes!(
823                "b09a2616"
824                "0000000000000000000000000000000000000000000000000000000000000000"
825                "0000000000000000000000000000000000000000000000000000000000000001"
826            ),
827        );
828        // Box the future to assert its concrete output type.
829        let _future: Box<dyn Future<Output = Result<MyContract::doStuffReturn>> + Send> =
830            Box::new(async move { call_builder.call().await });
831    }
832
833    #[test]
834    fn deploy_encoding() {
835        let provider = ProviderBuilder::new().connect_anvil();
836        let bytecode = &MyContract::BYTECODE[..];
837        let call_builder = MyContract::deploy_builder(&provider, false);
838        assert_eq!(
839            call_builder.calldata()[..],
840            [
841                bytecode,
842                &hex!("0000000000000000000000000000000000000000000000000000000000000000")[..]
843            ]
844            .concat(),
845        );
846        let call_builder = MyContract::deploy_builder(&provider, true);
847        assert_eq!(
848            call_builder.calldata()[..],
849            [
850                bytecode,
851                &hex!("0000000000000000000000000000000000000000000000000000000000000001")[..]
852            ]
853            .concat(),
854        );
855    }
856
857    #[tokio::test(flavor = "multi_thread")]
858    async fn deploy_and_call() {
859        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
860
861        let expected_address = provider.default_signer_address().create(0);
862        let my_contract = MyContract::deploy(provider, true).await.unwrap();
863        assert_eq!(*my_contract.address(), expected_address);
864
865        let my_state_builder = my_contract.myState();
866        assert_eq!(my_state_builder.calldata()[..], MyContract::myStateCall {}.abi_encode(),);
867        let my_state = my_state_builder.call().await.unwrap();
868        assert!(my_state);
869
870        let do_stuff_builder = my_contract.doStuff(U256::from(0x69), true);
871        assert_eq!(
872            do_stuff_builder.calldata()[..],
873            MyContract::doStuffCall { a: U256::from(0x69), b: true }.abi_encode(),
874        );
875        let result: MyContract::doStuffReturn = do_stuff_builder.call().await.unwrap();
876        assert_eq!(result.c, address!("0000000000000000000000000000000000000069"));
877        assert_eq!(
878            result.d,
879            b256!("0000000000000000000000000000000000000000000000000000000000000001"),
880        );
881    }
882
883    #[tokio::test(flavor = "multi_thread")]
884    async fn deploy_and_call_with_priority() {
885        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
886        let counter_contract = Counter::deploy(provider.clone()).await.unwrap();
887        let max_fee_per_gas: U256 = parse_units("50", "gwei").unwrap().into();
888        let max_priority_fee_per_gas: U256 = parse_units("0.1", "gwei").unwrap().into();
889        let receipt = counter_contract
890            .increment()
891            .max_fee_per_gas(max_fee_per_gas.to())
892            .max_priority_fee_per_gas(max_priority_fee_per_gas.to())
893            .send()
894            .await
895            .expect("Could not send transaction")
896            .get_receipt()
897            .await
898            .expect("Could not get the receipt");
899        let transaction_hash = receipt.transaction_hash;
900        let transaction = provider
901            .get_transaction_by_hash(transaction_hash)
902            .await
903            .expect("failed to fetch tx")
904            .expect("tx not included");
905        assert_eq!(
906            transaction.max_fee_per_gas(),
907            max_fee_per_gas.to::<u128>(),
908            "max_fee_per_gas of the transaction should be set to the right value"
909        );
910        assert_eq!(
911            transaction
912                .max_priority_fee_per_gas()
913                .expect("max_priority_fee_per_gas of the transaction should be set"),
914            max_priority_fee_per_gas.to::<u128>(),
915            "max_priority_fee_per_gas of the transaction should be set to the right value"
916        )
917    }
918
919    #[tokio::test(flavor = "multi_thread")]
920    async fn deploy_and_call_with_priority_sync() {
921        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
922        let counter_address =
923            Counter::deploy_builder(provider.clone()).deploy_sync().await.unwrap();
924        let counter_contract = Counter::new(counter_address, provider.clone());
925        let max_fee_per_gas: U256 = parse_units("50", "gwei").unwrap().into();
926        let max_priority_fee_per_gas: U256 = parse_units("0.1", "gwei").unwrap().into();
927        let receipt = counter_contract
928            .increment()
929            .max_fee_per_gas(max_fee_per_gas.to())
930            .max_priority_fee_per_gas(max_priority_fee_per_gas.to())
931            .send_sync()
932            .await
933            .expect("Could not send transaction");
934        let transaction_hash = receipt.transaction_hash;
935        let transaction = provider
936            .get_transaction_by_hash(transaction_hash)
937            .await
938            .expect("failed to fetch tx")
939            .expect("tx not included");
940        assert_eq!(
941            transaction.max_fee_per_gas(),
942            max_fee_per_gas.to::<u128>(),
943            "max_fee_per_gas of the transaction should be set to the right value"
944        );
945        assert_eq!(
946            transaction
947                .max_priority_fee_per_gas()
948                .expect("max_priority_fee_per_gas of the transaction should be set"),
949            max_priority_fee_per_gas.to::<u128>(),
950            "max_priority_fee_per_gas of the transaction should be set to the right value"
951        )
952    }
953
954    sol! {
955        #[sol(rpc, bytecode = "6080604052348015600e575f80fd5b506101448061001c5f395ff3fe60806040526004361061001d575f3560e01c8063785d04f514610021575b5f80fd5b61003461002f3660046100d5565b610036565b005b5f816001600160a01b0316836040515f6040518083038185875af1925050503d805f811461007f576040519150601f19603f3d011682016040523d82523d5f602084013e610084565b606091505b50509050806100d05760405162461bcd60e51b81526020600482015260146024820152734661696c656420746f2073656e64206d6f6e657960601b604482015260640160405180910390fd5b505050565b5f80604083850312156100e6575f80fd5b8235915060208301356001600160a01b0381168114610103575f80fd5b80915050925092905056fea2646970667358221220188e65dcedbc4bd68fdebc795292d5a9bf643385f138383969a28f796ff8858664736f6c63430008190033")]
956        contract SendMoney {
957            function send(uint256 amount, address target) external payable {
958                (bool success, ) = target.call{value: amount}("");
959                require(success, "Failed to send money");
960            }
961        }
962    }
963
964    // <https://github.com/alloy-rs/alloy/issues/1942>
965    #[tokio::test]
966    async fn fill_eth_call() {
967        let anvil = Anvil::new().spawn();
968        let pk: PrivateKeySigner =
969            "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
970
971        let wallet = EthereumWallet::new(pk);
972
973        let wallet_provider =
974            ProviderBuilder::new().wallet(wallet).connect_http(anvil.endpoint_url());
975
976        let contract = SendMoney::deploy(wallet_provider.clone()).await.unwrap();
977
978        let tx = contract
979            .send(U256::from(1000000), Address::with_last_byte(1))
980            .into_transaction_request()
981            .value(U256::from(1000000));
982
983        assert!(tx.from.is_none());
984
985        let std_provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
986        let should_fail = std_provider.estimate_gas(tx.clone()).await.is_err();
987
988        assert!(should_fail);
989
990        let gas = wallet_provider.estimate_gas(tx).await.unwrap();
991
992        assert_eq!(gas, 56555);
993    }
994
995    #[test]
996    fn change_sidecar_7594() {
997        use alloy_consensus::Blob;
998
999        let sidecar =
1000            BlobTransactionSidecarEip7594::new(vec![Blob::repeat_byte(0xAB)], vec![], vec![]);
1001        let call_builder = build_call_builder().sidecar_7594(sidecar.clone());
1002
1003        let set_sidecar = call_builder
1004            .request
1005            .sidecar
1006            .expect("sidecar should be set")
1007            .into_eip7594()
1008            .expect("sidecar should be EIP-7594 variant");
1009
1010        assert_eq!(set_sidecar, sidecar, "EIP-7594 sidecar should match the one we set");
1011    }
1012
1013    #[tokio::test]
1014    async fn decode_eth_call_ret_bytes() {
1015        sol! {
1016            #[derive(Debug, PartialEq)]
1017            #[sol(rpc, bytecode = "0x6080604052348015600e575f5ffd5b506101578061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80630d1d2c641461002d575b5f5ffd5b61003561004b565b6040516100429190610108565b60405180910390f35b61005361007b565b6040518060400160405280602a67ffffffffffffffff16815260200160011515815250905090565b60405180604001604052805f67ffffffffffffffff1681526020015f151581525090565b5f67ffffffffffffffff82169050919050565b6100bb8161009f565b82525050565b5f8115159050919050565b6100d5816100c1565b82525050565b604082015f8201516100ef5f8501826100b2565b50602082015161010260208501826100cc565b50505050565b5f60408201905061011b5f8301846100db565b9291505056fea264697066735822122039acc87c027f3bddf6806ff9914411d4245bdc708bca36a07138a37b1b98573464736f6c634300081c0033")]
1018            contract RetStruct {
1019                struct MyStruct {
1020                    uint64 a;
1021                    bool b;
1022                }
1023
1024                function retStruct() external pure returns (MyStruct memory) {
1025                    return MyStruct(42, true);
1026                }
1027            }
1028        }
1029
1030        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1031
1032        let contract = RetStruct::deploy(provider.clone()).await.unwrap();
1033
1034        let tx = contract.retStruct().into_transaction_request();
1035
1036        let result =
1037            provider.call(tx).decode_resp::<RetStruct::retStructCall>().await.unwrap().unwrap();
1038
1039        assert_eq!(result, RetStruct::MyStruct { a: 42, b: true });
1040    }
1041}