diff --git a/lerna.json b/lerna.json index 275d2ccc..7b59306f 100644 --- a/lerna.json +++ b/lerna.json @@ -3,7 +3,7 @@ "packages/*" ], "npmClient": "yarn", - "version": "2.0.21", + "version": "2.0.22", "stream": "true", "command": { "version": { diff --git a/package.json b/package.json index 1609208d..1a5b4f27 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "root", "private": true, - "version": "2.0.21", + "version": "2.0.22", "engines": { "node": ">=18.0.0" }, diff --git a/packages/api/package.json b/packages/api/package.json index f1f39730..db6328ee 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "The API module of Etherspot bundler client", "author": "Etherspot", "homepage": "https://https://github.com/etherspot/skandha#readme", @@ -34,10 +34,10 @@ "dependencies": { "@fastify/cors": "9.0.1", "@fastify/websocket": "10.0.1", - "@skandha/executor": "^2.0.21", - "@skandha/monitoring": "^2.0.21", - "@skandha/types": "^2.0.21", - "@skandha/utils": "^2.0.21", + "@skandha/executor": "^2.0.22", + "@skandha/monitoring": "^2.0.22", + "@skandha/types": "^2.0.22", + "@skandha/utils": "^2.0.22", "class-transformer": "0.5.1", "class-validator": "0.14.1", "ethers": "5.7.2", diff --git a/packages/cli/package.json b/packages/cli/package.json index fe1987e1..24b66768 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "> TODO: description", "author": "zincoshine ", "homepage": "https://https://github.com/etherspot/skandha#readme", @@ -40,12 +40,12 @@ "@libp2p/peer-id-factory": "2.0.1", "@libp2p/prometheus-metrics": "1.1.3", "@multiformats/multiaddr": "12.1.3", - "@skandha/api": "^2.0.21", - "@skandha/db": "^2.0.21", - "@skandha/executor": "^2.0.21", - "@skandha/monitoring": "^2.0.21", - "@skandha/node": "^2.0.21", - "@skandha/types": "^2.0.21", + "@skandha/api": "^2.0.22", + "@skandha/db": "^2.0.22", + "@skandha/executor": "^2.0.22", + "@skandha/monitoring": "^2.0.22", + "@skandha/node": "^2.0.22", + "@skandha/types": "^2.0.22", "find-up": "5.0.0", "got": "12.5.3", "js-yaml": "4.1.0", diff --git a/packages/db/package.json b/packages/db/package.json index 0980af07..589bef32 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "The DB module of Etherspot bundler client", "author": "Etherspot", "homepage": "https://github.com/etherspot/etherspot-bundler#readme", @@ -34,7 +34,7 @@ "dependencies": { "@chainsafe/ssz": "0.10.1", "@farcaster/rocksdb": "5.5.0", - "@skandha/types": "^2.0.21" + "@skandha/types": "^2.0.22" }, "devDependencies": { "@types/rocksdb": "3.0.1", diff --git a/packages/executor/package.json b/packages/executor/package.json index cbd98288..e6438025 100644 --- a/packages/executor/package.json +++ b/packages/executor/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "The Relayer module of Etherspot bundler client", "author": "Etherspot", "homepage": "https://https://github.com/etherspot/skandha#readme", @@ -35,10 +35,10 @@ }, "dependencies": { "@flashbots/ethers-provider-bundle": "0.6.2", - "@skandha/monitoring": "^2.0.21", - "@skandha/params": "^2.0.21", - "@skandha/types": "^2.0.21", - "@skandha/utils": "^2.0.21", + "@skandha/monitoring": "^2.0.22", + "@skandha/params": "^2.0.22", + "@skandha/types": "^2.0.22", + "@skandha/utils": "^2.0.22", "async-mutex": "0.4.0", "ethers": "5.7.2", "strict-event-emitter-types": "2.0.0", diff --git a/packages/executor/src/config.ts b/packages/executor/src/config.ts index b0f9553d..bdb544fc 100644 --- a/packages/executor/src/config.ts +++ b/packages/executor/src/config.ts @@ -328,6 +328,21 @@ export class Config { ) ); + config.blockscoutUrl = String( + fromEnvVar( + "BLOCKSCOUT_URL", + config.blockscoutUrl || bundlerDefaultConfigs.blockscoutUrl + ) + ); + + config.blockscoutApiKeys = fromEnvVar( + "BLOCKSCOUT_API_KEYS", + config.blockscoutApiKeys != undefined + ? config.blockscoutApiKeys + : bundlerDefaultConfigs.blockscoutApiKeys, + true + ) as string[]; + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!config.whitelistedEntities) { config.whitelistedEntities = bundlerDefaultConfigs.whitelistedEntities; @@ -402,6 +417,8 @@ const bundlerDefaultConfigs: BundlerConfig = { cglMarkupPercent: 0, vglMarkupPercent: 3000, // 30% eip1559: true, + blockscoutUrl: "", + blockscoutApiKeys: [], }; function getEnvVar(envVar: string, fallback: T): T | string { diff --git a/packages/executor/src/interfaces.ts b/packages/executor/src/interfaces.ts index 0ce73f4d..abc33892 100644 --- a/packages/executor/src/interfaces.ts +++ b/packages/executor/src/interfaces.ts @@ -176,6 +176,8 @@ export interface NetworkConfig { vglMarkupPercent: number; // enables / disabled eip1559 eip1559: boolean; + blockscoutUrl: string; + blockscoutApiKeys: string[]; } export type BundlerConfig = Omit< diff --git a/packages/executor/src/modules/eth.ts b/packages/executor/src/modules/eth.ts index 9b0ad710..2ebb0278 100644 --- a/packages/executor/src/modules/eth.ts +++ b/packages/executor/src/modules/eth.ts @@ -20,6 +20,7 @@ import { PerChainMetrics } from "@skandha/monitoring/lib"; import { UserOperation } from "@skandha/types/lib/contracts/UserOperation"; import { UserOperationStruct } from "@skandha/types/lib/contracts/EPv6/EntryPoint"; import { MempoolEntryStatus } from "@skandha/types/lib/executor"; +import { BlockscoutAPI } from "@skandha/utils/lib/third-party"; import { UserOpValidationService, MempoolService, @@ -37,6 +38,7 @@ import { Skandha } from "./skandha"; export class Eth { private pvgEstimator: IPVGEstimator | null = null; + private blockscoutApi: BlockscoutAPI | null = null; constructor( private chainId: number, @@ -64,6 +66,15 @@ export class Eth { if ([5000, 5001, 5003].includes(this.chainId)) { this.pvgEstimator = estimateMantlePVG(this.provider); } + + if (this.config.blockscoutUrl) { + this.blockscoutApi = new BlockscoutAPI( + this.provider, + this.logger, + this.config.blockscoutUrl, + this.config.blockscoutApiKeys + ); + } } /** @@ -403,7 +414,20 @@ export class Eth { blockNumber: transaction.blockNumber, }; } - return this.entryPointService.getUserOperationByHash(hash); + try { + const rpcUserOp = await this.entryPointService.getUserOperationByHash( + hash + ); + if (!rpcUserOp) { + throw new Error("userop not found"); + } + return rpcUserOp; + } catch (err) { + if (this.blockscoutApi) { + return await this.blockscoutApi.getUserOperationByHash(hash); + } + throw err; + } } /** @@ -414,7 +438,13 @@ export class Eth { async getUserOperationReceipt( hash: string ): Promise { - return this.entryPointService.getUserOperationReceipt(hash); + const rpcUserOp = await this.entryPointService.getUserOperationReceipt( + hash + ); + if (!rpcUserOp && this.blockscoutApi) { + return await this.blockscoutApi.getUserOperationReceipt(hash); + } + return null; } /** diff --git a/packages/executor/src/modules/skandha.ts b/packages/executor/src/modules/skandha.ts index f009d619..d64b0289 100644 --- a/packages/executor/src/modules/skandha.ts +++ b/packages/executor/src/modules/skandha.ts @@ -143,6 +143,8 @@ export class Skandha { userOpGasLimit: this.networkConfig.userOpGasLimit, bundleGasLimit: this.networkConfig.bundleGasLimit, merkleApiURL: this.networkConfig.merkleApiURL, + blockscoutUrl: this.networkConfig.blockscoutUrl, + blockscoutApiKeys: this.networkConfig.blockscoutApiKeys.length, }; } diff --git a/packages/monitoring/package.json b/packages/monitoring/package.json index b0ae10ef..a0834c67 100644 --- a/packages/monitoring/package.json +++ b/packages/monitoring/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "The Monitoring module of Etherspot bundler client", "author": "Etherspot", "homepage": "https://github.com/etherspot/etherspot-bundler#readme", @@ -32,7 +32,7 @@ "check-readme": "typescript-docs-verifier" }, "dependencies": { - "@skandha/types": "^2.0.21", + "@skandha/types": "^2.0.22", "prom-client": "15.1.0" } } diff --git a/packages/node/package.json b/packages/node/package.json index f1ac44fa..ee350d52 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "The bundler node module of Etherspot bundler client", "author": "Etherspot", "homepage": "https://https://github.com/etherspot/skandha#readme", @@ -56,13 +56,13 @@ "@libp2p/prometheus-metrics": "1.1.3", "@libp2p/tcp": "6.1.0", "@multiformats/multiaddr": "11.4.0", - "@skandha/api": "^2.0.21", - "@skandha/db": "^2.0.21", - "@skandha/executor": "^2.0.21", - "@skandha/monitoring": "^2.0.21", - "@skandha/params": "^2.0.21", - "@skandha/types": "^2.0.21", - "@skandha/utils": "^2.0.21", + "@skandha/api": "^2.0.22", + "@skandha/db": "^2.0.22", + "@skandha/executor": "^2.0.22", + "@skandha/monitoring": "^2.0.22", + "@skandha/params": "^2.0.22", + "@skandha/types": "^2.0.22", + "@skandha/utils": "^2.0.22", "abstract-leveldown": "7.2.0", "datastore-core": "8.0.1", "ethers": "5.7.2", diff --git a/packages/params/package.json b/packages/params/package.json index 81a383c8..4be900fa 100644 --- a/packages/params/package.json +++ b/packages/params/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "Various bundler parameters", "author": "Etherspot", "homepage": "https://github.com/etherspot/skandha#readme", @@ -28,8 +28,8 @@ "@arbitrum/sdk": "3.1.4", "@chainsafe/ssz": "0.16.0", "@mantleio/sdk": "0.2.1", - "@skandha/types": "^2.0.21", - "@skandha/utils": "^2.0.21", + "@skandha/types": "^2.0.22", + "@skandha/utils": "^2.0.22", "ethers": "5.7.2" }, "scripts": { diff --git a/packages/types/package.json b/packages/types/package.json index bbf63387..27a4039b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "The types of Etherspot bundler client", "author": "Etherspot", "homepage": "https://https://github.com/etherspot/skandha#readme", diff --git a/packages/types/src/api/interfaces.ts b/packages/types/src/api/interfaces.ts index ac68e03a..fda46c9a 100644 --- a/packages/types/src/api/interfaces.ts +++ b/packages/types/src/api/interfaces.ts @@ -91,6 +91,8 @@ export type GetConfigResponse = { userOpGasLimit: number; bundleGasLimit: number; merkleApiURL: string; + blockscoutUrl: string; + blockscoutApiKeys: number; }; export type SupportedEntryPoints = string[]; diff --git a/packages/utils/package.json b/packages/utils/package.json index fb74bb80..44f386f9 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -4,7 +4,7 @@ "publishConfig": { "access": "public" }, - "version": "2.0.21", + "version": "2.0.22", "description": "utils of Etherspot bundler client", "author": "Etherspot", "homepage": "https://https://github.com/etherspot/skandha#readme", @@ -33,7 +33,7 @@ }, "dependencies": { "@chainsafe/as-sha256": "0.3.1", - "@skandha/types": "^2.0.21", + "@skandha/types": "^2.0.22", "any-signal": "3.0.1", "bigint-buffer": "1.1.5", "case": "^1.6.3", diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 8526b2e9..0a53dacd 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -7,3 +7,4 @@ export * from "./errors"; export * from "./shuffle"; export * from "./sortBy"; export * from "./timeout"; +export * from "./third-party"; diff --git a/packages/utils/src/third-party/blockscout.ts b/packages/utils/src/third-party/blockscout.ts new file mode 100644 index 00000000..d0bb6a34 --- /dev/null +++ b/packages/utils/src/third-party/blockscout.ts @@ -0,0 +1,136 @@ +import { providers } from "ethers"; +import { + UserOperationByHashResponse, + UserOperationReceipt, +} from "@skandha/types/lib/api/interfaces"; +import { Logger } from "@skandha/types/lib"; +import { deepHexlify } from "../hexlify"; + +export class BlockscoutAPI { + private currentKeyIndex: number; + + constructor( + private provider: providers.JsonRpcProvider, + private logger: Logger, + private baseUrl: string, + private apiKeys: string[] + ) { + this.currentKeyIndex = 0; + } + + private getNextApiKey(): string { + if (this.apiKeys.length === 0) { + return ""; + } + this.currentKeyIndex = (this.currentKeyIndex + 1) % this.apiKeys.length; + return this.apiKeys[this.currentKeyIndex]; + } + + private async fetchUserOpByHash( + hash: string + ): Promise { + const apiKey = this.getNextApiKey(); + let url = `${this.baseUrl}/api/v2/proxy/account-abstraction/operations/${hash}`; + if (apiKey) { + url += `?apikey=${apiKey}`; + } + try { + const response = await fetch(url); + const data: UserOperationResponse = await response.json(); + + // userop not found + if (!data.entry_point_version) return null; + + return data; + } catch (err) { + return null; + } + } + + async getUserOperationReceipt( + hash: string + ): Promise { + this.logger.debug("Blockscout: getUserOperationReceipt"); + const data = await this.fetchUserOpByHash(hash); + if (!data) { + return null; + } + const receipt = await this.provider.getTransactionReceipt( + data.transaction_hash + ); + return deepHexlify({ + userOpHash: hash, + sender: data.raw.sender, + nonce: data.raw.nonce, + actualGasCost: data.fee, + actualGasUsed: data.gas_used, + success: data.revert_reason == null, + logs: receipt.logs, + receipt, + }); + } + + async getUserOperationByHash( + hash: string + ): Promise { + this.logger.debug("Blockscout: getUserOperationByHash"); + const data = await this.fetchUserOpByHash(hash); + if (!data) { + return null; + } + const { + raw, + transaction_hash: transactionHash, + entry_point: { hash: entryPoint }, + block_number: blockNumber, + block_hash: blockHash, + } = data; + + return deepHexlify({ + userOperation: { + sender: raw.sender, + nonce: raw.nonce, + initCode: raw.init_code, + callData: raw.call_data, + callGasLimit: raw.call_gas_limit, + verificationGasLimit: raw.verification_gas_limit, + preVerificationGas: raw.pre_verification_gas, + maxFeePerGas: raw.max_fee_per_gas, + maxPriorityFeePerGas: raw.max_priority_fee_per_gas, + paymasterAndData: raw.paymaster_and_data, + signature: raw.signature, + }, + entryPoint, + transactionHash, + blockHash, + blockNumber, + }); + } +} + +type UserOperationResponse = { + entry_point_version: string; + transaction_hash: string; + hash: string; + block_number: string; + block_hash: string; + raw: { + call_data: string; + call_gas_limit: string; + init_code: string; + max_fee_per_gas: string; + max_priority_fee_per_gas: string; + nonce: string; + paymaster_and_data: string; + pre_verification_gas: string; + sender: string; + signature: string; + verification_gas_limit: string; + }; + entry_point: { + hash: string; + }; + fee: string; // actualGasCost + gas_used: string; // actualGasUsed + revert_reason: string | null; +}; diff --git a/packages/utils/src/third-party/index.ts b/packages/utils/src/third-party/index.ts new file mode 100644 index 00000000..20a4c1cf --- /dev/null +++ b/packages/utils/src/third-party/index.ts @@ -0,0 +1 @@ +export * from "./blockscout";