diff --git a/Cargo.lock b/Cargo.lock index 9b6dd5b66..db11b7f7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5200,13 +5200,16 @@ dependencies = [ name = "rundler-rpc" version = "0.3.0" dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-sol-types", "anyhow", "async-trait", - "ethers", "futures-util", "jsonrpsee", "metrics", "mockall", + "rundler-contracts", "rundler-provider", "rundler-sim", "rundler-task", diff --git a/crates/provider/src/alloy/metrics.rs b/crates/provider/src/alloy/metrics.rs new file mode 100644 index 000000000..23c92e6d2 --- /dev/null +++ b/crates/provider/src/alloy/metrics.rs @@ -0,0 +1,32 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use alloy_json_rpc::RequestPacket; +/// Method extractor +use rundler_types::task::traits::RequestExtractor; + +/// Method extractor for Alloy providers +#[derive(Clone, Copy)] +pub struct AlloyMethodExtractor; + +impl RequestExtractor for AlloyMethodExtractor { + fn get_method_name(req: &RequestPacket) -> String { + match req { + RequestPacket::Single(request) => request.method().to_string(), + _ => { + // can't extract method name for batch. + "batch".to_string() + } + } + } +} diff --git a/crates/provider/src/alloy/mod.rs b/crates/provider/src/alloy/mod.rs index cbd639dc4..2a7b1b800 100644 --- a/crates/provider/src/alloy/mod.rs +++ b/crates/provider/src/alloy/mod.rs @@ -13,3 +13,4 @@ pub(crate) mod entry_point; pub(crate) mod evm; +pub(crate) mod metrics; diff --git a/crates/provider/src/lib.rs b/crates/provider/src/lib.rs index 94b6d814b..6b429a8d5 100644 --- a/crates/provider/src/lib.rs +++ b/crates/provider/src/lib.rs @@ -31,6 +31,7 @@ pub use alloy::{ }, }, evm::AlloyEvmProvider, + metrics::AlloyMethodExtractor, }; mod traits; diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index af651bc91..b5cb21a8c 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -8,15 +8,18 @@ repository.workspace = true publish = false [dependencies] +rundler-contracts = { path = "../contracts" } rundler-provider = { path = "../provider" } rundler-sim = { path = "../sim" } rundler-task = { path = "../task" } rundler-types = { path = "../types" } rundler-utils = { path = "../utils" } +alloy-primitives.workspace = true +alloy-sol-types.workspace = true + anyhow.workspace = true async-trait.workspace = true -ethers.workspace = true jsonrpsee = { workspace = true , features = ["client", "macros", "server"] } metrics.workspace = true thiserror.workspace = true @@ -32,6 +35,7 @@ futures-util.workspace = true [dev-dependencies] mockall.workspace = true +alloy-consensus.workspace = true rundler-provider = { path = "../provider", features = ["test-utils"]} rundler-sim = { path = "../sim", features = ["test-utils"] } rundler-types= { path = "../types", features = ["test-utils"]} diff --git a/crates/rpc/src/admin.rs b/crates/rpc/src/admin.rs index 73e896fc6..8ecd9dcf1 100644 --- a/crates/rpc/src/admin.rs +++ b/crates/rpc/src/admin.rs @@ -11,9 +11,9 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. +use alloy_primitives::Address; use anyhow::Context; use async_trait::async_trait; -use ethers::types::Address; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use rundler_types::pool::Pool; @@ -51,7 +51,7 @@ impl

AdminApi

{ #[async_trait] impl

AdminApiServer for AdminApi

where - P: Pool, + P: Pool + 'static, { async fn clear_state(&self, clear_params: RpcAdminClearState) -> RpcResult { utils::safe_call_rpc_handler( diff --git a/crates/rpc/src/debug.rs b/crates/rpc/src/debug.rs index 00f5fee96..9439c6a49 100644 --- a/crates/rpc/src/debug.rs +++ b/crates/rpc/src/debug.rs @@ -11,9 +11,9 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. +use alloy_primitives::{Address, B256, U64}; use anyhow::Context; use async_trait::async_trait; -use ethers::types::{Address, H256}; use futures_util::StreamExt; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use rundler_types::{ @@ -48,7 +48,7 @@ pub trait DebugApi { /// /// Note that the bundling mode must be set to `Manual` else this will fail. #[method(name = "bundler_sendBundleNow")] - async fn bundler_send_bundle_now(&self) -> RpcResult; + async fn bundler_send_bundle_now(&self) -> RpcResult; /// Sets the bundling mode. #[method(name = "bundler_setBundlingMode")] @@ -103,8 +103,8 @@ impl DebugApi { #[async_trait] impl DebugApiServer for DebugApi where - P: Pool, - B: Builder, + P: Pool + 'static, + B: Builder + 'static, { async fn bundler_clear_state(&self) -> RpcResult { utils::safe_call_rpc_handler("bundler_clearState", DebugApi::bundler_clear_state(self)) @@ -127,7 +127,7 @@ where .await } - async fn bundler_send_bundle_now(&self) -> RpcResult { + async fn bundler_send_bundle_now(&self) -> RpcResult { utils::safe_call_rpc_handler( "bundler_sendBundleNow", DebugApi::bundler_send_bundle_now(self), @@ -235,7 +235,7 @@ where .collect::>()) } - async fn bundler_send_bundle_now(&self) -> InternalRpcResult { + async fn bundler_send_bundle_now(&self) -> InternalRpcResult { tracing::debug!("Sending bundle"); let mut new_heads = self @@ -318,8 +318,8 @@ where let reputation = RpcReputationOutput { address: r.address, - ops_seen: r.ops_seen.into(), - ops_included: r.ops_included.into(), + ops_seen: U64::from(r.ops_seen), + ops_included: U64::from(r.ops_included), status, }; @@ -344,8 +344,12 @@ where is_staked: result.is_staked, stake_info: RpcStakeInfo { addr: address, - stake: result.stake_info.stake.as_u128(), - unstake_delay_sec: result.stake_info.unstake_delay_sec.as_u32(), + stake: result + .stake_info + .stake + .try_into() + .context("stake should fit in u128")?, + unstake_delay_sec: result.stake_info.unstake_delay_sec, }, }) } diff --git a/crates/rpc/src/eth/api.rs b/crates/rpc/src/eth/api.rs index 1a2e4de38..688cd9305 100644 --- a/crates/rpc/src/eth/api.rs +++ b/crates/rpc/src/eth/api.rs @@ -13,11 +13,9 @@ use std::{future::Future, pin::Pin}; -use ethers::{ - types::{spoof, Address, H256, U64}, - utils::to_checksum, -}; +use alloy_primitives::{Address, B256, U64}; use futures_util::future; +use rundler_provider::StateOverride; use rundler_types::{ chain::ChainSpec, pool::Pool, UserOperation, UserOperationOptionalGas, UserOperationVariant, }; @@ -68,7 +66,7 @@ where &self, op: UserOperationVariant, entry_point: Address, - ) -> EthResult { + ) -> EthResult { let bundle_size = op.single_uo_bundle_size_bytes(); if bundle_size > self.chain_spec.max_transaction_size_bytes { return Err(EthRpcError::InvalidParams(format!( @@ -90,7 +88,7 @@ where &self, op: UserOperationOptionalGas, entry_point: Address, - state_override: Option, + state_override: Option, ) -> EthResult { let bundle_size = op.single_uo_bundle_size_bytes(); if bundle_size > self.chain_spec.max_transaction_size_bytes { @@ -107,9 +105,9 @@ where pub(crate) async fn get_user_operation_by_hash( &self, - hash: H256, + hash: B256, ) -> EthResult> { - if hash == H256::zero() { + if hash == B256::ZERO { return Err(EthRpcError::InvalidParams( "Missing/invalid userOpHash".to_string(), )); @@ -132,9 +130,9 @@ where pub(crate) async fn get_user_operation_receipt( &self, - hash: H256, + hash: B256, ) -> EthResult> { - if hash == H256::zero() { + if hash == B256::ZERO { return Err(EthRpcError::InvalidParams( "Missing/invalid userOpHash".to_string(), )); @@ -153,17 +151,17 @@ where Ok(self .router .entry_points() - .map(|ep| to_checksum(ep, None)) + .map(|ep| ep.to_checksum(None)) .collect()) } pub(crate) async fn chain_id(&self) -> EthResult { - Ok(self.chain_spec.id.into()) + Ok(U64::from(self.chain_spec.id)) } async fn get_pending_user_operation_by_hash( &self, - hash: H256, + hash: B256, ) -> EthResult> { let res = self .pool @@ -185,15 +183,13 @@ where mod tests { use std::sync::Arc; - use ethers::{ - abi::AbiEncode, - types::{Bytes, Log, Transaction}, - }; + use alloy_primitives::{Log as PrimitiveLog, LogData, U256}; + use alloy_sol_types::SolInterface; use mockall::predicate::eq; - use rundler_provider::{MockEntryPointV0_6, MockProvider}; + use rundler_contracts::v0_6::IEntryPoint::{handleOpsCall, IEntryPointCalls}; + use rundler_provider::{Log, MockEntryPointV0_6, MockEvmProvider, Transaction}; use rundler_sim::MockGasEstimator; use rundler_types::{ - contracts::v0_6::i_entry_point::{HandleOpsCall, IEntryPointCalls}, pool::{MockPool, PoolOperation}, v0_6::UserOperation, EntityInfos, UserOperation as UserOperationTrait, ValidTimeRange, @@ -215,8 +211,8 @@ mod tests { entry_point: ep, aggregator: None, valid_time_range: ValidTimeRange::default(), - expected_code_hash: H256::random(), - sim_block_hash: H256::random(), + expected_code_hash: B256::random(), + sim_block_hash: B256::random(), sim_block_number: 1000, account_is_staked: false, entity_infos: EntityInfos::default(), @@ -228,12 +224,12 @@ mod tests { .times(1) .returning(move |_| Ok(Some(po.clone()))); - let mut provider = MockProvider::default(); + let mut provider = MockEvmProvider::default(); provider.expect_get_logs().returning(move |_| Ok(vec![])); provider.expect_get_block_number().returning(|| Ok(1000)); let mut entry_point = MockEntryPointV0_6::default(); - entry_point.expect_address().returning(move || ep); + entry_point.expect_address().return_const(ep); let api = create_api(provider, entry_point, pool, MockGasEstimator::default()); let res = api.get_user_operation_by_hash(hash).await.unwrap(); @@ -257,33 +253,36 @@ mod tests { let uo = UserOperation::default(); let hash = uo.hash(ep, 1); let block_number = 1000; - let block_hash = H256::random(); + let block_hash = B256::random(); let mut pool = MockPool::default(); pool.expect_get_op_by_hash() .with(eq(hash)) .returning(move |_| Ok(None)); - let mut provider = MockProvider::default(); + let mut provider = MockEvmProvider::default(); provider.expect_get_block_number().returning(|| Ok(1000)); - let tx_data: Bytes = IEntryPointCalls::HandleOps(HandleOpsCall { - beneficiary: Address::zero(), - ops: vec![uo.clone()], + let tx_data = IEntryPointCalls::handleOps(handleOpsCall { + ops: vec![uo.clone().into()], + beneficiary: Address::ZERO, }) - .encode() - .into(); + .abi_encode(); + let tx = Transaction { to: Some(ep), - input: tx_data, - block_number: Some(block_number.into()), + input: tx_data.into(), + block_number: Some(block_number), block_hash: Some(block_hash), ..Default::default() }; - let tx_hash = tx.hash(); + let tx_hash = tx.hash; let log = Log { - address: ep, - transaction_hash: Some(tx_hash), + inner: PrimitiveLog { + address: ep, + data: LogData::default(), + }, + transaction_hash: Some(tx.hash), ..Default::default() }; @@ -291,19 +290,19 @@ mod tests { .expect_get_logs() .returning(move |_| Ok(vec![log.clone()])); provider - .expect_get_transaction() + .expect_get_transaction_by_hash() .with(eq(tx_hash)) .returning(move |_| Ok(Some(tx.clone()))); let mut entry_point = MockEntryPointV0_6::default(); - entry_point.expect_address().returning(move || ep); + entry_point.expect_address().return_const(ep); let api = create_api(provider, entry_point, pool, MockGasEstimator::default()); let res = api.get_user_operation_by_hash(hash).await.unwrap(); let ro = RpcUserOperationByHash { user_operation: UserOperationVariant::from(uo).into(), entry_point: ep.into(), - block_number: Some(block_number.into()), + block_number: Some(U256::from(block_number)), block_hash: Some(block_hash), transaction_hash: Some(tx_hash), }; @@ -322,12 +321,12 @@ mod tests { .times(1) .returning(move |_| Ok(None)); - let mut provider = MockProvider::default(); + let mut provider = MockEvmProvider::default(); provider.expect_get_logs().returning(move |_| Ok(vec![])); provider.expect_get_block_number().returning(|| Ok(1000)); let mut entry_point = MockEntryPointV0_6::default(); - entry_point.expect_address().returning(move || ep); + entry_point.expect_address().return_const(ep); let api = create_api(provider, entry_point, pool, MockGasEstimator::default()); let res = api.get_user_operation_by_hash(hash).await.unwrap(); @@ -335,7 +334,7 @@ mod tests { } fn create_api( - provider: MockProvider, + provider: MockEvmProvider, ep: MockEntryPointV0_6, pool: MockPool, gas_estimator: MockGasEstimator, diff --git a/crates/rpc/src/eth/error.rs b/crates/rpc/src/eth/error.rs index 183a139e9..2dc0fdcb4 100644 --- a/crates/rpc/src/eth/error.rs +++ b/crates/rpc/src/eth/error.rs @@ -13,7 +13,7 @@ use std::fmt::Display; -use ethers::types::{Address, Bytes, U256}; +use alloy_primitives::{Address, Bytes, U128, U256, U32}; use jsonrpsee::types::{ error::{CALL_EXECUTION_FAILED_CODE, INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE}, ErrorObjectOwned, @@ -161,7 +161,7 @@ pub struct StakeTooLowData { accessed_entity: Option, slot: U256, minimum_stake: U256, - minimum_unstake_delay: U256, + minimum_unstake_delay: U32, } impl StakeTooLowData { @@ -172,7 +172,7 @@ impl StakeTooLowData { accessed_entity: Option, slot: U256, minimum_stake: U256, - minimum_unstake_delay: U256, + minimum_unstake_delay: U32, ) -> Self { Self { needs_stake, @@ -228,6 +228,11 @@ impl From for ValidationRevertData { inner_reason: None, revert_data: Some(data), }, + ValidationRevert::Panic(data) => Self { + reason: Some(format!("evm panicked: {}", data.code)), + inner_reason: None, + revert_data: None, + }, } } } @@ -235,15 +240,15 @@ impl From for ValidationRevertData { #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub struct ReplacementUnderpricedData { - pub current_max_priority_fee: U256, - pub current_max_fee: U256, + pub current_max_priority_fee: U128, + pub current_max_fee: U128, } impl ReplacementUnderpricedData { - pub fn new(current_max_priority_fee: U256, current_max_fee: U256) -> Self { + pub fn new(current_max_priority_fee: u128, current_max_fee: u128) -> Self { Self { - current_max_priority_fee, - current_max_fee, + current_max_priority_fee: U128::from(current_max_priority_fee), + current_max_fee: U128::from(current_max_fee), } } } @@ -277,10 +282,7 @@ impl From for EthRpcError { MempoolError::Other(e) => Self::Internal(e), MempoolError::OperationAlreadyKnown => Self::OperationAlreadyKnown, MempoolError::ReplacementUnderpriced(priority_fee, fee) => { - Self::ReplacementUnderpriced(ReplacementUnderpricedData { - current_max_priority_fee: priority_fee, - current_max_fee: fee, - }) + Self::ReplacementUnderpriced(ReplacementUnderpricedData::new(priority_fee, fee)) } MempoolError::MaxOperationsReached(count, address) => { Self::MaxOperationsReached(count, address) @@ -361,7 +363,7 @@ impl From for EthRpcError { stake_data.accessed_entity, stake_data.slot, stake_data.min_stake, - stake_data.min_unstake_delay, + U32::from(stake_data.min_unstake_delay), ))) } SimulationViolation::AggregatorValidationFailed => Self::SignatureCheckFailed, diff --git a/crates/rpc/src/eth/events/common.rs b/crates/rpc/src/eth/events/common.rs index 336634693..c5a15a41f 100644 --- a/crates/rpc/src/eth/events/common.rs +++ b/crates/rpc/src/eth/events/common.rs @@ -11,19 +11,17 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use std::{collections::VecDeque, marker::PhantomData, sync::Arc}; +use std::{collections::VecDeque, marker::PhantomData}; +use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_sol_types::SolEvent; use anyhow::Context; -use ethers::{ - prelude::EthEvent, - types::{ - Address, Bytes, Filter, GethDebugBuiltInTracerType, GethDebugTracerType, - GethDebugTracingOptions, GethTrace, GethTraceFrame, Log, TransactionReceipt, H256, U256, - }, +use rundler_provider::{ + EvmProvider, Filter, GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, + GethTrace, Log, TransactionReceipt, }; -use rundler_provider::Provider; use rundler_types::{chain::ChainSpec, UserOperation, UserOperationVariant}; -use rundler_utils::{eth, log::LogOnError}; +use rundler_utils::log::LogOnError; use super::UserOperationEventProvider; use crate::types::{RpcUserOperationByHash, RpcUserOperationReceipt}; @@ -31,19 +29,19 @@ use crate::types::{RpcUserOperationByHash, RpcUserOperationReceipt}; #[derive(Debug)] pub(crate) struct UserOperationEventProviderImpl { chain_spec: ChainSpec, - provider: Arc

, + provider: P, event_block_distance: Option, _f_type: PhantomData, } -pub(crate) trait EntryPointFilters: Send + Sync + 'static { +pub(crate) trait EntryPointEvents: Send + Sync { type UO: UserOperation + Into; - type UserOperationEventFilter: EthEvent; - type UserOperationRevertReasonFilter: EthEvent; + type UserOperationEvent: SolEvent; + type UserOperationRevertReason: SolEvent; fn construct_receipt( - event: Self::UserOperationEventFilter, - hash: H256, + event: Self::UserOperationEvent, + hash: B256, entry_point: Address, logs: Vec, tx_receipt: TransactionReceipt, @@ -55,14 +53,14 @@ pub(crate) trait EntryPointFilters: Send + Sync + 'static { } #[async_trait::async_trait] -impl UserOperationEventProvider for UserOperationEventProviderImpl +impl UserOperationEventProvider for UserOperationEventProviderImpl where - P: Provider, - F: EntryPointFilters, + P: EvmProvider, + E: EntryPointEvents, { async fn get_mined_by_hash( &self, - hash: H256, + hash: B256, ) -> anyhow::Result> { // Get event associated with hash (need to check all entry point addresses associated with this API) let event = self @@ -79,7 +77,7 @@ where let tx = self .provider - .get_transaction(transaction_hash) + .get_transaction_by_hash(transaction_hash) .await .context("should have fetched tx from provider")? .context("should have found tx")?; @@ -93,8 +91,8 @@ where .context("tx.to should be present on transaction containing user operation event")?; // Find first op matching the hash - let user_operation = if F::address(&self.chain_spec) == to { - F::get_user_operations_from_tx_data(tx.input, &self.chain_spec) + let user_operation = if E::address(&self.chain_spec) == to { + E::get_user_operations_from_tx_data(tx.input, &self.chain_spec) .into_iter() .find(|op| op.hash(to, self.chain_spec.id) == hash) .context("matching user operation should be found in tx data")? @@ -107,25 +105,21 @@ where Ok(Some(RpcUserOperationByHash { user_operation: user_operation.into().into(), - entry_point: event.address.into(), - block_number: Some( - tx.block_number - .map(|n| U256::from(n.as_u64())) - .unwrap_or_default(), - ), + entry_point: event.address().into(), + block_number: Some(tx.block_number.map(|n| U256::from(n)).unwrap_or_default()), block_hash: Some(tx.block_hash.unwrap_or_default()), transaction_hash: Some(transaction_hash), })) } - async fn get_receipt(&self, hash: H256) -> anyhow::Result> { + async fn get_receipt(&self, hash: B256) -> anyhow::Result> { let event = self .get_event_by_hash(hash) .await .log_on_error("should have successfully queried for user op events by hash")?; let Some(event) = event else { return Ok(None) }; - let entry_point = event.address; + let entry_point = event.address(); let tx_hash = event .transaction_hash @@ -148,7 +142,7 @@ where .decode_user_operation_event(event) .context("should have decoded user operation event")?; - Ok(Some(F::construct_receipt( + Ok(Some(E::construct_receipt( uo_event, hash, entry_point, @@ -158,14 +152,14 @@ where } } -impl UserOperationEventProviderImpl +impl UserOperationEventProviderImpl where - P: Provider, - F: EntryPointFilters, + P: EvmProvider, + E: EntryPointEvents, { pub(crate) fn new( chain_spec: ChainSpec, - provider: Arc

, + provider: P, event_block_distance: Option, ) -> Self { Self { @@ -176,7 +170,7 @@ where } } - async fn get_event_by_hash(&self, hash: H256) -> anyhow::Result> { + async fn get_event_by_hash(&self, hash: B256) -> anyhow::Result> { let to_block = self.provider.get_block_number().await?; let from_block = match self.event_block_distance { @@ -185,8 +179,8 @@ where }; let filter = Filter::new() - .address(F::address(&self.chain_spec)) - .event(&F::UserOperationEventFilter::abi_signature()) + .address(E::address(&self.chain_spec)) + .event_signature(E::UserOperationEvent::SIGNATURE_HASH) .from_block(from_block) .to_block(to_block) .topic1(hash); @@ -195,8 +189,9 @@ where Ok(logs.into_iter().next()) } - fn decode_user_operation_event(&self, log: Log) -> anyhow::Result { - F::UserOperationEventFilter::decode_log(ð::log_to_raw_log(log)) + fn decode_user_operation_event(&self, log: Log) -> anyhow::Result { + log.log_decode::() + .map(|l| l.inner.data) .context("log should be a user operation event") } @@ -206,9 +201,9 @@ where /// and returning the user operation that matches the hash. async fn trace_find_user_operation( &self, - tx_hash: H256, - user_op_hash: H256, - ) -> anyhow::Result> { + tx_hash: B256, + user_op_hash: B256, + ) -> anyhow::Result> { // initial call wasn't to an entrypoint, so we need to trace the transaction to find the user operation let trace_options = GethDebugTracingOptions { tracer: Some(GethDebugTracerType::BuiltInTracer( @@ -225,7 +220,7 @@ where // breadth first search for the user operation in the trace let mut frame_queue = VecDeque::new(); - if let GethTrace::Known(GethTraceFrame::CallTracer(call_frame)) = trace { + if let GethTrace::CallTracer(call_frame) = trace { frame_queue.push_back(call_frame); } @@ -233,20 +228,18 @@ where // check if the call is to an entrypoint, if not enqueue the child calls if any if let Some(to) = call_frame .to - .as_ref() - .and_then(|to| to.as_address()) - .filter(|to| **to == F::address(&self.chain_spec)) + .filter(|to| *to == E::address(&self.chain_spec)) { // check if the user operation is in the call frame if let Some(uo) = - F::get_user_operations_from_tx_data(call_frame.input, &self.chain_spec) + E::get_user_operations_from_tx_data(call_frame.input, &self.chain_spec) .into_iter() - .find(|op| op.hash(*to, self.chain_spec.id) == user_op_hash) + .find(|op| op.hash(to, self.chain_spec.id) == user_op_hash) { return Ok(Some(uo)); } - } else if let Some(calls) = call_frame.calls { - frame_queue.extend(calls) + } else { + frame_queue.extend(call_frame.calls) } } diff --git a/crates/rpc/src/eth/events/mod.rs b/crates/rpc/src/eth/events/mod.rs index eba1f184f..0355992cd 100644 --- a/crates/rpc/src/eth/events/mod.rs +++ b/crates/rpc/src/eth/events/mod.rs @@ -11,8 +11,9 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. +use alloy_primitives::B256; use anyhow::bail; -use ethers::types::{Log, TransactionReceipt, H256}; +use rundler_provider::{Log, TransactionReceipt}; use crate::types::{RpcUserOperationByHash, RpcUserOperationReceipt}; @@ -24,11 +25,11 @@ mod v0_7; pub(crate) use v0_7::UserOperationEventProviderV0_7; #[async_trait::async_trait] -pub(crate) trait UserOperationEventProvider: Send + Sync + 'static { - async fn get_mined_by_hash(&self, hash: H256) +pub(crate) trait UserOperationEventProvider: Send + Sync { + async fn get_mined_by_hash(&self, hash: B256) -> anyhow::Result>; - async fn get_receipt(&self, hash: H256) -> anyhow::Result>; + async fn get_receipt(&self, hash: B256) -> anyhow::Result>; } // This method takes a user operation event and a transaction receipt and filters out all the logs @@ -47,17 +48,20 @@ fn filter_receipt_logs_matching_user_op( reference_log: &Log, tx_receipt: &TransactionReceipt, ) -> anyhow::Result> { + let logs = tx_receipt.inner.logs(); + let mut start_idx = 0; - let mut end_idx = tx_receipt.logs.len() - 1; - let logs = &tx_receipt.logs; + let mut end_idx = logs.len() - 1; + + // TODO protect against topic of zero size let is_ref_user_op = |log: &Log| { - log.topics[0] == reference_log.topics[0] - && log.topics[1] == reference_log.topics[1] - && log.address == reference_log.address + log.topics()[0] == reference_log.topics()[0] + && log.topics()[1] == reference_log.topics()[1] + && log.address() == reference_log.address() }; - let is_user_op_event = |log: &Log| log.topics[0] == reference_log.topics[0]; + let is_user_op_event = |log: &Log| log.topics()[0] == reference_log.topics()[0]; let mut i = 0; while i < logs.len() { @@ -81,7 +85,8 @@ fn filter_receipt_logs_matching_user_op( #[cfg(test)] mod tests { - use ethers::{types::Address, utils::keccak256}; + use alloy_primitives::{address, utils::keccak256, Address, Log as PrimitiveLog, LogData}; + use rundler_provider::{TransactionReceiptEnvelope, TransactionReceiptWithBloom}; use super::*; @@ -101,7 +106,7 @@ mod tests { assert!(result.is_ok(), "{}", result.unwrap_err()); let result = result.unwrap(); - assert_eq!(result, receipt.logs[0..=1]); + assert_eq!(result, receipt.inner.logs()[0..=1]); } #[test] @@ -120,7 +125,7 @@ mod tests { assert!(result.is_ok(), "{}", result.unwrap_err()); let result = result.unwrap(); - assert_eq!(result, receipt.logs[2..=4]); + assert_eq!(result, receipt.inner.logs()[2..=4]); } #[test] @@ -139,14 +144,15 @@ mod tests { assert!(result.is_ok(), "{}", result.unwrap_err()); let result = result.unwrap(); - assert_eq!(result, receipt.logs[3..=5]); + assert_eq!(result, receipt.inner.logs()[3..=5]); } #[test] fn test_filter_receipt_logs_skips_event_from_different_address() { let reference_log = given_log(UO_OP_TOPIC, "moldy-hash"); let mut reference_log_w_different_address = reference_log.clone(); - reference_log_w_different_address.address = Address::from_low_u64_be(0x1234); + reference_log_w_different_address.inner.address = + address!("0000000000000000000000000000000000001234"); let receipt = given_receipt(vec![ given_log("other-topic", "some-hash"), @@ -162,7 +168,7 @@ mod tests { assert!(result.is_ok(), "{}", result.unwrap_err()); let result = result.unwrap(); - assert_eq!(result, receipt.logs[4..=6]); + assert_eq!(result, receipt.inner.logs()[4..=6]); } #[test] @@ -184,7 +190,7 @@ mod tests { assert!(result.is_ok(), "{}", result.unwrap_err()); let result = result.unwrap(); - assert_eq!(result, receipt.logs[2..=6]); + assert_eq!(result, receipt.inner.logs()[2..=6]); } #[test] @@ -204,19 +210,44 @@ mod tests { } fn given_log(topic_0: &str, topic_1: &str) -> Log { + let mut log_data = LogData::default(); + log_data.set_topics_unchecked(vec![ + keccak256(topic_0.as_bytes()), + keccak256(topic_1.as_bytes()), + ]); + Log { - topics: vec![ - keccak256(topic_0.as_bytes()).into(), - keccak256(topic_1.as_bytes()).into(), - ], + inner: PrimitiveLog { + address: Address::ZERO, + data: log_data, + }, ..Default::default() } } fn given_receipt(logs: Vec) -> TransactionReceipt { - TransactionReceipt { + let receipt = alloy_consensus::Receipt { logs, ..Default::default() + }; + TransactionReceipt { + inner: TransactionReceiptEnvelope::Legacy(TransactionReceiptWithBloom { + receipt, + ..Default::default() + }), + transaction_hash: B256::ZERO, + transaction_index: None, + block_hash: None, + block_number: None, + gas_used: 0, + effective_gas_price: 0, + blob_gas_used: None, + blob_gas_price: None, + from: Address::ZERO, + to: None, + contract_address: None, + state_root: None, + authorization_list: None, } } } diff --git a/crates/rpc/src/eth/events/v0_6.rs b/crates/rpc/src/eth/events/v0_6.rs index ff09d985e..ca4702f1f 100644 --- a/crates/rpc/src/eth/events/v0_6.rs +++ b/crates/rpc/src/eth/events/v0_6.rs @@ -11,20 +11,15 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use ethers::{ - abi::{AbiDecode, RawLog}, - prelude::EthEvent, - types::{Address, Bytes, Log, TransactionReceipt, H256}, -}; -use rundler_types::{ - chain::ChainSpec, - contracts::v0_6::i_entry_point::{ - IEntryPointCalls, UserOperationEventFilter, UserOperationRevertReasonFilter, - }, - v0_6::UserOperation, +use alloy_primitives::{ruint::UintTryFrom, Address, Bytes, B256, U128}; +use alloy_sol_types::SolInterface; +use rundler_contracts::v0_6::IEntryPoint::{ + IEntryPointCalls, UserOperationEvent, UserOperationRevertReason, }; +use rundler_provider::{Log, TransactionReceipt}; +use rundler_types::{chain::ChainSpec, v0_6::UserOperation}; -use super::common::{EntryPointFilters, UserOperationEventProviderImpl}; +use super::common::{EntryPointEvents, UserOperationEventProviderImpl}; use crate::types::RpcUserOperationReceipt; pub(crate) type UserOperationEventProviderV0_6

= @@ -32,14 +27,14 @@ pub(crate) type UserOperationEventProviderV0_6

= pub(crate) struct EntryPointFiltersV0_6; -impl EntryPointFilters for EntryPointFiltersV0_6 { +impl EntryPointEvents for EntryPointFiltersV0_6 { type UO = UserOperation; - type UserOperationEventFilter = UserOperationEventFilter; - type UserOperationRevertReasonFilter = UserOperationRevertReasonFilter; + type UserOperationEvent = UserOperationEvent; + type UserOperationRevertReason = UserOperationRevertReason; fn construct_receipt( - event: Self::UserOperationEventFilter, - hash: H256, + event: Self::UserOperationEvent, + hash: B256, entry_point: Address, logs: Vec, tx_receipt: TransactionReceipt, @@ -48,20 +43,18 @@ impl EntryPointFilters for EntryPointFiltersV0_6 { let reason: String = if event.success { "".to_owned() } else { - let revert_reason_evt: Option = logs + let revert_reason_evt: Option = logs .iter() - .filter(|l| l.topics.len() > 1 && l.topics[1] == hash) + .filter(|l| l.topics().len() > 1 && l.topics()[1] == hash) .map_while(|l| { - Self::UserOperationRevertReasonFilter::decode_log(&RawLog { - topics: l.topics.clone(), - data: l.data.to_vec(), - }) - .ok() + l.log_decode::() + .map(|l| l.inner.data) + .ok() }) .next(); revert_reason_evt - .map(|r| r.revert_reason.to_string()) + .map(|r| r.revertReason.to_string()) .unwrap_or_default() }; @@ -71,8 +64,8 @@ impl EntryPointFilters for EntryPointFiltersV0_6 { sender: event.sender.into(), nonce: event.nonce, paymaster: event.paymaster.into(), - actual_gas_cost: event.actual_gas_cost, - actual_gas_used: event.actual_gas_used, + actual_gas_cost: event.actualGasCost, + actual_gas_used: U128::uint_try_from(event.actualGasUsed).unwrap_or(U128::MAX), success: event.success, logs, receipt: tx_receipt, @@ -81,18 +74,22 @@ impl EntryPointFilters for EntryPointFiltersV0_6 { } fn get_user_operations_from_tx_data(tx_data: Bytes, _chain_spec: &ChainSpec) -> Vec { - let entry_point_calls = match IEntryPointCalls::decode(tx_data) { + let entry_point_calls = match IEntryPointCalls::abi_decode(&tx_data, false) { Ok(entry_point_calls) => entry_point_calls, Err(_) => return vec![], }; match entry_point_calls { - IEntryPointCalls::HandleOps(handle_ops_call) => handle_ops_call.ops, - IEntryPointCalls::HandleAggregatedOps(handle_aggregated_ops_call) => { + IEntryPointCalls::handleOps(handle_ops_call) => handle_ops_call + .ops + .into_iter() + .filter_map(|op| op.try_into().ok()) + .collect(), + IEntryPointCalls::handleAggregatedOps(handle_aggregated_ops_call) => { handle_aggregated_ops_call - .ops_per_aggregator + .opsPerAggregator .into_iter() - .flat_map(|ops| ops.user_ops) + .flat_map(|ops| ops.userOps.into_iter().filter_map(|op| op.try_into().ok())) .collect() } _ => vec![], diff --git a/crates/rpc/src/eth/events/v0_7.rs b/crates/rpc/src/eth/events/v0_7.rs index b43d5cf1b..50a0ee18c 100644 --- a/crates/rpc/src/eth/events/v0_7.rs +++ b/crates/rpc/src/eth/events/v0_7.rs @@ -11,20 +11,18 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use ethers::{ - abi::{AbiDecode, RawLog}, - prelude::EthEvent, - types::{Address, Bytes, Log, TransactionReceipt, H256}, +use alloy_primitives::{ruint::UintTryFrom, Address, Bytes, B256, U128}; +use alloy_sol_types::SolInterface; +use rundler_contracts::v0_7::IEntryPoint::{ + IEntryPointCalls, UserOperationEvent, UserOperationRevertReason, }; +use rundler_provider::{Log, TransactionReceipt}; use rundler_types::{ chain::ChainSpec, - contracts::v0_7::i_entry_point::{ - IEntryPointCalls, UserOperationEventFilter, UserOperationRevertReasonFilter, - }, - v0_7::UserOperation, + v0_7::{self, UserOperation}, }; -use super::common::{EntryPointFilters, UserOperationEventProviderImpl}; +use super::common::{EntryPointEvents, UserOperationEventProviderImpl}; use crate::types::RpcUserOperationReceipt; pub(crate) type UserOperationEventProviderV0_7

= @@ -32,14 +30,14 @@ pub(crate) type UserOperationEventProviderV0_7

= pub(crate) struct EntryPointFiltersV0_7; -impl EntryPointFilters for EntryPointFiltersV0_7 { +impl EntryPointEvents for EntryPointFiltersV0_7 { type UO = UserOperation; - type UserOperationEventFilter = UserOperationEventFilter; - type UserOperationRevertReasonFilter = UserOperationRevertReasonFilter; + type UserOperationEvent = UserOperationEvent; + type UserOperationRevertReason = UserOperationRevertReason; fn construct_receipt( - event: Self::UserOperationEventFilter, - hash: H256, + event: Self::UserOperationEvent, + hash: B256, entry_point: Address, logs: Vec, tx_receipt: TransactionReceipt, @@ -48,20 +46,18 @@ impl EntryPointFilters for EntryPointFiltersV0_7 { let reason: String = if event.success { "".to_owned() } else { - let revert_reason_evt: Option = logs + let revert_reason_evt: Option = logs .iter() - .filter(|l| l.topics.len() > 1 && l.topics[1] == hash) + .filter(|l| l.topics().len() > 1 && l.topics()[1] == hash) .map_while(|l| { - Self::UserOperationRevertReasonFilter::decode_log(&RawLog { - topics: l.topics.clone(), - data: l.data.to_vec(), - }) - .ok() + l.log_decode::() + .map(|l| l.inner.data) + .ok() }) .next(); revert_reason_evt - .map(|r| r.revert_reason.to_string()) + .map(|r| r.revertReason.to_string()) .unwrap_or_default() }; @@ -71,8 +67,8 @@ impl EntryPointFilters for EntryPointFiltersV0_7 { sender: event.sender.into(), nonce: event.nonce, paymaster: event.paymaster.into(), - actual_gas_cost: event.actual_gas_cost, - actual_gas_used: event.actual_gas_used, + actual_gas_cost: event.actualGasCost, + actual_gas_used: U128::uint_try_from(event.actualGasUsed).unwrap_or(U128::MAX), success: event.success, logs, receipt: tx_receipt, @@ -81,22 +77,26 @@ impl EntryPointFilters for EntryPointFiltersV0_7 { } fn get_user_operations_from_tx_data(tx_data: Bytes, chain_spec: &ChainSpec) -> Vec { - let entry_point_calls = match IEntryPointCalls::decode(tx_data) { + let entry_point_calls = match IEntryPointCalls::abi_decode(&tx_data, false) { Ok(entry_point_calls) => entry_point_calls, Err(_) => return vec![], }; match entry_point_calls { - IEntryPointCalls::HandleOps(handle_ops_call) => handle_ops_call + IEntryPointCalls::handleOps(handle_ops_call) => handle_ops_call .ops .into_iter() - .map(|op| op.unpack(chain_spec)) + .filter_map(|op| v0_7::unpack_user_operation(op, chain_spec).ok()) .collect(), - IEntryPointCalls::HandleAggregatedOps(handle_aggregated_ops_call) => { + IEntryPointCalls::handleAggregatedOps(handle_aggregated_ops_call) => { handle_aggregated_ops_call - .ops_per_aggregator + .opsPerAggregator .into_iter() - .flat_map(|ops| ops.user_ops.into_iter().map(|op| op.unpack(chain_spec))) + .flat_map(|ops| { + ops.userOps + .into_iter() + .filter_map(|op| v0_7::unpack_user_operation(op, chain_spec).ok()) + }) .collect() } _ => vec![], diff --git a/crates/rpc/src/eth/mod.rs b/crates/rpc/src/eth/mod.rs index 82151fcf9..295ca6bff 100644 --- a/crates/rpc/src/eth/mod.rs +++ b/crates/rpc/src/eth/mod.rs @@ -24,8 +24,9 @@ mod events; pub(crate) use events::{UserOperationEventProviderV0_6, UserOperationEventProviderV0_7}; mod server; -use ethers::types::{spoof, Address, H256, U64}; +use alloy_primitives::{Address, B256, U64}; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use rundler_provider::StateOverride; use crate::types::{ RpcGasEstimate, RpcUserOperation, RpcUserOperationByHash, RpcUserOperationOptionalGas, @@ -42,7 +43,7 @@ pub trait EthApi { &self, op: RpcUserOperation, entry_point: Address, - ) -> RpcResult; + ) -> RpcResult; /// Estimates the gas fields for a user operation. #[method(name = "estimateUserOperationGas")] @@ -50,21 +51,21 @@ pub trait EthApi { &self, op: RpcUserOperationOptionalGas, entry_point: Address, - state_override: Option, + state_override: Option, ) -> RpcResult; /// Returns the user operation with the given hash. #[method(name = "getUserOperationByHash")] async fn get_user_operation_by_hash( &self, - hash: H256, + hash: B256, ) -> RpcResult>; /// Returns the user operation receipt with the given hash. #[method(name = "getUserOperationReceipt")] async fn get_user_operation_receipt( &self, - hash: H256, + hash: B256, ) -> RpcResult>; /// Returns the supported entry points addresses diff --git a/crates/rpc/src/eth/router.rs b/crates/rpc/src/eth/router.rs index 051a953ff..f164b26fa 100644 --- a/crates/rpc/src/eth/router.rs +++ b/crates/rpc/src/eth/router.rs @@ -13,8 +13,8 @@ use std::{fmt::Debug, marker::PhantomData, sync::Arc}; -use ethers::types::{spoof, Address, H256}; -use rundler_provider::{EntryPoint, SimulationProvider}; +use alloy_primitives::{Address, B256}; +use rundler_provider::{EntryPoint, SimulationProvider, StateOverride}; use rundler_sim::{GasEstimationError, GasEstimator}; use rundler_types::{ EntryPointVersion, GasEstimate, UserOperation, UserOperationOptionalGas, UserOperationVariant, @@ -39,7 +39,7 @@ pub(crate) struct EntryPointRouterBuilder { impl EntryPointRouterBuilder { pub(crate) fn v0_6(mut self, route: R) -> Self where - R: EntryPointRoute, + R: EntryPointRoute + 'static, { if route.version() != EntryPointVersion::V0_6 { panic!( @@ -55,7 +55,7 @@ impl EntryPointRouterBuilder { pub(crate) fn v0_7(mut self, route: R) -> Self where - R: EntryPointRoute, + R: EntryPointRoute + 'static, { if route.version() != EntryPointVersion::V0_7 { panic!( @@ -121,7 +121,7 @@ impl EntryPointRouter { pub(crate) async fn get_mined_by_hash( &self, entry_point: &Address, - hash: H256, + hash: B256, ) -> EthResult> { self.get_route(entry_point)? .get_mined_by_hash(hash) @@ -132,7 +132,7 @@ impl EntryPointRouter { pub(crate) async fn get_receipt( &self, entry_point: &Address, - hash: H256, + hash: B256, ) -> EthResult> { self.get_route(entry_point)? .get_receipt(hash) @@ -144,7 +144,7 @@ impl EntryPointRouter { &self, entry_point: &Address, uo: UserOperationOptionalGas, - state_override: Option, + state_override: Option, ) -> EthResult { match self.get_ep_version(entry_point)? { EntryPointVersion::V0_6 => { @@ -191,7 +191,7 @@ impl EntryPointRouter { &self, entry_point: &Address, uo: UserOperationVariant, - max_verification_gas: u64, + max_verification_gas: u128, ) -> EthResult { self.check_and_get_route(entry_point, &uo)? .check_signature(uo, max_verification_gas) @@ -229,26 +229,26 @@ impl EntryPointRouter { } #[async_trait::async_trait] -pub(crate) trait EntryPointRoute: Send + Sync + 'static { +pub(crate) trait EntryPointRoute: Send + Sync { fn version(&self) -> EntryPointVersion; fn address(&self) -> Address; - async fn get_mined_by_hash(&self, hash: H256) + async fn get_mined_by_hash(&self, hash: B256) -> anyhow::Result>; - async fn get_receipt(&self, hash: H256) -> anyhow::Result>; + async fn get_receipt(&self, hash: B256) -> anyhow::Result>; async fn estimate_gas( &self, uo: UserOperationOptionalGas, - state_override: Option, + state_override: Option, ) -> Result; async fn check_signature( &self, uo: UserOperationVariant, - max_verification_gas: u64, + max_verification_gas: u128, ) -> anyhow::Result; } @@ -274,24 +274,24 @@ where } fn address(&self) -> Address { - self.entry_point.address() + *self.entry_point.address() } async fn get_mined_by_hash( &self, - hash: H256, + hash: B256, ) -> anyhow::Result> { self.event_provider.get_mined_by_hash(hash).await } - async fn get_receipt(&self, hash: H256) -> anyhow::Result> { + async fn get_receipt(&self, hash: B256) -> anyhow::Result> { self.event_provider.get_receipt(hash).await } async fn estimate_gas( &self, uo: UserOperationOptionalGas, - state_override: Option, + state_override: Option, ) -> Result { self.gas_estimator .estimate_op_gas(uo.into(), state_override.unwrap_or_default()) @@ -301,12 +301,12 @@ where async fn check_signature( &self, uo: UserOperationVariant, - max_verification_gas: u64, + max_verification_gas: u128, ) -> anyhow::Result { let output = self .entry_point - .call_simulate_validation(uo.into(), max_verification_gas, None) - .await?; + .simulate_validation(uo.into(), max_verification_gas, None) + .await??; Ok(!output.return_info.account_sig_failed) } diff --git a/crates/rpc/src/eth/server.rs b/crates/rpc/src/eth/server.rs index 8e485a924..1f5ed83bf 100644 --- a/crates/rpc/src/eth/server.rs +++ b/crates/rpc/src/eth/server.rs @@ -11,8 +11,9 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use ethers::types::{spoof, Address, H256, U64}; +use alloy_primitives::{Address, B256, U64}; use jsonrpsee::core::RpcResult; +use rundler_provider::StateOverride; use rundler_types::{pool::Pool, UserOperationVariant}; use super::{api::EthApi, EthApiServer}; @@ -27,13 +28,13 @@ use crate::{ #[async_trait::async_trait] impl

EthApiServer for EthApi

where - P: Pool, + P: Pool + 'static, { async fn send_user_operation( &self, op: RpcUserOperation, entry_point: Address, - ) -> RpcResult { + ) -> RpcResult { utils::safe_call_rpc_handler( "eth_sendUserOperation", EthApi::send_user_operation( @@ -49,7 +50,7 @@ where &self, op: RpcUserOperationOptionalGas, entry_point: Address, - state_override: Option, + state_override: Option, ) -> RpcResult { utils::safe_call_rpc_handler( "eth_estimateUserOperationGas", @@ -60,7 +61,7 @@ where async fn get_user_operation_by_hash( &self, - hash: H256, + hash: B256, ) -> RpcResult> { utils::safe_call_rpc_handler( "eth_getUserOperationByHash", @@ -71,7 +72,7 @@ where async fn get_user_operation_receipt( &self, - hash: H256, + hash: B256, ) -> RpcResult> { utils::safe_call_rpc_handler( "eth_getUserOperationReceipt", diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index cadd2ed75..4c983e400 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -31,7 +31,6 @@ mod eth; pub use eth::{EthApiClient, EthApiSettings}; mod health; -mod rpc_metrics; mod rundler; pub use rundler::{RundlerApiClient, Settings as RundlerApiSettings}; diff --git a/crates/rpc/src/rundler.rs b/crates/rpc/src/rundler.rs index d61967f5a..3ec8717d5 100644 --- a/crates/rpc/src/rundler.rs +++ b/crates/rpc/src/rundler.rs @@ -11,13 +11,10 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use std::sync::Arc; - +use alloy_primitives::{Address, B256, U128}; use anyhow::Context; use async_trait::async_trait; -use ethers::types::{Address, H256, U256}; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use rundler_provider::Provider; use rundler_sim::{gas, FeeEstimator}; use rundler_types::{chain::ChainSpec, pool::Pool, UserOperation, UserOperationVariant}; @@ -34,16 +31,16 @@ pub struct Settings { pub priority_fee_mode: gas::PriorityFeeMode, /// If using a bundle priority fee, the percentage to add to the network/oracle /// provided value as a safety margin for fast inclusion. - pub bundle_priority_fee_overhead_percent: u64, + pub bundle_priority_fee_overhead_percent: u32, /// Max verification gas - pub max_verification_gas: u64, + pub max_verification_gas: u128, } #[rpc(client, server, namespace = "rundler")] pub trait RundlerApi { /// Returns the maximum priority fee per gas required by Rundler #[method(name = "maxPriorityFeePerGas")] - async fn max_priority_fee_per_gas(&self) -> RpcResult; + async fn max_priority_fee_per_gas(&self) -> RpcResult; /// Drops a user operation from the local mempool. /// @@ -58,24 +55,24 @@ pub trait RundlerApi { &self, uo: RpcUserOperation, entry_point: Address, - ) -> RpcResult>; + ) -> RpcResult>; } -pub(crate) struct RundlerApi { +pub(crate) struct RundlerApi { chain_spec: ChainSpec, settings: Settings, - fee_estimator: FeeEstimator

, - pool_server: PL, + fee_estimator: F, + pool_server: P, entry_point_router: EntryPointRouter, } #[async_trait] -impl RundlerApiServer for RundlerApi +impl RundlerApiServer for RundlerApi where - P: Provider, - PL: Pool, + P: Pool + 'static, + F: FeeEstimator + 'static, { - async fn max_priority_fee_per_gas(&self) -> RpcResult { + async fn max_priority_fee_per_gas(&self) -> RpcResult { utils::safe_call_rpc_handler( "rundler_maxPriorityFeePerGas", RundlerApi::max_priority_fee_per_gas(self), @@ -87,7 +84,7 @@ where &self, user_op: RpcUserOperation, entry_point: Address, - ) -> RpcResult> { + ) -> RpcResult> { utils::safe_call_rpc_handler( "rundler_dropLocalUserOperation", RundlerApi::drop_local_user_operation(self, user_op, entry_point), @@ -96,56 +93,52 @@ where } } -impl RundlerApi +impl RundlerApi where - P: Provider, - PL: Pool, + P: Pool, + F: FeeEstimator, { pub(crate) fn new( chain_spec: &ChainSpec, - provider: Arc

, entry_point_router: EntryPointRouter, - pool_server: PL, + pool_server: P, + fee_estimator: F, settings: Settings, ) -> Self { Self { chain_spec: chain_spec.clone(), settings, - fee_estimator: FeeEstimator::new( - chain_spec, - provider, - settings.priority_fee_mode, - settings.bundle_priority_fee_overhead_percent, - ), entry_point_router, pool_server, + fee_estimator, } } - async fn max_priority_fee_per_gas(&self) -> EthResult { + async fn max_priority_fee_per_gas(&self) -> EthResult { let (bundle_fees, _) = self .fee_estimator .required_bundle_fees(None) .await .context("should get required fees")?; - Ok(self - .fee_estimator - .required_op_fees(bundle_fees) - .max_priority_fee_per_gas) + Ok(U128::from( + self.fee_estimator + .required_op_fees(bundle_fees) + .max_priority_fee_per_gas, + )) } async fn drop_local_user_operation( &self, user_op: RpcUserOperation, entry_point: Address, - ) -> EthResult> { + ) -> EthResult> { let uo = UserOperationVariant::from_rpc(user_op, &self.chain_spec); let id = uo.id(); - if uo.pre_verification_gas() != U256::zero() - || uo.call_gas_limit() != U256::zero() + if uo.pre_verification_gas() != 0 + || uo.call_gas_limit() != 0 || uo.call_data().len() != 0 - || uo.max_fee_per_gas() != U256::zero() + || uo.max_fee_per_gas() != 0 { Err(EthRpcError::InvalidParams("Invalid user operation for drop: preVerificationGas, callGasLimit, callData, and maxFeePerGas must be zero".to_string()))?; } diff --git a/crates/rpc/src/task.rs b/crates/rpc/src/task.rs index bdf2df382..de5c2ba2c 100644 --- a/crates/rpc/src/task.rs +++ b/crates/rpc/src/task.rs @@ -16,16 +16,15 @@ use std::{net::SocketAddr, sync::Arc, time::Duration}; use anyhow::{bail, Context}; use async_trait::async_trait; use jsonrpsee::{ - server::{middleware::http::ProxyGetRequestLayer, RpcServiceBuilder, ServerBuilder}, - types::Request, + server::{middleware::http::ProxyGetRequestLayer, ServerBuilder}, RpcModule, }; -use rundler_provider::{EntryPointProvider, Provider}; +use rundler_provider::{EntryPointProvider, EvmProvider}; use rundler_sim::{ + gas::{self, FeeEstimatorImpl, FeeOracle}, EstimationSettings, FeeEstimator, GasEstimatorV0_6, GasEstimatorV0_7, PrecheckSettings, }; use rundler_task::{ - metrics::MetricsLayer, server::{format_socket_addr, HealthCheck}, Task, }; @@ -44,7 +43,6 @@ use crate::{ EthApiSettings, UserOperationEventProviderV0_6, UserOperationEventProviderV0_7, }, health::{HealthChecker, SystemApiServer}, - rpc_metrics::RPCMethodExtractor, rundler::{RundlerApi, RundlerApiServer, Settings as RundlerApiSettings}, types::ApiNamespace, }; @@ -88,7 +86,7 @@ pub struct RpcTask { args: Args, pool: P, builder: B, - provider: Arc, + provider: PR, ep_06: Option, ep_07: Option, } @@ -96,17 +94,34 @@ pub struct RpcTask { #[async_trait] impl Task for RpcTask where - P: Pool + HealthCheck + Clone, - B: Builder + HealthCheck + Clone, - PR: Provider, - E06: EntryPointProvider, - E07: EntryPointProvider, + P: Pool + HealthCheck + Clone + 'static, + B: Builder + HealthCheck + Clone + 'static, + PR: EvmProvider + Clone + 'static, + E06: EntryPointProvider + Clone + 'static, + E07: EntryPointProvider + Clone + 'static, { + fn boxed(self) -> Box { + Box::new(self) + } + async fn run(mut self: Box, shutdown_token: CancellationToken) -> anyhow::Result<()> { let addr: SocketAddr = format_socket_addr(&self.args.host, self.args.port).parse()?; tracing::info!("Starting rpc server on {}", addr); let mut router_builder = EntryPointRouterBuilder::default(); + let fee_oracle = Arc::::from(gas::get_fee_oracle( + &self.args.chain_spec, + self.provider.clone(), + )); + let fee_estimator = FeeEstimatorImpl::new( + self.provider.clone(), + fee_oracle, + self.args.precheck_settings.priority_fee_mode, + self.args + .precheck_settings + .bundle_priority_fee_overhead_percent, + ); + if self.args.entry_point_v0_6_enabled { let ep = self .ep_06 @@ -117,21 +132,14 @@ where ep.clone(), GasEstimatorV0_6::new( self.args.chain_spec.clone(), - Arc::clone(&self.provider), + self.provider.clone(), ep.clone(), self.args.estimation_settings, - FeeEstimator::new( - &self.args.chain_spec, - Arc::clone(&self.provider), - self.args.precheck_settings.priority_fee_mode, - self.args - .precheck_settings - .bundle_priority_fee_overhead_percent, - ), + fee_estimator.clone(), ), UserOperationEventProviderV0_6::new( self.args.chain_spec.clone(), - Arc::clone(&self.provider), + self.provider.clone(), self.args .eth_api_settings .user_operation_event_block_distance, @@ -149,21 +157,14 @@ where ep.clone(), GasEstimatorV0_7::new( self.args.chain_spec.clone(), - Arc::clone(&self.provider), + self.provider.clone(), ep.clone(), self.args.estimation_settings, - FeeEstimator::new( - &self.args.chain_spec, - Arc::clone(&self.provider), - self.args.precheck_settings.priority_fee_mode, - self.args - .precheck_settings - .bundle_priority_fee_overhead_percent, - ), + fee_estimator.clone(), ), UserOperationEventProviderV0_7::new( self.args.chain_spec.clone(), - Arc::clone(&self.provider), + self.provider.clone(), self.args .eth_api_settings .user_operation_event_block_distance, @@ -175,7 +176,7 @@ where let router = router_builder.build(); let mut module = RpcModule::new(()); - self.attach_namespaces(router, &mut module)?; + self.attach_namespaces(router, fee_estimator, &mut module)?; let servers: Vec> = vec![Box::new(self.pool.clone()), Box::new(self.builder.clone())]; @@ -188,14 +189,14 @@ where .layer(ProxyGetRequestLayer::new("/health", "system_health")?) .timeout(self.args.rpc_timeout); - let rpc_metric_middleware = MetricsLayer::>::new( - "rundler-eth-service".to_string(), - "rpc".to_string(), - ); + // TODO: add metrics + // let rpc_metric_middleware = MetricsLayer::>::new( + // "rundler-eth-service".to_string(), + // "rpc".to_string(), + // ); let server = ServerBuilder::default() .set_http_middleware(http_middleware) - .set_rpc_middleware(rpc_metric_middleware) .max_connections(self.args.max_connections) // Set max request body size to 2x the max transaction size as none of our // APIs should require more than that. @@ -231,7 +232,7 @@ impl RpcTask { args: Args, pool: P, builder: B, - provider: Arc, + provider: PR, ep_06: Option, ep_07: Option, ) -> Self { @@ -250,18 +251,14 @@ impl RpcTask where P: Pool + HealthCheck + Clone, B: Builder + HealthCheck + Clone, - PR: Provider, + PR: EvmProvider, E06: EntryPointProvider, E07: EntryPointProvider, { - /// Converts the task into a boxed trait object. - pub fn boxed(self) -> Box { - Box::new(self) - } - - fn attach_namespaces( + fn attach_namespaces( &self, entry_point_router: EntryPointRouter, + fee_estimator: F, module: &mut RpcModule<()>, ) -> anyhow::Result<()> { if self.args.api_namespaces.contains(&ApiNamespace::Eth) { @@ -287,9 +284,9 @@ where module.merge( RundlerApi::new( &self.args.chain_spec, - Arc::clone(&self.provider), entry_point_router, self.pool.clone(), + fee_estimator, self.args.rundler_api_settings, ) .into_rpc(), diff --git a/crates/rpc/src/types/mod.rs b/crates/rpc/src/types/mod.rs index 293503807..ac6472361 100644 --- a/crates/rpc/src/types/mod.rs +++ b/crates/rpc/src/types/mod.rs @@ -11,10 +11,8 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use ethers::{ - types::{Address, Log, TransactionReceipt, H160, H256, U256}, - utils::to_checksum, -}; +use alloy_primitives::{Address, B256, U128, U256, U64}; +use rundler_provider::{Log, TransactionReceipt}; use rundler_types::{ chain::ChainSpec, pool::{Reputation, ReputationStatus}, @@ -51,14 +49,14 @@ pub(crate) trait FromRpc { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct RpcAddress(H160); +pub struct RpcAddress(Address); impl Serialize for RpcAddress { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - serializer.serialize_str(&to_checksum(&self.0, None)) + serializer.serialize_str(&self.0.to_checksum(None)) } } @@ -140,9 +138,9 @@ pub(crate) struct RpcUserOperationByHash { /// The number of the block this operation was included in pub(crate) block_number: Option, /// The hash of the block this operation was included in - pub(crate) block_hash: Option, + pub(crate) block_hash: Option, /// The hash of the transaction this operation was included in - pub(crate) transaction_hash: Option, + pub(crate) transaction_hash: Option, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -185,7 +183,7 @@ impl From for RpcGasEstimate { #[serde(rename_all = "camelCase")] pub struct RpcUserOperationReceipt { /// The hash of the user operation - pub user_op_hash: H256, + pub user_op_hash: B256, /// The entry point address this operation was sent to pub entry_point: RpcAddress, /// The sender of this user operation @@ -197,7 +195,7 @@ pub struct RpcUserOperationReceipt { /// The gas cost of this operation pub actual_gas_cost: U256, /// The gas used by this operation - pub actual_gas_used: U256, + pub actual_gas_used: U128, /// Whether this operation's execution was successful pub success: bool, /// If not successful, the revert reason string @@ -215,9 +213,9 @@ pub struct RpcReputationInput { /// Entity address pub address: Address, /// Number of operations seen in this interval - pub ops_seen: U256, + pub ops_seen: U64, /// Number of operations included in this interval - pub ops_included: U256, + pub ops_included: U64, } /// Reputation of an entity @@ -227,9 +225,9 @@ pub struct RpcReputationOutput { /// Entity address pub address: Address, /// Number of operations seen in this interval - pub ops_seen: U256, + pub ops_seen: U64, /// Number of operations included in this interval - pub ops_included: U256, + pub ops_included: U64, /// Reputation status pub status: ReputationStatus, } @@ -238,8 +236,8 @@ impl From for Reputation { fn from(rpc_reputation: RpcReputationInput) -> Self { Reputation { address: rpc_reputation.address, - ops_seen: rpc_reputation.ops_seen.as_u64(), - ops_included: rpc_reputation.ops_included.as_u64(), + ops_seen: rpc_reputation.ops_seen.to(), + ops_included: rpc_reputation.ops_included.to(), } } } @@ -250,8 +248,8 @@ impl TryFrom for RpcReputationInput { fn try_from(reputation: Reputation) -> Result { Ok(RpcReputationInput { address: reputation.address, - ops_seen: reputation.ops_seen.into(), - ops_included: reputation.ops_included.into(), + ops_seen: U64::from(reputation.ops_seen), + ops_included: U64::from(reputation.ops_included), }) } } diff --git a/crates/rpc/src/types/v0_6.rs b/crates/rpc/src/types/v0_6.rs index 68fa7f834..597d1d537 100644 --- a/crates/rpc/src/types/v0_6.rs +++ b/crates/rpc/src/types/v0_6.rs @@ -11,7 +11,7 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use ethers::types::{Address, Bytes, U256}; +use alloy_primitives::{Address, Bytes, U128, U256}; use rundler_types::{ chain::ChainSpec, v0_6::{UserOperation, UserOperationOptionalGas}, @@ -29,11 +29,11 @@ pub(crate) struct RpcUserOperation { nonce: U256, init_code: Bytes, call_data: Bytes, - call_gas_limit: U256, - verification_gas_limit: U256, - pre_verification_gas: U256, - max_fee_per_gas: U256, - max_priority_fee_per_gas: U256, + call_gas_limit: U128, + verification_gas_limit: U128, + pre_verification_gas: U128, + max_fee_per_gas: U128, + max_priority_fee_per_gas: U128, paymaster_and_data: Bytes, signature: Bytes, } @@ -45,11 +45,11 @@ impl From for RpcUserOperation { nonce: op.nonce, init_code: op.init_code, call_data: op.call_data, - call_gas_limit: op.call_gas_limit, - verification_gas_limit: op.verification_gas_limit, - pre_verification_gas: op.pre_verification_gas, - max_fee_per_gas: op.max_fee_per_gas, - max_priority_fee_per_gas: op.max_priority_fee_per_gas, + call_gas_limit: U128::from(op.call_gas_limit), + verification_gas_limit: U128::from(op.verification_gas_limit), + pre_verification_gas: U128::from(op.pre_verification_gas), + max_fee_per_gas: U128::from(op.max_fee_per_gas), + max_priority_fee_per_gas: U128::from(op.max_priority_fee_per_gas), paymaster_and_data: op.paymaster_and_data, signature: op.signature, } @@ -63,11 +63,11 @@ impl FromRpc for UserOperation { nonce: def.nonce, init_code: def.init_code, call_data: def.call_data, - call_gas_limit: def.call_gas_limit, - verification_gas_limit: def.verification_gas_limit, - pre_verification_gas: def.pre_verification_gas, - max_fee_per_gas: def.max_fee_per_gas, - max_priority_fee_per_gas: def.max_priority_fee_per_gas, + call_gas_limit: def.call_gas_limit.to(), + verification_gas_limit: def.verification_gas_limit.to(), + pre_verification_gas: def.pre_verification_gas.to(), + max_fee_per_gas: def.max_fee_per_gas.to(), + max_priority_fee_per_gas: def.max_priority_fee_per_gas.to(), paymaster_and_data: def.paymaster_and_data, signature: def.signature, } @@ -81,11 +81,11 @@ pub(crate) struct RpcUserOperationOptionalGas { nonce: U256, init_code: Bytes, call_data: Bytes, - call_gas_limit: Option, - verification_gas_limit: Option, - pre_verification_gas: Option, - max_fee_per_gas: Option, - max_priority_fee_per_gas: Option, + call_gas_limit: Option, + verification_gas_limit: Option, + pre_verification_gas: Option, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, paymaster_and_data: Bytes, signature: Bytes, } @@ -97,11 +97,11 @@ impl From for UserOperationOptionalGas { nonce: def.nonce, init_code: def.init_code, call_data: def.call_data, - call_gas_limit: def.call_gas_limit, - verification_gas_limit: def.verification_gas_limit, - pre_verification_gas: def.pre_verification_gas, - max_fee_per_gas: def.max_fee_per_gas, - max_priority_fee_per_gas: def.max_priority_fee_per_gas, + call_gas_limit: def.call_gas_limit.map(|x| x.to()), + verification_gas_limit: def.verification_gas_limit.map(|x| x.to()), + pre_verification_gas: def.pre_verification_gas.map(|x| x.to()), + max_fee_per_gas: def.max_fee_per_gas.map(|x| x.to()), + max_priority_fee_per_gas: def.max_priority_fee_per_gas.map(|x| x.to()), paymaster_and_data: def.paymaster_and_data, signature: def.signature, } @@ -111,17 +111,17 @@ impl From for UserOperationOptionalGas { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub(crate) struct RpcGasEstimate { - pre_verification_gas: U256, - call_gas_limit: U256, - verification_gas_limit: U256, + pre_verification_gas: U128, + call_gas_limit: U128, + verification_gas_limit: U128, } impl From for RpcGasEstimate { fn from(estimate: GasEstimate) -> Self { RpcGasEstimate { - pre_verification_gas: estimate.pre_verification_gas, - call_gas_limit: estimate.call_gas_limit, - verification_gas_limit: estimate.verification_gas_limit, + pre_verification_gas: U128::from(estimate.pre_verification_gas), + call_gas_limit: U128::from(estimate.call_gas_limit), + verification_gas_limit: U128::from(estimate.verification_gas_limit), } } } diff --git a/crates/rpc/src/types/v0_7.rs b/crates/rpc/src/types/v0_7.rs index ccd9ced6c..bf2270e84 100644 --- a/crates/rpc/src/types/v0_7.rs +++ b/crates/rpc/src/types/v0_7.rs @@ -11,7 +11,7 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use ethers::types::{Address, Bytes, H256, U128, U256}; +use alloy_primitives::{Address, Bytes, B256, U128, U256}; use rundler_types::{ chain::ChainSpec, v0_7::{ @@ -72,16 +72,17 @@ impl From for RpcUserOperation { sender: op.sender, nonce: op.nonce, call_data: op.call_data, - call_gas_limit: op.call_gas_limit, - verification_gas_limit: op.verification_gas_limit, - pre_verification_gas: op.pre_verification_gas, - max_priority_fee_per_gas: op.max_priority_fee_per_gas, - max_fee_per_gas: op.max_fee_per_gas, + call_gas_limit: U128::from(op.call_gas_limit), + verification_gas_limit: U128::from(op.verification_gas_limit), + pre_verification_gas: U256::from(op.pre_verification_gas), + max_priority_fee_per_gas: U128::from(op.max_priority_fee_per_gas), + max_fee_per_gas: U128::from(op.max_fee_per_gas), factory: op.factory, factory_data, paymaster: op.paymaster, - paymaster_verification_gas_limit, - paymaster_post_op_gas_limit, + paymaster_verification_gas_limit: paymaster_verification_gas_limit + .map(|x| U128::from(x)), + paymaster_post_op_gas_limit: paymaster_post_op_gas_limit.map(|x| U128::from(x)), paymaster_data, signature: op.signature, } @@ -96,19 +97,23 @@ impl FromRpc for UserOperation { sender: def.sender, nonce: def.nonce, call_data: def.call_data, - call_gas_limit: def.call_gas_limit, - verification_gas_limit: def.verification_gas_limit, - pre_verification_gas: def.pre_verification_gas, - max_priority_fee_per_gas: def.max_priority_fee_per_gas, - max_fee_per_gas: def.max_fee_per_gas, + call_gas_limit: def.call_gas_limit.to(), + verification_gas_limit: def.verification_gas_limit.to(), + pre_verification_gas: def.pre_verification_gas.to(), + max_priority_fee_per_gas: def.max_priority_fee_per_gas.to(), + max_fee_per_gas: def.max_fee_per_gas.to(), signature: def.signature, }, ); if def.paymaster.is_some() { builder = builder.paymaster( def.paymaster.unwrap(), - def.paymaster_verification_gas_limit.unwrap_or_default(), - def.paymaster_post_op_gas_limit.unwrap_or_default(), + def.paymaster_verification_gas_limit + .map(|x| x.to()) + .unwrap_or_default(), + def.paymaster_post_op_gas_limit + .map(|x| x.to()) + .unwrap_or_default(), def.paymaster_data.unwrap_or_default(), ); } @@ -127,8 +132,8 @@ pub(crate) struct RpcUserOperationByHash { user_operation: RpcUserOperation, entry_point: RpcAddress, block_number: Option, - block_hash: Option, - transaction_hash: Option, + block_hash: Option, + transaction_hash: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -157,16 +162,16 @@ impl From for UserOperationOptionalGas { sender: def.sender, nonce: def.nonce, call_data: def.call_data, - call_gas_limit: def.call_gas_limit, - verification_gas_limit: def.verification_gas_limit, - pre_verification_gas: def.pre_verification_gas, - max_priority_fee_per_gas: def.max_priority_fee_per_gas, - max_fee_per_gas: def.max_fee_per_gas, + call_gas_limit: def.call_gas_limit.map(|x| x.to()), + verification_gas_limit: def.verification_gas_limit.map(|x| x.to()), + pre_verification_gas: def.pre_verification_gas.map(|x| x.to()), + max_priority_fee_per_gas: def.max_priority_fee_per_gas.map(|x| x.to()), + max_fee_per_gas: def.max_fee_per_gas.map(|x| x.to()), factory: def.factory, factory_data: def.factory_data.unwrap_or_default(), paymaster: def.paymaster, - paymaster_verification_gas_limit: def.paymaster_verification_gas_limit, - paymaster_post_op_gas_limit: def.paymaster_post_op_gas_limit, + paymaster_verification_gas_limit: def.paymaster_verification_gas_limit.map(|x| x.to()), + paymaster_post_op_gas_limit: def.paymaster_post_op_gas_limit.map(|x| x.to()), paymaster_data: def.paymaster_data.unwrap_or_default(), signature: def.signature, } @@ -176,19 +181,21 @@ impl From for UserOperationOptionalGas { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub(crate) struct RpcGasEstimate { - pre_verification_gas: U256, - call_gas_limit: U256, - verification_gas_limit: U256, - paymaster_verification_gas_limit: Option, + pre_verification_gas: U128, + call_gas_limit: U128, + verification_gas_limit: U128, + paymaster_verification_gas_limit: Option, } impl From for RpcGasEstimate { fn from(estimate: GasEstimate) -> Self { RpcGasEstimate { - pre_verification_gas: estimate.pre_verification_gas, - call_gas_limit: estimate.call_gas_limit, - verification_gas_limit: estimate.verification_gas_limit, - paymaster_verification_gas_limit: estimate.paymaster_verification_gas_limit, + pre_verification_gas: U128::from(estimate.pre_verification_gas), + call_gas_limit: U128::from(estimate.call_gas_limit), + verification_gas_limit: U128::from(estimate.verification_gas_limit), + paymaster_verification_gas_limit: estimate + .paymaster_verification_gas_limit + .map(|x| U128::from(x)), } } }