alloy_provider/ext/
erc4337.rs

1use crate::Provider;
2use alloy_network::Network;
3use alloy_primitives::{Address, Bytes};
4use alloy_rpc_types_eth::erc4337::{
5    SendUserOperation, SendUserOperationResponse, UserOperationGasEstimation, UserOperationReceipt,
6};
7use alloy_transport::TransportResult;
8
9/// ERC-4337 Account Abstraction API
10///
11/// This module provides support for the `eth_sendUserOperation` RPC method
12/// as defined in ERC-4337.
13#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
14#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
15pub trait Erc4337Api<N>: Send + Sync {
16    /// Sends a user operation to the bundler, as defined in ERC-4337.
17    ///
18    /// Entry point changes based on the user operation type.
19    async fn send_user_operation(
20        &self,
21        user_op: SendUserOperation,
22        entry_point: Address,
23    ) -> TransportResult<SendUserOperationResponse>;
24
25    /// Returns the list of supported entry points.
26    async fn supported_entry_points(&self) -> TransportResult<Vec<Address>>;
27
28    /// Returns the receipt for any user operation.
29    ///
30    /// Hash is the same returned by any user operation.
31    async fn get_user_operation_receipt(
32        &self,
33        user_op_hash: Bytes,
34    ) -> TransportResult<UserOperationReceipt>;
35
36    /// Estimates the gas for a user operation.
37    ///
38    /// Entry point changes based on the user operation type.
39    async fn estimate_user_operation_gas(
40        &self,
41        user_op: SendUserOperation,
42        entry_point: Address,
43    ) -> TransportResult<UserOperationGasEstimation>;
44}
45
46#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
47#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
48impl<N, P> Erc4337Api<N> for P
49where
50    N: Network,
51    P: Provider<N>,
52{
53    async fn send_user_operation(
54        &self,
55        user_op: SendUserOperation,
56        entry_point: Address,
57    ) -> TransportResult<SendUserOperationResponse> {
58        match user_op {
59            SendUserOperation::EntryPointV06(user_op) => {
60                self.client().request("eth_sendUserOperation", (user_op, entry_point)).await
61            }
62            SendUserOperation::EntryPointV07(packed_user_op) => {
63                self.client().request("eth_sendUserOperation", (packed_user_op, entry_point)).await
64            }
65        }
66    }
67
68    async fn supported_entry_points(&self) -> TransportResult<Vec<Address>> {
69        self.client().request("eth_supportedEntryPoints", ()).await
70    }
71
72    async fn get_user_operation_receipt(
73        &self,
74        user_op_hash: Bytes,
75    ) -> TransportResult<UserOperationReceipt> {
76        self.client().request("eth_getUserOperationReceipt", (user_op_hash,)).await
77    }
78
79    async fn estimate_user_operation_gas(
80        &self,
81        user_op: SendUserOperation,
82        entry_point: Address,
83    ) -> TransportResult<UserOperationGasEstimation> {
84        match user_op {
85            SendUserOperation::EntryPointV06(user_op) => {
86                self.client().request("eth_estimateUserOperationGas", (user_op, entry_point)).await
87            }
88            SendUserOperation::EntryPointV07(packed_user_op) => {
89                self.client()
90                    .request("eth_estimateUserOperationGas", (packed_user_op, entry_point))
91                    .await
92            }
93        }
94    }
95}