From 857170df13e623d1293ba3f5f92086631fa7d29d Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Tue, 27 Aug 2024 18:12:25 +0400 Subject: [PATCH] feat(user-ops-indexer): support custom entry point addresses (#1032) * feat(user-ops-indexer): support custom entry point addresses * chore(user-ops-indexer): update readme --- user-ops-indexer/README.md | 34 +-- .../src/indexer/base_indexer.rs | 131 +++++------ .../src/indexer/rpc_utils.rs | 6 +- .../src/indexer/settings.rs | 25 ++- .../src/indexer/v06/mod.rs | 166 +++++++------- .../src/indexer/v07/mod.rs | 203 +++++++++--------- .../src/types/account.rs | 6 +- .../src/types/bundle.rs | 6 +- .../src/types/bundler.rs | 3 +- .../src/types/factory.rs | 3 +- .../src/types/paymaster.rs | 3 +- .../src/types/user_op.rs | 5 +- .../user-ops-indexer-server/src/indexer.rs | 37 +++- 13 files changed, 345 insertions(+), 283 deletions(-) diff --git a/user-ops-indexer/README.md b/user-ops-indexer/README.md index b5923dae4..ff68d389e 100644 --- a/user-ops-indexer/README.md +++ b/user-ops-indexer/README.md @@ -56,22 +56,24 @@ cargo run --bin user-ops-indexer-server Here, we describe variables specific to this service. Variables common to all services can be found [here](../docs/common-envs.md). -| Variable | Required | Description | Default value | -|-----------------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------| -| `USER_OPS_INDEXER__API__MAX_PAGE_SIZE` | | Max page size for API requests | `100` | -| `USER_OPS_INDEXER__INDEXER__RPC_URL` | true | Indexer RPC URL, should be an archive JSON RPC node with `eth`, `web3` and `trace`/`debug` namespaces enabled. Both HTTP and WS protocols are supported. WS is recommended for local RPC nodes, use HTTP otherwise. | `ws://127.0.0.1:8546` | -| `USER_OPS_INDEXER__INDEXER__CONCURRENCY` | | Indexer concurrency. Will process up to the configured number of transactions concurrently | `10` | -| `USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V06` | | Enable Entrypoint v0.6 indexer | `true` | -| `USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V07` | | Enable Entrypoint v0.7 indexer | `true` | -| `USER_OPS_INDEXER__INDEXER__REALTIME__ENABLED` | | Enable forward realtime indexing of user operations from the `latest` block | `true` | -| `USER_OPS_INDEXER__INDEXER__PAST_RPC_LOGS_INDEXER__ENABLED` | | Enable one-time reindex of missed user operations from recent blocks | `false` | -| `USER_OPS_INDEXER__INDEXER__PAST_RPC_LOGS_INDEXER__BLOCK_RANGE` | | Block range width for missed user operations reindex. Will re-index events from a given number of blocks prior the `latest` block | `0` | -| `USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__ENABLED` | | Enable one-time reindex of missed user operations from core Blockscout DB. Will query relevant events from `logs` Postgres table | `false` | -| `USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__START_BLOCK` | | Block range start for one-time DB reindex. Use positive number for static block number, or zero/negative number to count backwards from `latest` | `0` | -| `USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__END_BLOCK` | | Block range end for one-time DB reindex. Use positive number for static block number, or zero/negative number to count backwards from `latest` | `0` | -| `USER_OPS_INDEXER__DATABASE__CONNECT__URL` | true | Postgres connect URL to Blockscout DB with read/write access | (empty) | -| `USER_OPS_INDEXER__DATABASE__CREATE_DATABASE` | | Create database if doesn't exist | `false` | -| `USER_OPS_INDEXER__DATABASE__RUN_MIGRATIONS` | | Run database migrations | `false` | +| Variable | Required | Description | Default value | +|-----------------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------| +| `USER_OPS_INDEXER__API__MAX_PAGE_SIZE` | | Max page size for API requests | `100` | +| `USER_OPS_INDEXER__INDEXER__RPC_URL` | true | Indexer RPC URL, should be an archive JSON RPC node with `eth`, `web3` and `trace`/`debug` namespaces enabled. Both HTTP and WS protocols are supported. WS is recommended for local RPC nodes, use HTTP otherwise. | `ws://127.0.0.1:8546` | +| `USER_OPS_INDEXER__INDEXER__CONCURRENCY` | | Indexer concurrency. Will process up to the configured number of transactions concurrently | `10` | +| `USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V06` | | Enable Entrypoint v0.6 indexer | `true` | +| `USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V06_ENTRY_POINT` | | Entrypoint v0.6 contract address | `0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789` | +| `USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V07` | | Enable Entrypoint v0.7 indexer | `true` | +| `USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V07_ENTRY_POINT` | | Entrypoint v0.7 contract address | `0x0000000071727De22E5E9d8BAf0edAc6f37da032` | +| `USER_OPS_INDEXER__INDEXER__REALTIME__ENABLED` | | Enable forward realtime indexing of user operations from the `latest` block | `true` | +| `USER_OPS_INDEXER__INDEXER__PAST_RPC_LOGS_INDEXER__ENABLED` | | Enable one-time reindex of missed user operations from recent blocks | `false` | +| `USER_OPS_INDEXER__INDEXER__PAST_RPC_LOGS_INDEXER__BLOCK_RANGE` | | Block range width for missed user operations reindex. Will re-index events from a given number of blocks prior the `latest` block | `0` | +| `USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__ENABLED` | | Enable one-time reindex of missed user operations from core Blockscout DB. Will query relevant events from `logs` Postgres table | `false` | +| `USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__START_BLOCK` | | Block range start for one-time DB reindex. Use positive number for static block number, or zero/negative number to count backwards from `latest` | `0` | +| `USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__END_BLOCK` | | Block range end for one-time DB reindex. Use positive number for static block number, or zero/negative number to count backwards from `latest` | `0` | +| `USER_OPS_INDEXER__DATABASE__CONNECT__URL` | true | Postgres connect URL to Blockscout DB with read/write access | (empty) | +| `USER_OPS_INDEXER__DATABASE__CREATE_DATABASE` | | Create database if doesn't exist | `false` | +| `USER_OPS_INDEXER__DATABASE__RUN_MIGRATIONS` | | Run database migrations | `false` | ## Links diff --git a/user-ops-indexer/user-ops-indexer-logic/src/indexer/base_indexer.rs b/user-ops-indexer/user-ops-indexer-logic/src/indexer/base_indexer.rs index bc38b134a..48392569d 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/indexer/base_indexer.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/indexer/base_indexer.rs @@ -60,7 +60,7 @@ impl TryFrom for Job { } pub trait IndexerLogic { - fn entry_point() -> Address; + fn entry_point(&self) -> Address; fn version() -> &'static str; fn user_operation_event_signature() -> H256; @@ -70,58 +70,63 @@ pub trait IndexerLogic { fn matches_handler_calldata(calldata: &Bytes) -> bool; fn parse_user_ops( + &self, receipt: &TransactionReceipt, bundle_index: usize, calldata: &Bytes, log_bundle: &[&[Log]], ) -> anyhow::Result>; - fn user_operation_event_matcher(log: &Log) -> bool { - log.address == Self::entry_point() + fn user_operation_event_matcher(&self, log: &Log) -> bool { + log.address == self.entry_point() && log.topics.first() == Some(&Self::user_operation_event_signature()) } - fn before_execution_matcher(log: &Log) -> bool { - log.address == Self::entry_point() + fn before_execution_matcher(&self, log: &Log) -> bool { + log.address == self.entry_point() && log.topics.first() == Some(&Self::before_execution_signature()) } - fn match_and_parse(log: &Log) -> Option> { - if log.address == Self::entry_point() && log.topics.first() == Some(&T::signature()) { + fn match_and_parse(&self, log: &Log) -> Option> { + if log.address == self.entry_point() && log.topics.first() == Some(&T::signature()) { Some(parse_log::(log.clone())) } else { None } } - fn base_tx_logs_filter() -> Filter { + fn base_tx_logs_filter(&self) -> Filter { Filter::new() - .address(Self::entry_point()) + .address(self.entry_point()) .topic0(Self::before_execution_signature()) } } -pub struct Indexer { +pub struct Indexer { client: Provider, db: Arc, settings: IndexerSettings, + + logic: L, } -impl Indexer { +impl Indexer { pub fn new( client: Provider, db: Arc, settings: IndexerSettings, + logic: L, ) -> Self { Self { client, db, settings, + logic, } } #[instrument(name = "indexer", skip_all, level = "info", fields(version = L::version()))] - pub async fn start(&self) -> anyhow::Result<()> { + pub async fn start(&self) -> anyhow::Result<()> { tracing::debug!("fetching node client"); let variant = self.client.node_client().await.unwrap_or(NodeClient::Geth); tracing::info!(variant = to_string(variant), "fetched node client"); @@ -134,7 +139,7 @@ impl Indexer { tracing::info!("subscribing to BeforeExecution logs from rpc"); let realtime_stream_jobs = self .client - .subscribe_logs(&L::base_tx_logs_filter()) + .subscribe_logs(&self.logic.base_tx_logs_filter()) .await? .filter_map(|log| async { Job::try_from(log).ok() }); @@ -147,7 +152,7 @@ impl Indexer { stream_jobs.push(Box::pin(realtime_stream_jobs)); } else { tracing::info!("starting polling of past BeforeExecution logs from rpc"); - stream_jobs.push(Box::pin(self.poll_for_jobs::())); + stream_jobs.push(Box::pin(self.poll_for_jobs())); } } @@ -173,7 +178,7 @@ impl Indexer { tracing::info!(from_block, to_block, "fetching missed tx hashes in db"); let missed_txs = repository::user_op::stream_unprocessed_logs_tx_hashes( &self.db, - L::entry_point(), + self.logic.entry_point(), L::user_operation_event_signature(), from_block, to_block, @@ -185,7 +190,7 @@ impl Indexer { if self.settings.past_rpc_logs_indexer.enabled { let jobs = self - .fetch_jobs_for_block_range::(rpc_refetch_block_number + 1, block_number) + .fetch_jobs_for_block_range(rpc_refetch_block_number + 1, block_number) .await?; stream_jobs.push(Box::pin(stream::iter(jobs))); @@ -213,7 +218,7 @@ impl Indexer { stream_txs .for_each_concurrent(Some(self.settings.concurrency as usize), |tx| async move { let mut backoff = vec![5, 20, 120].into_iter().map(Duration::from_secs); - while let Err(err) = &self.handle_tx::(tx, variant).await { + while let Err(err) = &self.handle_tx(tx, variant).await { match backoff.next() { None => { tracing::error!(error = ?err, tx_hash = ?tx, "tx handler failed, skipping"); @@ -231,12 +236,14 @@ impl Indexer { Ok(()) } - async fn fetch_jobs_for_block_range( + async fn fetch_jobs_for_block_range( &self, from_block: u32, to_block: u32, ) -> Result, ProviderError> { - let filter = L::base_tx_logs_filter() + let filter = self + .logic + .base_tx_logs_filter() .from_block(from_block) .to_block(to_block); @@ -257,7 +264,7 @@ impl Indexer { Ok(jobs) } - fn poll_for_jobs(&self) -> impl Stream + '_ { + fn poll_for_jobs(&self) -> impl Stream + '_ { repeat_with(|| async { sleep(self.settings.realtime.polling_interval).await; tracing::debug!("fetching latest block number"); @@ -267,7 +274,7 @@ impl Indexer { let from_block = block_number.saturating_sub(self.settings.realtime.polling_block_range); let jobs = self - .fetch_jobs_for_block_range::(from_block, block_number) + .fetch_jobs_for_block_range(from_block, block_number) .await?; Ok::, ProviderError>(jobs) @@ -281,11 +288,7 @@ impl Indexer { } #[instrument(name = "indexer::handle_tx", skip(self, variant), level = "info")] - async fn handle_tx( - &self, - tx_hash: H256, - variant: NodeClient, - ) -> anyhow::Result<()> { + async fn handle_tx(&self, tx_hash: H256, variant: NodeClient) -> anyhow::Result<()> { let tx = self .client .get_transaction(tx_hash) @@ -302,38 +305,42 @@ impl Indexer { // then we split each bundle into logs batches for respective user operations let log_bundles: Vec> = receipt .logs - .split(L::before_execution_matcher) + .split(|log| self.logic.before_execution_matcher(log)) .skip(1) .map(|logs| { - logs.split_inclusive(L::user_operation_event_matcher) - .filter(|logs| logs.last().is_some_and(L::user_operation_event_matcher)) + logs.split_inclusive(|log| self.logic.user_operation_event_matcher(log)) + .filter(|logs| { + logs.last() + .is_some_and(|log| self.logic.user_operation_event_matcher(log)) + }) .collect() }) .collect(); tracing::info!(bundles_count = log_bundles.len(), "found user op bundles"); - let calldatas: Vec = if log_bundles.len() == 1 && tx.to == Some(L::entry_point()) { - vec![tx.input] - } else { - tracing::info!( - "tx contains more than one bundle or was sent indirectly, fetching tx trace" - ); - self.client - .common_trace_transaction(tx_hash, variant) - .await? - .into_iter() - .filter_map(|t| { - if t.typ == TraceType::Call - && t.to == Some(L::entry_point()) - && L::matches_handler_calldata(&t.input) - { - Some(t.input) - } else { - None - } - }) - .collect() - }; + let calldatas: Vec = + if log_bundles.len() == 1 && tx.to == Some(self.logic.entry_point()) { + vec![tx.input] + } else { + tracing::info!( + "tx contains more than one bundle or was sent indirectly, fetching tx trace" + ); + self.client + .common_trace_transaction(tx_hash, variant) + .await? + .into_iter() + .filter_map(|t| { + if t.typ == TraceType::Call + && t.to == Some(self.logic.entry_point()) + && L::matches_handler_calldata(&t.input) + { + Some(t.input) + } else { + None + } + }) + .collect() + }; if calldatas.len() != log_bundles.len() { bail!( @@ -348,7 +355,9 @@ impl Indexer { .iter() .zip(log_bundles.iter()) .enumerate() - .map(|(i, (calldata, log_bundle))| L::parse_user_ops(&receipt, i, calldata, log_bundle)) + .map(|(i, (calldata, log_bundle))| { + self.logic.parse_user_ops(&receipt, i, calldata, log_bundle) + }) .filter_map(|b| { // user ops parsing logic won't be retried, since we don't propagate the error here b.map_err(|err| tracing::error!(error = ?err, "failed to parse user ops")) @@ -396,6 +405,7 @@ mod tests { .unwrap(); let tx: Transaction = serde_json::from_str(r#"{"accessList":[],"blockHash":"0xe90aa1d6038c87b029a0666148ac2058ab8397f9c53594cc5a38c0113a48eab4","blockNumber":"0x11e7bd0","chainId":"0x1","from":"0x2df993cd76bb8dbda50546eef00eee2e6331a2c8","gas":"0x64633","gasPrice":"0x8b539dcf3","hash":"0xf9f60f6dc99663c6ce4912ef92fe6a122bb90585e47b5f213efca1705be26d6e","input":"0x1fad948c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000002df993cd76bb8dbda50546eef00eee2e6331a2c800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000eae4d85f7733ad522f601ce7ad4f595704a2d67700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000169b7000000000000000000000000000000000000000000000000000000000001546d000000000000000000000000000000000000000000000000000000000000c2ec0000000000000000000000000000000000000000000000000000000c88385240000000000000000000000000000000000000000000000000000000001dcd650000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e470641a22000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001e0049783f008a0085193e00003d00cd54003c71ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000062000000000000000000000000000000000000000000000000000000000065793a092c25c7a7c5e4bc46467324e2845caf1ccae767786e07806ca720f8a6b83356bc7d43a63a96b34507cfe7c424db37f351d71851ae9318e8d5c3d9f17c8bdb744c1c000000000000000000000000000000000000000000000000000000000000","maxFeePerGas":"0xc88385240","maxPriorityFeePerGas":"0x1dcd6500","nonce":"0x143","r":"0x1c2b5eb48f71d803de3557309428decfa63f639a97ab98ab6b52667b9c415aa0","s":"0x54a110c5f7db8ce7a488080249d3eab77e426300cd36b78cf82156ded86b26ee","to":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","transactionIndex":"0x63","type":"0x2","v":"0x0","value":"0x0","yParity":"0x0"}"#).unwrap(); let receipt: TransactionReceipt = serde_json::from_str(r#"{"blockHash":"0xe90aa1d6038c87b029a0666148ac2058ab8397f9c53594cc5a38c0113a48eab4","blockNumber":"0x11e7bd0","contractAddress":null,"cumulativeGasUsed":"0xca9e14","effectiveGasPrice":"0x8b539dcf3","from":"0x2df993cd76bb8dbda50546eef00eee2e6331a2c8","gasUsed":"0x27a21","logs":[{"address":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","blockHash":"0xe90aa1d6038c87b029a0666148ac2058ab8397f9c53594cc5a38c0113a48eab4","blockNumber":"0x11e7bd0","data":"0x000000000000000000000000000000000000000000000000002bea15dbb76400","logIndex":"0x10a","removed":false,"topics":["0x2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4","0x000000000000000000000000eae4d85f7733ad522f601ce7ad4f595704a2d677"],"transactionHash":"0xf9f60f6dc99663c6ce4912ef92fe6a122bb90585e47b5f213efca1705be26d6e","transactionIndex":"0x63"},{"address":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","blockHash":"0xe90aa1d6038c87b029a0666148ac2058ab8397f9c53594cc5a38c0113a48eab4","blockNumber":"0x11e7bd0","data":"0x","logIndex":"0x10b","removed":false,"topics":["0xbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972"],"transactionHash":"0xf9f60f6dc99663c6ce4912ef92fe6a122bb90585e47b5f213efca1705be26d6e","transactionIndex":"0x63"},{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","blockHash":"0xe90aa1d6038c87b029a0666148ac2058ab8397f9c53594cc5a38c0113a48eab4","blockNumber":"0x11e7bd0","data":"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","logIndex":"0x10c","removed":false,"topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000eae4d85f7733ad522f601ce7ad4f595704a2d677","0x0000000000000000000000001e0049783f008a0085193e00003d00cd54003c71"],"transactionHash":"0xf9f60f6dc99663c6ce4912ef92fe6a122bb90585e47b5f213efca1705be26d6e","transactionIndex":"0x63"},{"address":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","blockHash":"0xe90aa1d6038c87b029a0666148ac2058ab8397f9c53594cc5a38c0113a48eab4","blockNumber":"0x11e7bd0","data":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000015ed8b1358919200000000000000000000000000000000000000000000000000000000000284a6","logIndex":"0x10d","removed":false,"topics":["0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f","0x2d5f7a884e9a99cfe2445db2af140a8851fbd860852b668f2f199190f68adf87","0x000000000000000000000000eae4d85f7733ad522f601ce7ad4f595704a2d677","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":"0xf9f60f6dc99663c6ce4912ef92fe6a122bb90585e47b5f213efca1705be26d6e","transactionIndex":"0x63"}],"logsBloom":"0x000000000400000000000000000000000000000000000000000000000000000000080000000000000002000100000000021000000800000000000200002000000000008000000000200000000000000020000000000000000000000000002000000000000a0000000000000000000800000000000000000000000000000200000000000000002000000000000000000000000000000000000000000000000000020001000000400000400000000000000000000020000000000002000000000000000000000000000001000000000000000000000000000000000000000020000050200000000000000000000000000000000000000000000010000000000000","status":"0x1","to":"0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789","transactionHash":"0xf9f60f6dc99663c6ce4912ef92fe6a122bb90585e47b5f213efca1705be26d6e","transactionIndex":"0x63","type":"0x2"}"#).unwrap(); + let entry_point = Address::from_str("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789").unwrap(); client.push(receipt).unwrap(); client.push(tx).unwrap(); @@ -404,11 +414,9 @@ mod tests { Provider::new(CommonTransport::Mock(client)), db.clone(), Default::default(), + v06::IndexerV06 { entry_point }, ); - indexer - .handle_tx::(tx_hash, NodeClient::Geth) - .await - .unwrap(); + indexer.handle_tx(tx_hash, NodeClient::Geth).await.unwrap(); let op_hash = H256::from_str("0x2d5f7a884e9a99cfe2445db2af140a8851fbd860852b668f2f199190f68adf87") @@ -432,7 +440,7 @@ mod tests { signature: Bytes::from_str("0x000000000000000000000000000000000000000000000000000000000065793a092c25c7a7c5e4bc46467324e2845caf1ccae767786e07806ca720f8a6b83356bc7d43a63a96b34507cfe7c424db37f351d71851ae9318e8d5c3d9f17c8bdb744c1c").unwrap(), aggregator: None, aggregator_signature: None, - entry_point: v06::IndexerV06::entry_point(), + entry_point, entry_point_version: EntryPointVersion::V06, transaction_hash: tx_hash, block_number: 18774992, @@ -467,6 +475,7 @@ mod tests { .unwrap(); let tx: Transaction = serde_json::from_str(r#"{"blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","blockNumber":"0x519c6b","from":"0x43d1089285a94bf481e1f6b1a7a114acbc833796","gas":"0x4c4b40","gasPrice":"0xbb3e00f1","maxPriorityFeePerGas":"0xb2d05e00","maxFeePerGas":"0xc20d353e","hash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","input":"0x765e827f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000043d1089285a94bf481e1f6b1a7a114acbc83379600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000f4240000000000000000000000000001e8480000000000000000000000000000000000000000000000000000000000007a120000000000000000000000000000000010000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000002d81f5806eafab78028b6e29ab65208f54cfdd4ce45a1aafc9e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000244ac27308a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000080ee560d57f4b1d2acfeb2174d09d54879c7408800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000598991c9d726cbac7eb023ca974fe6e7e7a57ce80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000003479096622cf141e3cc93126bbccc3ef10b952c1ef000000000000000000000000000000000000000000000000000000000002a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000074115cff9c5b847b402c382f066cf275ab6440b75aaa1b881c164e5d43131cfb3895759573bc597baf526002f8d1943f1aaa67dbf7fa99cd30d12a235169eef4f3d5c96fc1619c60bc9d8028dfea0f89c7ec5e3f27000000000000000000000000000000000000000000000000000000000002a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014434fcd5be00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000094a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000001b637a3008dc1f86d92031a97fc4b5ac0803329e00000000000000000000000000000000000000000000000000000002540be400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000741b637a3008dc1f86d92031a97fc4b5ac0803329e00000000000000000000000000061a8000000000000000000000000000061a8000000000000000000000000094a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c800000000000000000000000000000000000000000000000000000002540be400000000000000000000000000000000000000000000000000000000000000000000000000000000000000005a89d0e2cdece3d2f2e2497f2b68c5f96ef073c1800000004200775c0e5049afa24e5370a754faade91452b89dfc97907588ac49b441bcf43d06067f220a252454360907199ae8dfdc7fef2caf6c2aae03e4e0676b2c1ae351601b000000000000","nonce":"0x6","to":"0x0000000071727de22e5e9d8baf0edac6f37da032","transactionIndex":"0x6b","value":"0x0","type":"0x2","accessList":[],"chainId":"0xaa36a7","v":"0x0","r":"0x708c8520e17da32765f6270908ec9961023380a115f6c2a3bbf100f7ef39b68a","s":"0x4730c2959f785391db89cb2cc23db9782054db7d650e7a8df04836e954271d5e"}"#).unwrap(); let receipt: TransactionReceipt = serde_json::from_str(r#"{"blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","blockNumber":"0x519c6b","contractAddress":null,"cumulativeGasUsed":"0x43e83a","effectiveGasPrice":"0xbb3e00f1","from":"0x43d1089285a94bf481e1f6b1a7a114acbc833796","gasUsed":"0xd0fbb","logs":[{"address":"0xf098c91823f1ef080f22645d030a7196e72d31eb","topics":["0x76329674d4361897f3154af54261c4cc05a0d5964509aeedce71949fa0d34725","0x000000000000000000000000598991c9d726cbac7eb023ca974fe6e7e7a57ce8"],"data":"0x","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x1f","removed":false},{"address":"0xf098c91823f1ef080f22645d030a7196e72d31eb","topics":["0xf80f6dfd1cac76f4ebc9005d547d88739ba90991e2c432ac74b18536c9e72af2"],"data":"0x00000000000000000000000089d0e2cdece3d2f2e2497f2b68c5f96ef073c180","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x20","removed":false},{"address":"0x79096622cf141e3cc93126bbccc3ef10b952c1ef","topics":["0xcdddfb4e53d2f7d725fae607b33383443789359047546dbdbd01f85d21adf61c","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb"],"data":"0x","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x21","removed":false},{"address":"0xf098c91823f1ef080f22645d030a7196e72d31eb","topics":["0xb4a437488482177b2d124ce7c50e57d8f8d42a9896b525c9c497ee0d533a95de"],"data":"0x00000000000000000000000079096622cf141e3cc93126bbccc3ef10b952c1ef","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x22","removed":false},{"address":"0x115cff9c5b847b402c382f066cf275ab6440b75a","topics":["0x18c5105ca36f183d9b8ee510786b13e3e58916d2525c72884d40ada1a6112e74","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb"],"data":"0xaa1b881c164e5d43131cfb3895759573bc597baf526002f8d1943f1aaa67dbf7fa99cd30d12a235169eef4f3d5c96fc1619c60bc9d8028dfea0f89c7ec5e3f27000000000000000000000000000000000000000000000000000000000002a300","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x23","removed":false},{"address":"0x115cff9c5b847b402c382f066cf275ab6440b75a","topics":["0xcdddfb4e53d2f7d725fae607b33383443789359047546dbdbd01f85d21adf61c","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb"],"data":"0x","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x24","removed":false},{"address":"0xf098c91823f1ef080f22645d030a7196e72d31eb","topics":["0xb4a437488482177b2d124ce7c50e57d8f8d42a9896b525c9c497ee0d533a95de"],"data":"0x000000000000000000000000115cff9c5b847b402c382f066cf275ab6440b75a","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x25","removed":false},{"address":"0xf098c91823f1ef080f22645d030a7196e72d31eb","topics":["0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2"],"data":"0x0000000000000000000000000000000000000000000000000000000000000001","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x26","removed":false},{"address":"0x1f5806eafab78028b6e29ab65208f54cfdd4ce45","topics":["0x48df5b960943935df47b5ee244b72a9ea791c73f9d518287bf46d17c8bbe1259","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb"],"data":"0x","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x27","removed":false},{"address":"0x0000000071727de22e5e9d8baf0edac6f37da032","topics":["0xd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d","0x02bfece5db8c1bd400049c14e20ee988e62c057d296e9aefa34bd9b7f146033e","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb"],"data":"0x0000000000000000000000001f5806eafab78028b6e29ab65208f54cfdd4ce450000000000000000000000001b637a3008dc1f86d92031a97fc4b5ac0803329e","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x28","removed":false},{"address":"0x0000000071727de22e5e9d8baf0edac6f37da032","topics":["0xbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972"],"data":"0x","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x29","removed":false},{"address":"0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb","0x0000000000000000000000001b637a3008dc1f86d92031a97fc4b5ac0803329e"],"data":"0x00000000000000000000000000000000000000000000000000000002540be400","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x2a","removed":false},{"address":"0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb","0x0000000000000000000000001b637a3008dc1f86d92031a97fc4b5ac0803329e"],"data":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x2b","removed":false},{"address":"0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb","0x0000000000000000000000001b637a3008dc1f86d92031a97fc4b5ac0803329e"],"data":"0x00000000000000000000000000000000000000000000000000000002540be400","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x2c","removed":false},{"address":"0x1b637a3008dc1f86d92031a97fc4b5ac0803329e","topics":["0x17ffde6359ce255c678a17b62fba7f276b9187996206563daaab42c2d836d675","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb"],"data":"0x00000000000000000000000094a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012f4e9","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x2d","removed":false},{"address":"0x0000000071727de22e5e9d8baf0edac6f37da032","topics":["0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f","0x02bfece5db8c1bd400049c14e20ee988e62c057d296e9aefa34bd9b7f146033e","0x000000000000000000000000f098c91823f1ef080f22645d030a7196e72d31eb","0x0000000000000000000000001b637a3008dc1f86d92031a97fc4b5ac0803329e"],"data":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000001768630000000000000000000000000000000000000000000000000000000000176863","blockNumber":"0x519c6b","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","blockHash":"0x65940368797f7f65885f86fdb367467b2c942aee60ddf9a3fb149a8924ac073b","logIndex":"0x2e","removed":false}],"logsBloom":"0x02000400001000000200000000010000000180800030000000040000008000000008008801200000004000010000000090000001000000000000020000240000000900080000000000000008000000000040000000000000000008000000900280000000000800000000001000000000000000000000200000000010000002000000000000000000000800404000000000000200000480000000000000000001020000000100400000404000000000000000000004000000000002000010000000002002000000400001008000100400000000040004010000000000000000000010400010100000000000000000000000000000000000000000040000000010","status":"0x1","to":"0x0000000071727de22e5e9d8baf0edac6f37da032","transactionHash":"0xfce54378732b4fdf41a3c65b3b93c6bdabcd0b841bc24969d3593f65ca730f12","transactionIndex":"0x6b","type":"0x2"}"#).unwrap(); + let entry_point = Address::from_str("0x0000000071727De22E5E9d8BAf0edAc6f37da032").unwrap(); client.push(receipt).unwrap(); client.push(tx).unwrap(); @@ -475,11 +484,9 @@ mod tests { Provider::new(CommonTransport::Mock(client)), db.clone(), Default::default(), + v07::IndexerV07 { entry_point }, ); - indexer - .handle_tx::(tx_hash, NodeClient::Geth) - .await - .unwrap(); + indexer.handle_tx(tx_hash, NodeClient::Geth).await.unwrap(); let op_hash = H256::from_str("0x02bfece5db8c1bd400049c14e20ee988e62c057d296e9aefa34bd9b7f146033e") @@ -503,7 +510,7 @@ mod tests { signature: Bytes::from_str("0x89d0e2cdece3d2f2e2497f2b68c5f96ef073c1800000004200775c0e5049afa24e5370a754faade91452b89dfc97907588ac49b441bcf43d06067f220a252454360907199ae8dfdc7fef2caf6c2aae03e4e0676b2c1ae351601b").unwrap(), aggregator: None, aggregator_signature: None, - entry_point: v07::IndexerV07::entry_point(), + entry_point, entry_point_version: EntryPointVersion::V07, transaction_hash: tx_hash, block_number: 5348459, diff --git a/user-ops-indexer/user-ops-indexer-logic/src/indexer/rpc_utils.rs b/user-ops-indexer/user-ops-indexer-logic/src/indexer/rpc_utils.rs index 22502fe35..ca084176e 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/indexer/rpc_utils.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/indexer/rpc_utils.rs @@ -146,8 +146,10 @@ pub fn to_string(node_client: NodeClient) -> String { #[cfg(test)] mod tests { use crate::indexer::rpc_utils::{flatten_geth_trace, TraceType}; - use ethers::prelude::{Address, Bytes, CallFrame}; - use ethers_core::utils::to_checksum; + use ethers::{ + prelude::{Address, Bytes, CallFrame}, + utils::to_checksum, + }; use std::str::FromStr; #[test] diff --git a/user-ops-indexer/user-ops-indexer-logic/src/indexer/settings.rs b/user-ops-indexer/user-ops-indexer-logic/src/indexer/settings.rs index 5bdcc8e6d..27af787f8 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/indexer/settings.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/indexer/settings.rs @@ -1,3 +1,4 @@ +use ethers::prelude::Address; use serde::Deserialize; use serde_with::serde_as; use std::time; @@ -31,10 +32,12 @@ pub struct IndexerSettings { } #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] -#[serde(deny_unknown_fields)] +#[serde(default, deny_unknown_fields)] pub struct EntrypointsSettings { pub v06: bool, + pub v06_entry_point: Address, pub v07: bool, + pub v07_entry_point: Address, } #[serde_as] @@ -94,10 +97,7 @@ impl Default for IndexerSettings { Self { rpc_url: "ws://127.0.0.1:8546".to_string(), concurrency: 10, - entrypoints: EntrypointsSettings { - v06: true, - v07: true, - }, + entrypoints: Default::default(), realtime: RealtimeIndexerSettings { enabled: true, polling_interval: default_polling_interval(), @@ -118,3 +118,18 @@ impl Default for IndexerSettings { } } } + +impl Default for EntrypointsSettings { + fn default() -> Self { + Self { + v06: true, + v06_entry_point: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" + .parse() + .unwrap(), + v07: true, + v07_entry_point: "0x0000000071727De22E5E9d8BAf0edAc6f37da032" + .parse() + .unwrap(), + } + } +} diff --git a/user-ops-indexer/user-ops-indexer-logic/src/indexer/v06/mod.rs b/user-ops-indexer/user-ops-indexer-logic/src/indexer/v06/mod.rs index 1090a4028..7a41160b7 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/indexer/v06/mod.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/indexer/v06/mod.rs @@ -15,18 +15,14 @@ use ethers::prelude::{ types::{Bytes, Log, TransactionReceipt, H256}, BigEndianHash, EthEvent, }; -use lazy_static::lazy_static; use std::ops::Div; -lazy_static! { - static ref ENTRYPOINT: ethers::types::Address = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789" - .parse() - .unwrap(); -} - abigen!(IEntrypointV06, "./src/indexer/v06/abi.json"); -pub struct IndexerV06; +#[derive(Debug, Clone)] +pub struct IndexerV06 { + pub entry_point: Address, +} struct ExtendedUserOperation { user_op: UserOperation, @@ -36,8 +32,8 @@ struct ExtendedUserOperation { } impl IndexerLogic for IndexerV06 { - fn entry_point() -> Address { - *ENTRYPOINT + fn entry_point(&self) -> Address { + self.entry_point } fn version() -> &'static str { @@ -57,6 +53,7 @@ impl IndexerLogic for IndexerV06 { } fn parse_user_ops( + &self, receipt: &TransactionReceipt, bundle_index: usize, calldata: &Bytes, @@ -103,7 +100,13 @@ impl IndexerLogic for IndexerV06 { .zip(log_bundle.iter()) .enumerate() .filter_map(|(j, (user_op, logs))| { - match build_user_op_model(receipt, bundle_index as u32, j as u32, user_op, logs) { + match self.build_user_op_model( + receipt, + bundle_index as u32, + j as u32, + user_op, + logs, + ) { Ok(model) => Some(model), Err(err) => { let logs_start_index = @@ -126,76 +129,79 @@ impl IndexerLogic for IndexerV06 { } } -fn build_user_op_model( - receipt: &TransactionReceipt, - bundle_index: u32, - index: u32, - user_op: ExtendedUserOperation, - logs: &[Log], -) -> anyhow::Result { - let user_op_event = logs - .last() - .and_then(IndexerV06::match_and_parse::) - .transpose()? - .ok_or(anyhow!("last log doesn't match UserOperationEvent"))?; - let revert_event = logs - .iter() - .find_map(IndexerV06::match_and_parse::) - .transpose()?; +impl IndexerV06 { + fn build_user_op_model( + &self, + receipt: &TransactionReceipt, + bundle_index: u32, + index: u32, + user_op: ExtendedUserOperation, + logs: &[Log], + ) -> anyhow::Result { + let user_op_event = logs + .last() + .and_then(|log| self.match_and_parse::(log)) + .transpose()? + .ok_or(anyhow!("last log doesn't match UserOperationEvent"))?; + let revert_event = logs + .iter() + .find_map(|log| self.match_and_parse::(log)) + .transpose()?; - let tx_deposits: Vec
= receipt - .logs - .iter() - .filter_map(IndexerV06::match_and_parse::) - .filter_map(Result::ok) - .map(|e| e.account) - .collect(); + let tx_deposits: Vec
= receipt + .logs + .iter() + .filter_map(|log| self.match_and_parse::(log)) + .filter_map(Result::ok) + .map(|e| e.account) + .collect(); - let factory = extract_address(&user_op.user_op.init_code); - let paymaster = extract_address(&user_op.user_op.paymaster_and_data); - let sender = user_op.user_op.sender; - let (user_logs_start_index, user_logs_count) = - extract_user_logs_boundaries(logs, *ENTRYPOINT, paymaster); - Ok(UserOp { - hash: H256::from(user_op_event.user_op_hash), - sender, - nonce: H256::from_uint(&user_op.user_op.nonce), - init_code: none_if_empty(user_op.user_op.init_code), - call_data: user_op.user_op.call_data, - call_gas_limit: user_op.user_op.call_gas_limit, - verification_gas_limit: user_op.user_op.verification_gas_limit, - pre_verification_gas: user_op.user_op.pre_verification_gas, - max_fee_per_gas: user_op.user_op.max_fee_per_gas, - max_priority_fee_per_gas: user_op.user_op.max_priority_fee_per_gas, - paymaster_and_data: none_if_empty(user_op.user_op.paymaster_and_data), - signature: user_op.user_op.signature, - aggregator: user_op.aggregator, - aggregator_signature: user_op.aggregator_signature, - entry_point: *ENTRYPOINT, - entry_point_version: EntryPointVersion::V06, - transaction_hash: receipt.transaction_hash, - block_number: receipt.block_number.map_or(0, |n| n.as_u64()), - block_hash: receipt.block_hash.unwrap_or(H256::zero()), - bundler: user_op.bundler, - bundle_index, - index, - factory, - paymaster, - status: user_op_event.success, - revert_reason: revert_event.map(|e| e.revert_reason), - gas: user_op.user_op.call_gas_limit - + user_op.user_op.verification_gas_limit * if paymaster.is_none() { 1 } else { 3 } - + user_op.user_op.pre_verification_gas, - gas_price: user_op_event - .actual_gas_cost - .div(user_op_event.actual_gas_used), - gas_used: user_op_event.actual_gas_used, - sponsor_type: extract_sponsor_type(sender, paymaster, &tx_deposits), - user_logs_start_index, - user_logs_count, - fee: user_op_event.actual_gas_cost, + let factory = extract_address(&user_op.user_op.init_code); + let paymaster = extract_address(&user_op.user_op.paymaster_and_data); + let sender = user_op.user_op.sender; + let (user_logs_start_index, user_logs_count) = + extract_user_logs_boundaries(logs, self.entry_point, paymaster); + Ok(UserOp { + hash: H256::from(user_op_event.user_op_hash), + sender, + nonce: H256::from_uint(&user_op.user_op.nonce), + init_code: none_if_empty(user_op.user_op.init_code), + call_data: user_op.user_op.call_data, + call_gas_limit: user_op.user_op.call_gas_limit, + verification_gas_limit: user_op.user_op.verification_gas_limit, + pre_verification_gas: user_op.user_op.pre_verification_gas, + max_fee_per_gas: user_op.user_op.max_fee_per_gas, + max_priority_fee_per_gas: user_op.user_op.max_priority_fee_per_gas, + paymaster_and_data: none_if_empty(user_op.user_op.paymaster_and_data), + signature: user_op.user_op.signature, + aggregator: user_op.aggregator, + aggregator_signature: user_op.aggregator_signature, + entry_point: self.entry_point, + entry_point_version: EntryPointVersion::V06, + transaction_hash: receipt.transaction_hash, + block_number: receipt.block_number.map_or(0, |n| n.as_u64()), + block_hash: receipt.block_hash.unwrap_or(H256::zero()), + bundler: user_op.bundler, + bundle_index, + index, + factory, + paymaster, + status: user_op_event.success, + revert_reason: revert_event.map(|e| e.revert_reason), + gas: user_op.user_op.call_gas_limit + + user_op.user_op.verification_gas_limit * if paymaster.is_none() { 1 } else { 3 } + + user_op.user_op.pre_verification_gas, + gas_price: user_op_event + .actual_gas_cost + .div(user_op_event.actual_gas_used), + gas_used: user_op_event.actual_gas_used, + sponsor_type: extract_sponsor_type(sender, paymaster, &tx_deposits), + user_logs_start_index, + user_logs_count, + fee: user_op_event.actual_gas_cost, - consensus: None, - timestamp: None, - }) + consensus: None, + timestamp: None, + }) + } } diff --git a/user-ops-indexer/user-ops-indexer-logic/src/indexer/v07/mod.rs b/user-ops-indexer/user-ops-indexer-logic/src/indexer/v07/mod.rs index 0d2f29205..1da32ffc2 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/indexer/v07/mod.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/indexer/v07/mod.rs @@ -16,18 +16,14 @@ use ethers::prelude::{ types::{Bytes, Log, TransactionReceipt, H256}, BigEndianHash, EthEvent, U256, }; -use lazy_static::lazy_static; use std::ops::Div; -lazy_static! { - static ref ENTRYPOINT: ethers::types::Address = "0x0000000071727De22E5E9d8BAf0edAc6f37da032" - .parse() - .unwrap(); -} - abigen!(IEntrypointV07, "./src/indexer/v07/abi.json"); -pub struct IndexerV07; +#[derive(Debug, Clone)] +pub struct IndexerV07 { + pub entry_point: Address, +} struct ExtendedUserOperation { user_op: PackedUserOperation, @@ -37,8 +33,8 @@ struct ExtendedUserOperation { } impl IndexerLogic for IndexerV07 { - fn entry_point() -> Address { - *ENTRYPOINT + fn entry_point(&self) -> Address { + self.entry_point } fn version() -> &'static str { @@ -58,6 +54,7 @@ impl IndexerLogic for IndexerV07 { } fn parse_user_ops( + &self, receipt: &TransactionReceipt, bundle_index: usize, calldata: &Bytes, @@ -104,7 +101,13 @@ impl IndexerLogic for IndexerV07 { .zip(log_bundle.iter()) .enumerate() .filter_map(|(j, (user_op, logs))| { - match build_user_op_model(receipt, bundle_index as u32, j as u32, user_op, logs) { + match self.build_user_op_model( + receipt, + bundle_index as u32, + j as u32, + user_op, + logs, + ) { Ok(model) => Some(model), Err(err) => { let logs_start_index = @@ -127,91 +130,95 @@ impl IndexerLogic for IndexerV07 { } } -fn build_user_op_model( - receipt: &TransactionReceipt, - bundle_index: u32, - index: u32, - user_op: ExtendedUserOperation, - logs: &[Log], -) -> anyhow::Result { - let user_op_event = logs - .last() - .and_then(IndexerV07::match_and_parse::) - .transpose()? - .ok_or(anyhow!("last log doesn't match UserOperationEvent"))?; - let revert_event = logs - .iter() - .find_map(IndexerV07::match_and_parse::) - .transpose()?; - - let tx_deposits: Vec
= receipt - .logs - .iter() - .filter_map(IndexerV07::match_and_parse::) - .filter_map(Result::ok) - .map(|e| e.account) - .collect(); - - let (verification_gas_limit, call_gas_limit) = - unpack_uints(&user_op.user_op.account_gas_limits[..]); - let pre_verification_gas = user_op.user_op.pre_verification_gas; - let (paymaster_verification_gas_limit, paymaster_post_op_gas_limit) = - if user_op.user_op.paymaster_and_data.len() >= 52 { - unpack_uints(&user_op.user_op.paymaster_and_data[20..52]) - } else { - (U256::zero(), U256::zero()) - }; - let gas = call_gas_limit - + verification_gas_limit - + pre_verification_gas - + paymaster_verification_gas_limit - + paymaster_post_op_gas_limit; - - let (max_fee_per_gas, max_priority_fee_per_gas) = unpack_uints(&user_op.user_op.gas_fees[..]); - - let factory = extract_address(&user_op.user_op.init_code); - let paymaster = extract_address(&user_op.user_op.paymaster_and_data); - let sender = user_op.user_op.sender; - let (user_logs_start_index, user_logs_count) = - extract_user_logs_boundaries(logs, *ENTRYPOINT, paymaster); - Ok(UserOp { - hash: H256::from(user_op_event.user_op_hash), - sender, - nonce: H256::from_uint(&user_op.user_op.nonce), - init_code: none_if_empty(user_op.user_op.init_code), - call_data: user_op.user_op.call_data, - call_gas_limit, - verification_gas_limit, - pre_verification_gas, - max_fee_per_gas, - max_priority_fee_per_gas, - paymaster_and_data: none_if_empty(user_op.user_op.paymaster_and_data), - signature: user_op.user_op.signature, - aggregator: user_op.aggregator, - aggregator_signature: user_op.aggregator_signature, - entry_point: *ENTRYPOINT, - entry_point_version: EntryPointVersion::V07, - transaction_hash: receipt.transaction_hash, - block_number: receipt.block_number.map_or(0, |n| n.as_u64()), - block_hash: receipt.block_hash.unwrap_or(H256::zero()), - bundler: user_op.bundler, - bundle_index, - index, - factory, - paymaster, - status: user_op_event.success, - revert_reason: revert_event.map(|e| e.revert_reason), - gas, - gas_price: user_op_event - .actual_gas_cost - .div(user_op_event.actual_gas_used), - gas_used: user_op_event.actual_gas_used, - sponsor_type: extract_sponsor_type(sender, paymaster, &tx_deposits), - user_logs_start_index, - user_logs_count, - fee: user_op_event.actual_gas_cost, - - consensus: None, - timestamp: None, - }) +impl IndexerV07 { + fn build_user_op_model( + &self, + receipt: &TransactionReceipt, + bundle_index: u32, + index: u32, + user_op: ExtendedUserOperation, + logs: &[Log], + ) -> anyhow::Result { + let user_op_event = logs + .last() + .and_then(|log| self.match_and_parse::(log)) + .transpose()? + .ok_or(anyhow!("last log doesn't match UserOperationEvent"))?; + let revert_event = logs + .iter() + .find_map(|log| self.match_and_parse::(log)) + .transpose()?; + + let tx_deposits: Vec
= receipt + .logs + .iter() + .filter_map(|log| self.match_and_parse::(log)) + .filter_map(Result::ok) + .map(|e| e.account) + .collect(); + + let (verification_gas_limit, call_gas_limit) = + unpack_uints(&user_op.user_op.account_gas_limits[..]); + let pre_verification_gas = user_op.user_op.pre_verification_gas; + let (paymaster_verification_gas_limit, paymaster_post_op_gas_limit) = + if user_op.user_op.paymaster_and_data.len() >= 52 { + unpack_uints(&user_op.user_op.paymaster_and_data[20..52]) + } else { + (U256::zero(), U256::zero()) + }; + let gas = call_gas_limit + + verification_gas_limit + + pre_verification_gas + + paymaster_verification_gas_limit + + paymaster_post_op_gas_limit; + + let (max_fee_per_gas, max_priority_fee_per_gas) = + unpack_uints(&user_op.user_op.gas_fees[..]); + + let factory = extract_address(&user_op.user_op.init_code); + let paymaster = extract_address(&user_op.user_op.paymaster_and_data); + let sender = user_op.user_op.sender; + let (user_logs_start_index, user_logs_count) = + extract_user_logs_boundaries(logs, self.entry_point, paymaster); + Ok(UserOp { + hash: H256::from(user_op_event.user_op_hash), + sender, + nonce: H256::from_uint(&user_op.user_op.nonce), + init_code: none_if_empty(user_op.user_op.init_code), + call_data: user_op.user_op.call_data, + call_gas_limit, + verification_gas_limit, + pre_verification_gas, + max_fee_per_gas, + max_priority_fee_per_gas, + paymaster_and_data: none_if_empty(user_op.user_op.paymaster_and_data), + signature: user_op.user_op.signature, + aggregator: user_op.aggregator, + aggregator_signature: user_op.aggregator_signature, + entry_point: self.entry_point, + entry_point_version: EntryPointVersion::V07, + transaction_hash: receipt.transaction_hash, + block_number: receipt.block_number.map_or(0, |n| n.as_u64()), + block_hash: receipt.block_hash.unwrap_or(H256::zero()), + bundler: user_op.bundler, + bundle_index, + index, + factory, + paymaster, + status: user_op_event.success, + revert_reason: revert_event.map(|e| e.revert_reason), + gas, + gas_price: user_op_event + .actual_gas_cost + .div(user_op_event.actual_gas_used), + gas_used: user_op_event.actual_gas_used, + sponsor_type: extract_sponsor_type(sender, paymaster, &tx_deposits), + user_logs_start_index, + user_logs_count, + fee: user_op_event.actual_gas_cost, + + consensus: None, + timestamp: None, + }) + } } diff --git a/user-ops-indexer/user-ops-indexer-logic/src/types/account.rs b/user-ops-indexer/user-ops-indexer-logic/src/types/account.rs index 7311ef9f6..47b5886b4 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/types/account.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/types/account.rs @@ -1,6 +1,8 @@ use crate::repository::account::AccountDB; -use ethers::prelude::{Address, H256}; -use ethers_core::{abi::AbiEncode, utils::to_checksum}; +use ethers::{ + prelude::{abi::AbiEncode, Address, H256}, + utils::to_checksum, +}; #[derive(Clone, Debug, PartialEq)] pub struct Account { diff --git a/user-ops-indexer/user-ops-indexer-logic/src/types/bundle.rs b/user-ops-indexer/user-ops-indexer-logic/src/types/bundle.rs index 046a17f84..56fd41382 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/types/bundle.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/types/bundle.rs @@ -1,6 +1,8 @@ use crate::repository::bundle::BundleDB; -use ethers::prelude::{Address, H256}; -use ethers_core::{abi::AbiEncode, utils::to_checksum}; +use ethers::{ + prelude::{abi::AbiEncode, Address, H256}, + utils::to_checksum, +}; #[derive(Clone, Debug, PartialEq)] pub struct Bundle { diff --git a/user-ops-indexer/user-ops-indexer-logic/src/types/bundler.rs b/user-ops-indexer/user-ops-indexer-logic/src/types/bundler.rs index 7d36e6c37..5dd1258dc 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/types/bundler.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/types/bundler.rs @@ -1,6 +1,5 @@ use crate::repository::bundler::BundlerDB; -use ethers::prelude::Address; -use ethers_core::utils::to_checksum; +use ethers::{prelude::Address, utils::to_checksum}; #[derive(Clone, Debug, PartialEq)] pub struct Bundler { diff --git a/user-ops-indexer/user-ops-indexer-logic/src/types/factory.rs b/user-ops-indexer/user-ops-indexer-logic/src/types/factory.rs index e0dd2e9d8..02983d67e 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/types/factory.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/types/factory.rs @@ -1,6 +1,5 @@ use crate::repository::factory::FactoryDB; -use ethers::prelude::Address; -use ethers_core::utils::to_checksum; +use ethers::{prelude::Address, utils::to_checksum}; #[derive(Clone, Debug, PartialEq)] pub struct Factory { diff --git a/user-ops-indexer/user-ops-indexer-logic/src/types/paymaster.rs b/user-ops-indexer/user-ops-indexer-logic/src/types/paymaster.rs index 31d973707..8080dd527 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/types/paymaster.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/types/paymaster.rs @@ -1,6 +1,5 @@ use crate::repository::paymaster::PaymasterDB; -use ethers::prelude::Address; -use ethers_core::utils::to_checksum; +use ethers::{prelude::Address, utils::to_checksum}; #[derive(Clone, Debug, PartialEq)] pub struct Paymaster { diff --git a/user-ops-indexer/user-ops-indexer-logic/src/types/user_op.rs b/user-ops-indexer/user-ops-indexer-logic/src/types/user_op.rs index f05048547..c712c522b 100644 --- a/user-ops-indexer/user-ops-indexer-logic/src/types/user_op.rs +++ b/user-ops-indexer/user-ops-indexer-logic/src/types/user_op.rs @@ -4,8 +4,9 @@ use crate::{ }; pub use entity::sea_orm_active_enums::{EntryPointVersion, SponsorType}; use entity::user_operations::Model; -use ethers::prelude::{ - abi::AbiEncode, core::utils::to_checksum, Address, BigEndianHash, Bytes, H256, U256, +use ethers::{ + prelude::{abi::AbiEncode, Address, BigEndianHash, Bytes, H256, U256}, + utils::to_checksum, }; use num_traits::cast::ToPrimitive; use sea_orm::ActiveEnum; diff --git a/user-ops-indexer/user-ops-indexer-server/src/indexer.rs b/user-ops-indexer/user-ops-indexer-server/src/indexer.rs index 4176cf8ab..ee8c8a952 100644 --- a/user-ops-indexer/user-ops-indexer-server/src/indexer.rs +++ b/user-ops-indexer/user-ops-indexer-server/src/indexer.rs @@ -1,5 +1,5 @@ use crate::settings::Settings; -use ethers::prelude::Provider; +use ethers::{prelude::Provider, utils::to_checksum}; use sea_orm::DatabaseConnection; use std::sync::Arc; use tokio::time::sleep; @@ -14,9 +14,12 @@ pub async fn run( let db_connection = Arc::new(db_connection); if settings.indexer.entrypoints.v06 { - start_indexer_with_retries::( + start_indexer_with_retries( db_connection.clone(), settings.indexer.clone(), + v06::IndexerV06 { + entry_point: settings.indexer.entrypoints.v06_entry_point, + }, ) .await?; } else { @@ -24,9 +27,12 @@ pub async fn run( } if settings.indexer.entrypoints.v07 { - start_indexer_with_retries::( + start_indexer_with_retries( db_connection.clone(), settings.indexer.clone(), + v07::IndexerV07 { + entry_point: settings.indexer.entrypoints.v07_entry_point, + }, ) .await?; } else { @@ -36,23 +42,33 @@ pub async fn run( Ok(()) } -async fn start_indexer_with_retries( +async fn start_indexer_with_retries( db_connection: Arc, settings: IndexerSettings, + logic: L, ) -> anyhow::Result<()> { - tracing::info!(version = L::version(), "connecting to rpc"); + tracing::info!( + version = L::version(), + entry_point = to_checksum(&logic.entry_point(), None), + "connecting to rpc" + ); // If the first connect fails, the function will return an error immediately. // All subsequent reconnects are done inside tokio task and will not propagate to above. let transport = CommonTransport::new(settings.rpc_url.clone()).await?; let client = Provider::new(transport); - let mut indexer = Indexer::new(client, db_connection.clone(), settings.clone()); + let mut indexer = Indexer::new( + client, + db_connection.clone(), + settings.clone(), + logic.clone(), + ); let delay = settings.restart_delay; tokio::spawn(async move { loop { - match indexer.start::().await { + match indexer.start().await { Err(err) => { tracing::error!(error = ?err, version = L::version(), ?delay, "indexer startup failed, retrying"); } @@ -90,7 +106,12 @@ async fn start_indexer_with_retries( } }; let client = Provider::new(transport); - indexer = Indexer::new(client, db_connection.clone(), settings.clone()); + indexer = Indexer::new( + client, + db_connection.clone(), + settings.clone(), + logic.clone(), + ); break; } }