-
Notifications
You must be signed in to change notification settings - Fork 198
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add erc4337 endpoint methods to provider (#1176)
* feat: add eip4337 eth_sendUserOperation method to provider * chore: serde rename all eip4337 operation * chore: use actual entry address for eth eip4337 * chore: nightly fmt * add feature * fix: user op elements in test * chore: rename eip to erc * chore: rename eip4337 files to erc4337 * chore: resolve pr comment * fix: address parse * feat: add new endpoints * feat: add gas estimate endpoint * chore: add err code * chore: use packed User Operation * chore: update tests * feat: combine user op for both old and new entry points * fix: clippy and docs ci * fix: docs * fix: geth tests * fix: rm tests --------- Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
- Loading branch information
1 parent
ff4e72e
commit 155914b
Showing
7 changed files
with
281 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
use crate::Provider; | ||
use alloy_network::Network; | ||
use alloy_primitives::{Address, Bytes}; | ||
use alloy_rpc_types_eth::erc4337::{ | ||
SendUserOperation, SendUserOperationResponse, UserOperationGasEstimation, UserOperationReceipt, | ||
}; | ||
use alloy_transport::{Transport, TransportResult}; | ||
|
||
/// ERC-4337 Account Abstraction API | ||
/// | ||
/// This module provides support for the `eth_sendUserOperation` RPC method | ||
/// as defined in ERC-4337. | ||
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] | ||
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] | ||
pub trait Erc4337Api<N, T>: Send + Sync { | ||
/// Sends a user operation to the bundler, as defined in ERC-4337. | ||
/// | ||
/// Entry point changes based on the user operation type. | ||
async fn send_user_operation( | ||
&self, | ||
user_op: SendUserOperation, | ||
entry_point: Address, | ||
) -> TransportResult<SendUserOperationResponse>; | ||
|
||
/// Returns the list of supported entry points. | ||
async fn supported_entry_points(&self) -> TransportResult<Vec<Address>>; | ||
|
||
/// Returns the receipt for any user operation. | ||
/// | ||
/// Hash is the same returned by any user operation. | ||
async fn get_user_operation_receipt( | ||
&self, | ||
user_op_hash: Bytes, | ||
) -> TransportResult<UserOperationReceipt>; | ||
|
||
/// Estimates the gas for a user operation. | ||
/// | ||
/// Entry point changes based on the user operation type. | ||
async fn estimate_user_operation_gas( | ||
&self, | ||
user_op: SendUserOperation, | ||
entry_point: Address, | ||
) -> TransportResult<UserOperationGasEstimation>; | ||
} | ||
|
||
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] | ||
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] | ||
impl<N, T, P> Erc4337Api<N, T> for P | ||
where | ||
N: Network, | ||
T: Transport + Clone, | ||
P: Provider<T, N>, | ||
{ | ||
async fn send_user_operation( | ||
&self, | ||
user_op: SendUserOperation, | ||
entry_point: Address, | ||
) -> TransportResult<SendUserOperationResponse> { | ||
match user_op { | ||
SendUserOperation::EntryPointV06(user_op) => { | ||
self.client().request("eth_sendUserOperation", (user_op, entry_point)).await | ||
} | ||
SendUserOperation::EntryPointV07(packed_user_op) => { | ||
self.client().request("eth_sendUserOperation", (packed_user_op, entry_point)).await | ||
} | ||
} | ||
} | ||
|
||
async fn supported_entry_points(&self) -> TransportResult<Vec<Address>> { | ||
self.client().request("eth_supportedEntryPoints", ()).await | ||
} | ||
|
||
async fn get_user_operation_receipt( | ||
&self, | ||
user_op_hash: Bytes, | ||
) -> TransportResult<UserOperationReceipt> { | ||
self.client().request("eth_getUserOperationReceipt", (user_op_hash,)).await | ||
} | ||
|
||
async fn estimate_user_operation_gas( | ||
&self, | ||
user_op: SendUserOperation, | ||
entry_point: Address, | ||
) -> TransportResult<UserOperationGasEstimation> { | ||
match user_op { | ||
SendUserOperation::EntryPointV06(user_op) => { | ||
self.client().request("eth_estimateUserOperationGas", (user_op, entry_point)).await | ||
} | ||
SendUserOperation::EntryPointV07(packed_user_op) => { | ||
self.client() | ||
.request("eth_estimateUserOperationGas", (packed_user_op, entry_point)) | ||
.await | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
use crate::{Log, TransactionReceipt}; | ||
use alloy_primitives::{Address, BlockNumber, Bytes, B256, U256}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
|
||
/// Options for conditional raw transaction submissions. | ||
// reference for the implementation <https://notes.ethereum.org/@yoav/SkaX2lS9j#> | ||
// See also <https://pkg.go.dev/github.com/aK0nshin/go-ethereum/arbitrum_types#ConditionalOptions> | ||
#[derive(Debug, Serialize, Deserialize, Clone, Default)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct ConditionalOptions { | ||
/// A map of account addresses to their expected storage states. | ||
/// Each account can have a specified storage root or explicit slot-value pairs. | ||
#[serde(default)] | ||
pub known_accounts: HashMap<Address, AccountStorage>, | ||
/// The minimal block number at which the transaction can be included. | ||
/// `None` indicates no minimum block number constraint. | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub block_number_min: Option<BlockNumber>, | ||
/// The maximal block number at which the transaction can be included. | ||
/// `None` indicates no maximum block number constraint. | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub block_number_max: Option<BlockNumber>, | ||
/// The minimal timestamp at which the transaction can be included. | ||
/// `None` indicates no minimum timestamp constraint. | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub timestamp_min: Option<u64>, | ||
/// The maximal timestamp at which the transaction can be included. | ||
/// `None` indicates no maximum timestamp constraint. | ||
#[serde(default, skip_serializing_if = "Option::is_none")] | ||
pub timestamp_max: Option<u64>, | ||
} | ||
|
||
/// Represents the expected state of an account for a transaction to be conditionally accepted. | ||
#[derive(Debug, Serialize, Deserialize, Clone)] | ||
#[serde(untagged)] | ||
pub enum AccountStorage { | ||
/// Expected storage root hash of the account. | ||
RootHash(B256), | ||
/// Explicit storage slots and their expected values. | ||
Slots(HashMap<U256, B256>), | ||
} | ||
|
||
/// [`UserOperation`] in the spec: Entry Point V0.6 | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct UserOperation { | ||
/// The address of the smart contract account | ||
pub sender: Address, | ||
/// Anti-replay protection; also used as the salt for first-time account creation | ||
pub nonce: U256, | ||
/// Code used to deploy the account if not yet on-chain | ||
pub init_code: Bytes, | ||
/// Data that's passed to the sender for execution | ||
pub call_data: Bytes, | ||
/// Gas limit for execution phase | ||
pub call_gas_limit: U256, | ||
/// Gas limit for verification phase | ||
pub verification_gas_limit: U256, | ||
/// Gas to compensate the bundler | ||
pub pre_verification_gas: U256, | ||
/// Maximum fee per gas | ||
pub max_fee_per_gas: U256, | ||
/// Maximum priority fee per gas | ||
pub max_priority_fee_per_gas: U256, | ||
/// Paymaster Contract address and any extra data required for verification and execution | ||
/// (empty for self-sponsored transaction) | ||
pub paymaster_and_data: Bytes, | ||
/// Used to validate a UserOperation along with the nonce during verification | ||
pub signature: Bytes, | ||
} | ||
|
||
/// [`PackedUserOperation`] in the spec: Entry Point V0.7 | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct PackedUserOperation { | ||
/// The account making the operation. | ||
pub sender: Address, | ||
/// Prevents message replay attacks and serves as a randomizing element for initial user | ||
/// registration. | ||
pub nonce: U256, | ||
/// Deployer contract address: Required exclusively for deploying new accounts that don't yet | ||
/// exist on the blockchain. | ||
pub factory: Address, | ||
/// Factory data for the account creation process, applicable only when using a deployer | ||
/// contract. | ||
pub factory_data: Bytes, | ||
/// The call data. | ||
pub call_data: Bytes, | ||
/// The gas limit for the call. | ||
pub call_gas_limit: U256, | ||
/// The gas limit for the verification. | ||
pub verification_gas_limit: U256, | ||
/// Prepaid gas fee: Covers the bundler's costs for initial transaction validation and data | ||
/// transmission. | ||
pub pre_verification_gas: U256, | ||
/// The maximum fee per gas. | ||
pub max_fee_per_gas: U256, | ||
/// The maximum priority fee per gas. | ||
pub max_priority_fee_per_gas: U256, | ||
/// Paymaster contract address: Needed if a third party is covering transaction costs; left | ||
/// blank for self-funded accounts. | ||
pub paymaster: Address, | ||
/// The gas limit for the paymaster verification. | ||
pub paymaster_verification_gas_limit: U256, | ||
/// The gas limit for the paymaster post-operation. | ||
pub paymaster_post_op_gas_limit: U256, | ||
/// The paymaster data. | ||
pub paymaster_data: Bytes, | ||
/// The signature of the transaction. | ||
pub signature: Bytes, | ||
} | ||
|
||
/// Send User Operation | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
pub enum SendUserOperation { | ||
/// User Operation | ||
EntryPointV06(UserOperation), | ||
/// Packed User Operation | ||
EntryPointV07(PackedUserOperation), | ||
} | ||
|
||
/// Response to sending a user operation. | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct SendUserOperationResponse { | ||
/// The hash of the user operation. | ||
pub user_op_hash: Bytes, | ||
} | ||
|
||
/// Represents the receipt of a user operation. | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct UserOperationReceipt { | ||
/// The hash of the user operation. | ||
pub user_op_hash: Bytes, | ||
/// The entry point address for the user operation. | ||
pub entry_point: Address, | ||
/// The address of the sender of the user operation. | ||
pub sender: Address, | ||
/// The nonce of the user operation. | ||
pub nonce: U256, | ||
/// The address of the paymaster, if any. | ||
pub paymaster: Address, | ||
/// The actual gas cost incurred by the user operation. | ||
pub actual_gas_cost: U256, | ||
/// The actual gas used by the user operation. | ||
pub actual_gas_used: U256, | ||
/// Indicates whether the user operation was successful. | ||
pub success: bool, | ||
/// The reason for failure, if any. | ||
pub reason: Bytes, | ||
/// The logs generated by the user operation. | ||
pub logs: Vec<Log>, | ||
/// The transaction receipt of the user operation. | ||
pub receipt: TransactionReceipt, | ||
} | ||
|
||
/// Represents the gas estimation for a user operation. | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct UserOperationGasEstimation { | ||
/// The gas limit for the pre-verification. | ||
pub pre_verification_gas: U256, | ||
/// The gas limit for the verification. | ||
pub verification_gas: U256, | ||
/// The gas limit for the paymaster verification. | ||
pub paymaster_verification_gas: U256, | ||
/// The gas limit for the call. | ||
pub call_gas_limit: U256, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters