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
18pub type SolCallBuilder<P, C, N = Ethereum> = CallBuilder<P, PhantomData<C>, N>;
24
25pub type DynCallBuilder<P, N = Ethereum> = CallBuilder<P, Function, N>;
27
28pub type RawCallBuilder<P, N = Ethereum> = CallBuilder<P, (), N>;
30
31#[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 pub provider: P,
135 decoder: D,
136}
137
138impl<P, D, N: Network> CallBuilder<P, D, N> {
139 pub fn into_transaction_request(self) -> N::TransactionRequest {
141 self.request
142 }
143
144 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 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
237impl<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 #[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 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 #[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 #[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 #[inline]
359 pub fn new_raw(provider: P, input: Bytes) -> Self {
360 Self::new_inner_call(provider, input, ())
361 }
362
363 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 pub fn chain_id(mut self, chain_id: ChainId) -> Self {
396 self.request.set_chain_id(chain_id);
397 self
398 }
399
400 pub fn from(mut self, from: Address) -> Self {
402 self.request.set_from(from);
403 self
404 }
405
406 pub fn kind(mut self, to: TxKind) -> Self {
408 self.request.set_kind(to);
409 self
410 }
411
412 pub fn to(mut self, to: Address) -> Self {
414 self.request.set_to(to);
415 self
416 }
417
418 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 pub fn gas(mut self, gas: u64) -> Self {
429 self.request.set_gas_limit(gas);
430 self
431 }
432
433 pub fn gas_price(mut self, gas_price: u128) -> Self {
437 self.request.set_gas_price(gas_price);
438 self
439 }
440
441 pub fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
443 self.request.set_max_fee_per_gas(max_fee_per_gas);
444 self
445 }
446
447 pub fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
449 self.request.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
450 self
451 }
452
453 pub fn max_fee_per_blob_gas(mut self, max_fee_per_blob_gas: u128) -> Self
455 where
456 N::TransactionRequest: TransactionBuilder4844,
457 {
458 self.request.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
459 self
460 }
461
462 pub fn access_list(mut self, access_list: AccessList) -> Self {
464 self.request.set_access_list(access_list);
465 self
466 }
467
468 pub fn authorization_list(mut self, authorization_list: Vec<SignedAuthorization>) -> Self
470 where
471 N::TransactionRequest: TransactionBuilder7702,
472 {
473 self.request.set_authorization_list(authorization_list);
474 self
475 }
476
477 pub fn value(mut self, value: U256) -> Self {
479 self.request.set_value(value);
480 self
481 }
482
483 pub fn nonce(mut self, nonce: u64) -> Self {
485 self.request.set_nonce(nonce);
486 self
487 }
488
489 pub fn map<F>(mut self, f: F) -> Self
491 where
492 F: FnOnce(N::TransactionRequest) -> N::TransactionRequest,
493 {
494 self.request = f(self.request);
495 self
496 }
497
498 pub const fn block(mut self, block: BlockId) -> Self {
500 self.block = block;
501 self
502 }
503
504 pub fn state(mut self, state: impl Into<StateOverride>) -> Self {
510 self.state = Some(state.into());
511 self
512 }
513
514 pub fn calldata(&self) -> &Bytes {
516 self.request.input().expect("set in the constructor")
517 }
518
519 pub async fn estimate_gas(&self) -> Result<u64> {
522 let mut estimate = self.provider.estimate_gas(self.request.clone());
523 if let Some(state) = self.state.clone() {
524 estimate = estimate.overrides(state);
525 }
526 estimate.block(self.block).await.map_err(Into::into)
527 }
528
529 #[doc(alias = "eth_call")]
535 #[doc(alias = "call_with_overrides")]
536 pub fn call(&self) -> EthCall<'_, D, N> {
537 self.call_raw().with_decoder(&self.decoder)
538 }
539
540 pub fn call_raw(&self) -> EthCall<'_, (), N> {
547 let call = self.provider.call(self.request.clone()).block(self.block);
548 let call = match self.state.clone() {
549 Some(state) => call.overrides(state),
550 None => call,
551 };
552 call.into()
553 }
554
555 #[inline]
557 pub fn decode_output(&self, data: Bytes) -> Result<D::CallOutput> {
558 self.decoder.abi_decode_output(data)
559 }
560
561 pub async fn deploy(&self) -> Result<Address> {
572 if !self.request.kind().is_some_and(|to| to.is_create()) {
573 return Err(Error::NotADeploymentTransaction);
574 }
575 let pending_tx = self.send().await?;
576 let receipt = pending_tx.get_receipt().await?;
577 receipt.contract_address().ok_or(Error::ContractNotDeployed)
578 }
579
580 pub async fn send(&self) -> Result<PendingTransactionBuilder<N>> {
585 Ok(self.provider.send_transaction(self.request.clone()).await?)
586 }
587
588 pub fn calculate_create_address(&self) -> Option<Address> {
593 self.request.calculate_create_address()
594 }
595}
596
597impl<P: Clone, D, N: Network> CallBuilder<&P, D, N> {
598 pub fn with_cloned_provider(self) -> CallBuilder<P, D, N> {
600 CallBuilder {
601 request: self.request,
602 block: self.block,
603 state: self.state,
604 provider: self.provider.clone(),
605 decoder: self.decoder,
606 }
607 }
608}
609
610impl<P, D: CallDecoder, N: Network> std::fmt::Debug for CallBuilder<P, D, N> {
611 #[inline]
612 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
613 f.debug_struct("CallBuilder")
614 .field("request", &self.request)
615 .field("block", &self.block)
616 .field("state", &self.state)
617 .field("decoder", &self.decoder.as_debug_field())
618 .finish()
619 }
620}
621
622#[cfg(test)]
623mod tests {
624 use super::*;
625 use alloy_consensus::Transaction;
626 use alloy_network::EthereumWallet;
627 use alloy_node_bindings::Anvil;
628 use alloy_primitives::{address, b256, bytes, hex, utils::parse_units, B256};
629 use alloy_provider::{Provider, ProviderBuilder, WalletProvider};
630 use alloy_rpc_types_eth::{AccessListItem, Authorization};
631 use alloy_signer_local::PrivateKeySigner;
632 use alloy_sol_types::sol;
633 use futures::Future;
634
635 #[test]
636 fn empty_constructor() {
637 sol! {
638 #[sol(rpc, bytecode = "6942")]
639 contract EmptyConstructor {
640 constructor();
641 }
642 }
643
644 let provider = ProviderBuilder::new().connect_anvil();
645 let call_builder = EmptyConstructor::deploy_builder(&provider);
646 assert_eq!(*call_builder.calldata(), bytes!("6942"));
647 }
648
649 sol! {
650 #[sol(rpc, bytecode = "60803461006357601f61014838819003918201601f19168301916001600160401b038311848410176100675780849260209460405283398101031261006357518015158091036100635760ff80195f54169116175f5560405160cc908161007c8239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60808060405260043610156011575f80fd5b5f3560e01c9081638bf1799f14607a575063b09a261614602f575f80fd5b346076576040366003190112607657602435801515810360765715606f57604060015b81516004356001600160a01b0316815260ff919091166020820152f35b60405f6052565b5f80fd5b346076575f36600319011260765760209060ff5f541615158152f3fea264697066735822122043709781c9bdc30c530978abf5db25a4b4ccfebf989baafd2ba404519a7f7e8264736f6c63430008180033")]
653 contract MyContract {
654 bool public myState;
655
656 constructor(bool myState_) {
657 myState = myState_;
658 }
659
660 function doStuff(uint a, bool b) external pure returns(address c, bytes32 d) {
661 return (address(uint160(a)), bytes32(uint256(b ? 1 : 0)));
662 }
663 }
664 }
665
666 sol! {
667 #[sol(rpc, bytecode = "608080604052346100155760d4908161001a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c90816361bc221a14607e575063d09de08a14602f575f80fd5b34607a575f366003190112607a575f546001600160801b038082166001018181116066576001600160801b03199092169116175f55005b634e487b7160e01b5f52601160045260245ffd5b5f80fd5b34607a575f366003190112607a575f546001600160801b03168152602090f3fea26469706673582212208b360e442c4bb2a4bbdec007ee24588c7a88e0aa52ac39efac748e5e23eff69064736f6c63430008180033")]
670 contract Counter {
671 uint128 public counter;
672
673 function increment() external {
674 counter += 1;
675 }
676 }
677 }
678
679 fn build_call_builder() -> CallBuilder<impl Provider, PhantomData<MyContract::doStuffCall>> {
681 let provider = ProviderBuilder::new().connect_anvil();
682 let contract = MyContract::new(Address::ZERO, provider);
683 let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
684 call_builder
685 }
686
687 #[test]
688 fn change_chain_id() {
689 let call_builder = build_call_builder().chain_id(1337);
690 assert_eq!(
691 call_builder.request.chain_id.expect("chain_id should be set"),
692 1337,
693 "chain_id of request should be '1337'"
694 );
695 }
696
697 #[test]
698 fn change_max_fee_per_gas() {
699 let call_builder = build_call_builder().max_fee_per_gas(42);
700 assert_eq!(
701 call_builder.request.max_fee_per_gas.expect("max_fee_per_gas should be set"),
702 42,
703 "max_fee_per_gas of request should be '42'"
704 );
705 }
706
707 #[test]
708 fn change_max_priority_fee_per_gas() {
709 let call_builder = build_call_builder().max_priority_fee_per_gas(45);
710 assert_eq!(
711 call_builder
712 .request
713 .max_priority_fee_per_gas
714 .expect("max_priority_fee_per_gas should be set"),
715 45,
716 "max_priority_fee_per_gas of request should be '45'"
717 );
718 }
719
720 #[test]
721 fn change_max_fee_per_blob_gas() {
722 let call_builder = build_call_builder().max_fee_per_blob_gas(50);
723 assert_eq!(
724 call_builder.request.max_fee_per_blob_gas.expect("max_fee_per_blob_gas should be set"),
725 50,
726 "max_fee_per_blob_gas of request should be '50'"
727 );
728 }
729
730 #[test]
731 fn change_authorization_list() {
732 let authorization_list = vec![SignedAuthorization::new_unchecked(
733 Authorization { chain_id: U256::from(1337), address: Address::ZERO, nonce: 0 },
734 0,
735 U256::ZERO,
736 U256::ZERO,
737 )];
738 let call_builder = build_call_builder().authorization_list(authorization_list.clone());
739 assert_eq!(
740 call_builder.request.authorization_list.expect("authorization_list should be set"),
741 authorization_list,
742 "Authorization list of the transaction should have been set to our authorization list"
743 );
744 }
745
746 #[test]
747 fn change_access_list() {
748 let access_list = AccessList::from(vec![AccessListItem {
749 address: Address::ZERO,
750 storage_keys: vec![B256::ZERO],
751 }]);
752 let call_builder = build_call_builder().access_list(access_list.clone());
753 assert_eq!(
754 call_builder.request.access_list.expect("access_list should be set"),
755 access_list,
756 "Access list of the transaction should have been set to our access list"
757 )
758 }
759
760 #[test]
761 fn call_encoding() {
762 let provider = ProviderBuilder::new().connect_anvil();
763 let contract = MyContract::new(Address::ZERO, &&provider).with_cloned_provider();
764 let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
765 assert_eq!(
766 *call_builder.calldata(),
767 bytes!(
768 "b09a2616"
769 "0000000000000000000000000000000000000000000000000000000000000000"
770 "0000000000000000000000000000000000000000000000000000000000000001"
771 ),
772 );
773 let _future: Box<dyn Future<Output = Result<MyContract::doStuffReturn>> + Send> =
775 Box::new(async move { call_builder.call().await });
776 }
777
778 #[test]
779 fn deploy_encoding() {
780 let provider = ProviderBuilder::new().connect_anvil();
781 let bytecode = &MyContract::BYTECODE[..];
782 let call_builder = MyContract::deploy_builder(&provider, false);
783 assert_eq!(
784 call_builder.calldata()[..],
785 [
786 bytecode,
787 &hex!("0000000000000000000000000000000000000000000000000000000000000000")[..]
788 ]
789 .concat(),
790 );
791 let call_builder = MyContract::deploy_builder(&provider, true);
792 assert_eq!(
793 call_builder.calldata()[..],
794 [
795 bytecode,
796 &hex!("0000000000000000000000000000000000000000000000000000000000000001")[..]
797 ]
798 .concat(),
799 );
800 }
801
802 #[tokio::test(flavor = "multi_thread")]
803 async fn deploy_and_call() {
804 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
805
806 let expected_address = provider.default_signer_address().create(0);
807 let my_contract = MyContract::deploy(provider, true).await.unwrap();
808 assert_eq!(*my_contract.address(), expected_address);
809
810 let my_state_builder = my_contract.myState();
811 assert_eq!(my_state_builder.calldata()[..], MyContract::myStateCall {}.abi_encode(),);
812 let my_state = my_state_builder.call().await.unwrap();
813 assert!(my_state);
814
815 let do_stuff_builder = my_contract.doStuff(U256::from(0x69), true);
816 assert_eq!(
817 do_stuff_builder.calldata()[..],
818 MyContract::doStuffCall { a: U256::from(0x69), b: true }.abi_encode(),
819 );
820 let result: MyContract::doStuffReturn = do_stuff_builder.call().await.unwrap();
821 assert_eq!(result.c, address!("0000000000000000000000000000000000000069"));
822 assert_eq!(
823 result.d,
824 b256!("0000000000000000000000000000000000000000000000000000000000000001"),
825 );
826 }
827
828 #[tokio::test(flavor = "multi_thread")]
829 async fn deploy_and_call_with_priority() {
830 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
831 let counter_contract = Counter::deploy(provider.clone()).await.unwrap();
832 let max_fee_per_gas: U256 = parse_units("50", "gwei").unwrap().into();
833 let max_priority_fee_per_gas: U256 = parse_units("0.1", "gwei").unwrap().into();
834 let receipt = counter_contract
835 .increment()
836 .max_fee_per_gas(max_fee_per_gas.to())
837 .max_priority_fee_per_gas(max_priority_fee_per_gas.to())
838 .send()
839 .await
840 .expect("Could not send transaction")
841 .get_receipt()
842 .await
843 .expect("Could not get the receipt");
844 let transaction_hash = receipt.transaction_hash;
845 let transaction = provider
846 .get_transaction_by_hash(transaction_hash)
847 .await
848 .expect("failed to fetch tx")
849 .expect("tx not included");
850 assert_eq!(
851 transaction.max_fee_per_gas(),
852 max_fee_per_gas.to::<u128>(),
853 "max_fee_per_gas of the transaction should be set to the right value"
854 );
855 assert_eq!(
856 transaction
857 .max_priority_fee_per_gas()
858 .expect("max_priority_fee_per_gas of the transaction should be set"),
859 max_priority_fee_per_gas.to::<u128>(),
860 "max_priority_fee_per_gas of the transaction should be set to the right value"
861 )
862 }
863
864 sol! {
865 #[sol(rpc, bytecode = "6080604052348015600e575f80fd5b506101448061001c5f395ff3fe60806040526004361061001d575f3560e01c8063785d04f514610021575b5f80fd5b61003461002f3660046100d5565b610036565b005b5f816001600160a01b0316836040515f6040518083038185875af1925050503d805f811461007f576040519150601f19603f3d011682016040523d82523d5f602084013e610084565b606091505b50509050806100d05760405162461bcd60e51b81526020600482015260146024820152734661696c656420746f2073656e64206d6f6e657960601b604482015260640160405180910390fd5b505050565b5f80604083850312156100e6575f80fd5b8235915060208301356001600160a01b0381168114610103575f80fd5b80915050925092905056fea2646970667358221220188e65dcedbc4bd68fdebc795292d5a9bf643385f138383969a28f796ff8858664736f6c63430008190033")]
866 contract SendMoney {
867 function send(uint256 amount, address target) external payable {
868 (bool success, ) = target.call{value: amount}("");
869 require(success, "Failed to send money");
870 }
871 }
872 }
873
874 #[tokio::test]
876 async fn fill_eth_call() {
877 let anvil = Anvil::new().spawn();
878 let pk: PrivateKeySigner =
879 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
880
881 let wallet = EthereumWallet::new(pk);
882
883 let wallet_provider =
884 ProviderBuilder::new().wallet(wallet).connect_http(anvil.endpoint_url());
885
886 let contract = SendMoney::deploy(wallet_provider.clone()).await.unwrap();
887
888 let tx = contract
889 .send(U256::from(1000000), Address::with_last_byte(1))
890 .into_transaction_request()
891 .value(U256::from(1000000));
892
893 assert!(tx.from.is_none());
894
895 let std_provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
896 let should_fail = std_provider.estimate_gas(tx.clone()).await.is_err();
897
898 assert!(should_fail);
899
900 let gas = wallet_provider.estimate_gas(tx).await.unwrap();
901
902 assert_eq!(gas, 56555);
903 }
904
905 #[tokio::test]
906 async fn decode_eth_call_ret_bytes() {
907 sol! {
908 #[derive(Debug, PartialEq)]
909 #[sol(rpc, bytecode = "0x6080604052348015600e575f5ffd5b506101578061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80630d1d2c641461002d575b5f5ffd5b61003561004b565b6040516100429190610108565b60405180910390f35b61005361007b565b6040518060400160405280602a67ffffffffffffffff16815260200160011515815250905090565b60405180604001604052805f67ffffffffffffffff1681526020015f151581525090565b5f67ffffffffffffffff82169050919050565b6100bb8161009f565b82525050565b5f8115159050919050565b6100d5816100c1565b82525050565b604082015f8201516100ef5f8501826100b2565b50602082015161010260208501826100cc565b50505050565b5f60408201905061011b5f8301846100db565b9291505056fea264697066735822122039acc87c027f3bddf6806ff9914411d4245bdc708bca36a07138a37b1b98573464736f6c634300081c0033")]
910 contract RetStruct {
911 struct MyStruct {
912 uint64 a;
913 bool b;
914 }
915
916 function retStruct() external pure returns (MyStruct memory) {
917 return MyStruct(42, true);
918 }
919 }
920 }
921
922 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
923
924 let contract = RetStruct::deploy(provider.clone()).await.unwrap();
925
926 let tx = contract.retStruct().into_transaction_request();
927
928 let result =
929 provider.call(tx).decode_resp::<RetStruct::retStructCall>().await.unwrap().unwrap();
930
931 assert_eq!(result, RetStruct::MyStruct { a: 42, b: true });
932 }
933}