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, 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::{state::StateOverride, AccessList, BlobTransactionSidecar, BlockId};
13use alloy_sol_types::SolCall;
14use std::{self, marker::PhantomData};
15
16pub type SolCallBuilder<P, C, N = Ethereum> = CallBuilder<P, PhantomData<C>, N>;
22
23pub type DynCallBuilder<P, N = Ethereum> = CallBuilder<P, Function, N>;
25
26pub type RawCallBuilder<P, N = Ethereum> = CallBuilder<P, (), N>;
28
29#[derive(Clone)]
125#[must_use = "call builders do nothing unless you `.call`, `.send`, or `.await` them"]
126pub struct CallBuilder<P, D, N: Network = Ethereum> {
127 pub(crate) request: N::TransactionRequest,
128 block: BlockId,
129 state: Option<StateOverride>,
130 pub provider: P,
133 decoder: D,
134}
135
136impl<P, D, N: Network> CallBuilder<P, D, N> {
137 pub fn into_transaction_request(self) -> N::TransactionRequest {
139 self.request
140 }
141
142 pub fn build_unsigned_raw_transaction(self) -> Result<Vec<u8>, TransactionBuilderError<N>>
175 where
176 N::UnsignedTx: SignableTransaction<Signature>,
177 {
178 let tx = self.request.build_unsigned().map_err(|e| e.error)?;
179 Ok(tx.encoded_for_signing())
180 }
181
182 pub async fn build_raw_transaction<S>(
218 self,
219 signer: S,
220 ) -> Result<Vec<u8>, TransactionBuilderError<N>>
221 where
222 S: TxSigner<Signature> + IntoWallet<N>,
223 {
224 let tx = self.request.build(&signer.into_wallet()).await?;
225 Ok(tx.encoded_2718())
226 }
227}
228
229impl<P, D, N: Network> AsRef<N::TransactionRequest> for CallBuilder<P, D, N> {
230 fn as_ref(&self) -> &N::TransactionRequest {
231 &self.request
232 }
233}
234
235impl<P: Provider<N>, N: Network> DynCallBuilder<P, N> {
237 pub(crate) fn new_dyn(
238 provider: P,
239 address: &Address,
240 function: &Function,
241 args: &[DynSolValue],
242 ) -> Result<Self> {
243 Ok(Self::new_inner_call(
244 provider,
245 function.abi_encode_input(args)?.into(),
246 function.clone(),
247 )
248 .to(*address))
249 }
250
251 #[inline]
253 pub fn clear_decoder(self) -> RawCallBuilder<P, N> {
254 RawCallBuilder {
255 request: self.request,
256 block: self.block,
257 state: self.state,
258 provider: self.provider,
259 decoder: (),
260 }
261 }
262}
263
264#[doc(hidden)]
265impl<'a, P: Provider<N>, C: SolCall, N: Network> SolCallBuilder<&'a P, C, N> {
266 pub fn new_sol(provider: &'a P, address: &Address, call: &C) -> Self {
269 Self::new_inner_call(provider, call.abi_encode().into(), PhantomData::<C>).to(*address)
270 }
271}
272
273impl<P: Provider<N>, C: SolCall, N: Network> SolCallBuilder<P, C, N> {
274 #[inline]
276 pub fn clear_decoder(self) -> RawCallBuilder<P, N> {
277 RawCallBuilder {
278 request: self.request,
279 block: self.block,
280 state: self.state,
281 provider: self.provider,
282 decoder: (),
283 }
284 }
285}
286
287impl<P: Provider<N>, N: Network> RawCallBuilder<P, N> {
288 #[inline]
340 pub fn with_sol_decoder<C: SolCall>(self) -> SolCallBuilder<P, C, N> {
341 SolCallBuilder {
342 request: self.request,
343 block: self.block,
344 state: self.state,
345 provider: self.provider,
346 decoder: PhantomData::<C>,
347 }
348 }
349}
350
351impl<P: Provider<N>, N: Network> RawCallBuilder<P, N> {
352 #[inline]
357 pub fn new_raw(provider: P, input: Bytes) -> Self {
358 Self::new_inner_call(provider, input, ())
359 }
360
361 pub fn new_raw_deploy(provider: P, input: Bytes) -> Self {
367 Self::new_inner_deploy(provider, input, ())
368 }
369}
370
371impl<P: Provider<N>, D: CallDecoder, N: Network> CallBuilder<P, D, N> {
372 fn new_inner_deploy(provider: P, input: Bytes, decoder: D) -> Self {
373 Self {
374 request: <N::TransactionRequest>::default().with_deploy_code(input),
375 decoder,
376 provider,
377 block: BlockId::default(),
378 state: None,
379 }
380 }
381
382 fn new_inner_call(provider: P, input: Bytes, decoder: D) -> Self {
383 Self {
384 request: <N::TransactionRequest>::default().with_input(input),
385 decoder,
386 provider,
387 block: BlockId::default(),
388 state: None,
389 }
390 }
391
392 pub fn chain_id(mut self, chain_id: ChainId) -> Self {
394 self.request.set_chain_id(chain_id);
395 self
396 }
397
398 pub fn from(mut self, from: Address) -> Self {
400 self.request.set_from(from);
401 self
402 }
403
404 pub fn kind(mut self, to: TxKind) -> Self {
406 self.request.set_kind(to);
407 self
408 }
409
410 pub fn to(mut self, to: Address) -> Self {
412 self.request.set_to(to);
413 self
414 }
415
416 pub fn sidecar(mut self, blob_sidecar: BlobTransactionSidecar) -> Self
418 where
419 N::TransactionRequest: TransactionBuilder4844,
420 {
421 self.request.set_blob_sidecar(blob_sidecar);
422 self
423 }
424
425 pub fn legacy(self) -> Self {
427 todo!()
428 }
429
430 pub fn gas(mut self, gas: u64) -> Self {
432 self.request.set_gas_limit(gas);
433 self
434 }
435
436 pub fn gas_price(mut self, gas_price: u128) -> Self {
440 self.request.set_gas_price(gas_price);
441 self
442 }
443
444 pub fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
446 self.request.set_max_fee_per_gas(max_fee_per_gas);
447 self
448 }
449
450 pub fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
452 self.request.set_max_priority_fee_per_gas(max_priority_fee_per_gas);
453 self
454 }
455
456 pub fn max_fee_per_blob_gas(mut self, max_fee_per_blob_gas: u128) -> Self
458 where
459 N::TransactionRequest: TransactionBuilder4844,
460 {
461 self.request.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
462 self
463 }
464
465 pub fn access_list(mut self, access_list: AccessList) -> Self {
467 self.request.set_access_list(access_list);
468 self
469 }
470
471 pub fn value(mut self, value: U256) -> Self {
473 self.request.set_value(value);
474 self
475 }
476
477 pub fn nonce(mut self, nonce: u64) -> Self {
479 self.request.set_nonce(nonce);
480 self
481 }
482
483 pub fn map<F>(mut self, f: F) -> Self
485 where
486 F: FnOnce(N::TransactionRequest) -> N::TransactionRequest,
487 {
488 self.request = f(self.request);
489 self
490 }
491
492 pub const fn block(mut self, block: BlockId) -> Self {
494 self.block = block;
495 self
496 }
497
498 pub fn state(mut self, state: impl Into<StateOverride>) -> Self {
504 self.state = Some(state.into());
505 self
506 }
507
508 pub fn calldata(&self) -> &Bytes {
510 self.request.input().expect("set in the constructor")
511 }
512
513 pub async fn estimate_gas(&self) -> Result<u64> {
516 let mut estimate = self.provider.estimate_gas(self.request.clone());
517 if let Some(state) = self.state.clone() {
518 estimate = estimate.overrides(state);
519 }
520 estimate.block(self.block).await.map_err(Into::into)
521 }
522
523 #[doc(alias = "eth_call")]
529 #[doc(alias = "call_with_overrides")]
530 pub fn call(&self) -> EthCall<'_, D, N> {
531 self.call_raw().with_decoder(&self.decoder)
532 }
533
534 pub fn call_raw(&self) -> EthCall<'_, (), N> {
541 let call = self.provider.call(self.request.clone()).block(self.block);
542 let call = match self.state.clone() {
543 Some(state) => call.overrides(state),
544 None => call,
545 };
546 call.into()
547 }
548
549 #[inline]
551 pub fn decode_output(&self, data: Bytes) -> Result<D::CallOutput> {
552 self.decoder.abi_decode_output(data)
553 }
554
555 pub async fn deploy(&self) -> Result<Address> {
566 if !self.request.kind().is_some_and(|to| to.is_create()) {
567 return Err(Error::NotADeploymentTransaction);
568 }
569 let pending_tx = self.send().await?;
570 let receipt = pending_tx.get_receipt().await?;
571 receipt.contract_address().ok_or(Error::ContractNotDeployed)
572 }
573
574 pub async fn send(&self) -> Result<PendingTransactionBuilder<N>> {
579 Ok(self.provider.send_transaction(self.request.clone()).await?)
580 }
581
582 pub fn calculate_create_address(&self) -> Option<Address> {
587 self.request.calculate_create_address()
588 }
589}
590
591impl<P: Clone, D, N: Network> CallBuilder<&P, D, N> {
592 pub fn with_cloned_provider(self) -> CallBuilder<P, D, N> {
594 CallBuilder {
595 request: self.request,
596 block: self.block,
597 state: self.state,
598 provider: self.provider.clone(),
599 decoder: self.decoder,
600 }
601 }
602}
603
604impl<P, D: CallDecoder, N: Network> std::fmt::Debug for CallBuilder<P, D, N> {
605 #[inline]
606 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
607 f.debug_struct("CallBuilder")
608 .field("request", &self.request)
609 .field("block", &self.block)
610 .field("state", &self.state)
611 .field("decoder", &self.decoder.as_debug_field())
612 .finish()
613 }
614}
615
616#[cfg(test)]
617mod tests {
618 use super::*;
619 use alloy_consensus::Transaction;
620 use alloy_network::EthereumWallet;
621 use alloy_node_bindings::Anvil;
622 use alloy_primitives::{address, b256, bytes, hex, utils::parse_units, B256};
623 use alloy_provider::{Provider, ProviderBuilder, WalletProvider};
624 use alloy_rpc_types_eth::AccessListItem;
625 use alloy_signer_local::PrivateKeySigner;
626 use alloy_sol_types::sol;
627 use futures::Future;
628
629 #[test]
630 fn empty_constructor() {
631 sol! {
632 #[sol(rpc, bytecode = "6942")]
633 contract EmptyConstructor {
634 constructor();
635 }
636 }
637
638 let provider = ProviderBuilder::new().connect_anvil();
639 let call_builder = EmptyConstructor::deploy_builder(&provider);
640 assert_eq!(*call_builder.calldata(), bytes!("6942"));
641 }
642
643 sol! {
644 #[sol(rpc, bytecode = "60803461006357601f61014838819003918201601f19168301916001600160401b038311848410176100675780849260209460405283398101031261006357518015158091036100635760ff80195f54169116175f5560405160cc908161007c8239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60808060405260043610156011575f80fd5b5f3560e01c9081638bf1799f14607a575063b09a261614602f575f80fd5b346076576040366003190112607657602435801515810360765715606f57604060015b81516004356001600160a01b0316815260ff919091166020820152f35b60405f6052565b5f80fd5b346076575f36600319011260765760209060ff5f541615158152f3fea264697066735822122043709781c9bdc30c530978abf5db25a4b4ccfebf989baafd2ba404519a7f7e8264736f6c63430008180033")]
647 contract MyContract {
648 bool public myState;
649
650 constructor(bool myState_) {
651 myState = myState_;
652 }
653
654 function doStuff(uint a, bool b) external pure returns(address c, bytes32 d) {
655 return (address(uint160(a)), bytes32(uint256(b ? 1 : 0)));
656 }
657 }
658 }
659
660 sol! {
661 #[sol(rpc, bytecode = "608080604052346100155760d4908161001a8239f35b5f80fdfe60808060405260043610156011575f80fd5b5f3560e01c90816361bc221a14607e575063d09de08a14602f575f80fd5b34607a575f366003190112607a575f546001600160801b038082166001018181116066576001600160801b03199092169116175f55005b634e487b7160e01b5f52601160045260245ffd5b5f80fd5b34607a575f366003190112607a575f546001600160801b03168152602090f3fea26469706673582212208b360e442c4bb2a4bbdec007ee24588c7a88e0aa52ac39efac748e5e23eff69064736f6c63430008180033")]
664 contract Counter {
665 uint128 public counter;
666
667 function increment() external {
668 counter += 1;
669 }
670 }
671 }
672
673 fn build_call_builder() -> CallBuilder<impl Provider, PhantomData<MyContract::doStuffCall>> {
675 let provider = ProviderBuilder::new().connect_anvil();
676 let contract = MyContract::new(Address::ZERO, provider);
677 let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
678 call_builder
679 }
680
681 #[test]
682 fn change_chain_id() {
683 let call_builder = build_call_builder().chain_id(1337);
684 assert_eq!(
685 call_builder.request.chain_id.expect("chain_id should be set"),
686 1337,
687 "chain_id of request should be '1337'"
688 );
689 }
690
691 #[test]
692 fn change_max_fee_per_gas() {
693 let call_builder = build_call_builder().max_fee_per_gas(42);
694 assert_eq!(
695 call_builder.request.max_fee_per_gas.expect("max_fee_per_gas should be set"),
696 42,
697 "max_fee_per_gas of request should be '42'"
698 );
699 }
700
701 #[test]
702 fn change_max_priority_fee_per_gas() {
703 let call_builder = build_call_builder().max_priority_fee_per_gas(45);
704 assert_eq!(
705 call_builder
706 .request
707 .max_priority_fee_per_gas
708 .expect("max_priority_fee_per_gas should be set"),
709 45,
710 "max_priority_fee_per_gas of request should be '45'"
711 );
712 }
713
714 #[test]
715 fn change_max_fee_per_blob_gas() {
716 let call_builder = build_call_builder().max_fee_per_blob_gas(50);
717 assert_eq!(
718 call_builder.request.max_fee_per_blob_gas.expect("max_fee_per_blob_gas should be set"),
719 50,
720 "max_fee_per_blob_gas of request should be '50'"
721 );
722 }
723
724 #[test]
725 fn change_access_list() {
726 let access_list = AccessList::from(vec![AccessListItem {
727 address: Address::ZERO,
728 storage_keys: vec![B256::ZERO],
729 }]);
730 let call_builder = build_call_builder().access_list(access_list.clone());
731 assert_eq!(
732 call_builder.request.access_list.expect("access_list should be set"),
733 access_list,
734 "Access list of the transaction should have been set to our access list"
735 )
736 }
737
738 #[test]
739 fn call_encoding() {
740 let provider = ProviderBuilder::new().connect_anvil();
741 let contract = MyContract::new(Address::ZERO, &&provider).with_cloned_provider();
742 let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider();
743 assert_eq!(
744 *call_builder.calldata(),
745 bytes!(
746 "b09a2616"
747 "0000000000000000000000000000000000000000000000000000000000000000"
748 "0000000000000000000000000000000000000000000000000000000000000001"
749 ),
750 );
751 let _future: Box<dyn Future<Output = Result<MyContract::doStuffReturn>> + Send> =
753 Box::new(async move { call_builder.call().await });
754 }
755
756 #[test]
757 fn deploy_encoding() {
758 let provider = ProviderBuilder::new().connect_anvil();
759 let bytecode = &MyContract::BYTECODE[..];
760 let call_builder = MyContract::deploy_builder(&provider, false);
761 assert_eq!(
762 call_builder.calldata()[..],
763 [
764 bytecode,
765 &hex!("0000000000000000000000000000000000000000000000000000000000000000")[..]
766 ]
767 .concat(),
768 );
769 let call_builder = MyContract::deploy_builder(&provider, true);
770 assert_eq!(
771 call_builder.calldata()[..],
772 [
773 bytecode,
774 &hex!("0000000000000000000000000000000000000000000000000000000000000001")[..]
775 ]
776 .concat(),
777 );
778 }
779
780 #[tokio::test(flavor = "multi_thread")]
781 async fn deploy_and_call() {
782 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
783
784 let expected_address = provider.default_signer_address().create(0);
785 let my_contract = MyContract::deploy(provider, true).await.unwrap();
786 assert_eq!(*my_contract.address(), expected_address);
787
788 let my_state_builder = my_contract.myState();
789 assert_eq!(my_state_builder.calldata()[..], MyContract::myStateCall {}.abi_encode(),);
790 let my_state = my_state_builder.call().await.unwrap();
791 assert!(my_state);
792
793 let do_stuff_builder = my_contract.doStuff(U256::from(0x69), true);
794 assert_eq!(
795 do_stuff_builder.calldata()[..],
796 MyContract::doStuffCall { a: U256::from(0x69), b: true }.abi_encode(),
797 );
798 let result: MyContract::doStuffReturn = do_stuff_builder.call().await.unwrap();
799 assert_eq!(result.c, address!("0000000000000000000000000000000000000069"));
800 assert_eq!(
801 result.d,
802 b256!("0000000000000000000000000000000000000000000000000000000000000001"),
803 );
804 }
805
806 #[tokio::test(flavor = "multi_thread")]
807 async fn deploy_and_call_with_priority() {
808 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
809 let counter_contract = Counter::deploy(provider.clone()).await.unwrap();
810 let max_fee_per_gas: U256 = parse_units("50", "gwei").unwrap().into();
811 let max_priority_fee_per_gas: U256 = parse_units("0.1", "gwei").unwrap().into();
812 let receipt = counter_contract
813 .increment()
814 .max_fee_per_gas(max_fee_per_gas.to())
815 .max_priority_fee_per_gas(max_priority_fee_per_gas.to())
816 .send()
817 .await
818 .expect("Could not send transaction")
819 .get_receipt()
820 .await
821 .expect("Could not get the receipt");
822 let transaction_hash = receipt.transaction_hash;
823 let transaction = provider
824 .get_transaction_by_hash(transaction_hash)
825 .await
826 .expect("failed to fetch tx")
827 .expect("tx not included");
828 assert_eq!(
829 transaction.max_fee_per_gas(),
830 max_fee_per_gas.to::<u128>(),
831 "max_fee_per_gas of the transaction should be set to the right value"
832 );
833 assert_eq!(
834 transaction
835 .max_priority_fee_per_gas()
836 .expect("max_priority_fee_per_gas of the transaction should be set"),
837 max_priority_fee_per_gas.to::<u128>(),
838 "max_priority_fee_per_gas of the transaction should be set to the right value"
839 )
840 }
841
842 sol! {
843 #[sol(rpc, bytecode = "6080604052348015600e575f80fd5b506101448061001c5f395ff3fe60806040526004361061001d575f3560e01c8063785d04f514610021575b5f80fd5b61003461002f3660046100d5565b610036565b005b5f816001600160a01b0316836040515f6040518083038185875af1925050503d805f811461007f576040519150601f19603f3d011682016040523d82523d5f602084013e610084565b606091505b50509050806100d05760405162461bcd60e51b81526020600482015260146024820152734661696c656420746f2073656e64206d6f6e657960601b604482015260640160405180910390fd5b505050565b5f80604083850312156100e6575f80fd5b8235915060208301356001600160a01b0381168114610103575f80fd5b80915050925092905056fea2646970667358221220188e65dcedbc4bd68fdebc795292d5a9bf643385f138383969a28f796ff8858664736f6c63430008190033")]
844 contract SendMoney {
845 function send(uint256 amount, address target) external payable {
846 (bool success, ) = target.call{value: amount}("");
847 require(success, "Failed to send money");
848 }
849 }
850 }
851
852 #[tokio::test]
854 async fn fill_eth_call() {
855 let anvil = Anvil::new().spawn();
856 let pk: PrivateKeySigner =
857 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
858
859 let wallet = EthereumWallet::new(pk);
860
861 let wallet_provider =
862 ProviderBuilder::new().wallet(wallet).connect_http(anvil.endpoint_url());
863
864 let contract = SendMoney::deploy(wallet_provider.clone()).await.unwrap();
865
866 let tx = contract
867 .send(U256::from(1000000), Address::with_last_byte(1))
868 .into_transaction_request()
869 .value(U256::from(1000000));
870
871 assert!(tx.from.is_none());
872
873 let std_provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
874 let should_fail = std_provider.estimate_gas(tx.clone()).await.is_err();
875
876 assert!(should_fail);
877
878 let gas = wallet_provider.estimate_gas(tx).await.unwrap();
879
880 assert_eq!(gas, 56555);
881 }
882
883 #[tokio::test]
884 async fn decode_eth_call_ret_bytes() {
885 sol! {
886 #[derive(Debug, PartialEq)]
887 #[sol(rpc, bytecode = "0x6080604052348015600e575f5ffd5b506101578061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610029575f3560e01c80630d1d2c641461002d575b5f5ffd5b61003561004b565b6040516100429190610108565b60405180910390f35b61005361007b565b6040518060400160405280602a67ffffffffffffffff16815260200160011515815250905090565b60405180604001604052805f67ffffffffffffffff1681526020015f151581525090565b5f67ffffffffffffffff82169050919050565b6100bb8161009f565b82525050565b5f8115159050919050565b6100d5816100c1565b82525050565b604082015f8201516100ef5f8501826100b2565b50602082015161010260208501826100cc565b50505050565b5f60408201905061011b5f8301846100db565b9291505056fea264697066735822122039acc87c027f3bddf6806ff9914411d4245bdc708bca36a07138a37b1b98573464736f6c634300081c0033")]
888 contract RetStruct {
889 struct MyStruct {
890 uint64 a;
891 bool b;
892 }
893
894 function retStruct() external pure returns (MyStruct memory) {
895 return MyStruct(42, true);
896 }
897 }
898 }
899
900 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
901
902 let contract = RetStruct::deploy(provider.clone()).await.unwrap();
903
904 let tx = contract.retStruct().into_transaction_request();
905
906 let result =
907 provider.call(tx).decode_resp::<RetStruct::retStructCall>().await.unwrap().unwrap();
908
909 assert_eq!(result, RetStruct::MyStruct { a: 42, b: true });
910 }
911}