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