diff --git a/.github/workflows/bundler-spec-tests.yml b/.github/workflows/bundler-spec-tests.yml
index 9f9451e9..ae7480de 100644
--- a/.github/workflows/bundler-spec-tests.yml
+++ b/.github/workflows/bundler-spec-tests.yml
@@ -13,9 +13,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- with:
- repository: etherspot/skandha
- ref: some-fixes
- name: Setup PDM
run: curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 -
@@ -61,8 +58,11 @@ jobs:
- name: Deploy ERC-4337 contracts
working-directory: ./bundler-spec-tests
+ # checkout to 0.6.0 commit https://github.com/eth-infinitism/account-abstraction/commits/abff2aca61a8f0934e533d0d352978055fddbd96
+ # change this command when using new EP
run: |
cd @account-abstraction && \
+ git checkout abff2aca61a8f0934e533d0d352978055fddbd96 && \
yarn deploy --network localhost
- name: Fund bundler
@@ -85,6 +85,7 @@ jobs:
- name: Send Slack notification
uses: ravsamhq/notify-slack-action@v2
if: always()
+ continue-on-error: true
with:
status: ${{ job.status }}
notification_title: "{workflow} has {status_message}"
diff --git a/.github/workflows/check-package-version.yml b/.github/workflows/check-package-version.yml
index 084654f6..fc9fb981 100644
--- a/.github/workflows/check-package-version.yml
+++ b/.github/workflows/check-package-version.yml
@@ -10,15 +10,26 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
+
+ - name: Setup NodeJS
+ uses: "actions/setup-node@v3"
+ with:
+ node-version: 18.15
+
- name: Check if version has been updated
id: check
uses: EndBug/version-check@v2.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
file-name: ./package.json
+
- name: Log when changed
if: steps.check.outputs.changed == 'true'
run: 'echo "Yayy!! Version change found in commit ${{ steps.check.outputs.commit }}! New version: ${{ steps.check.outputs.version }} (${{ steps.check.outputs.type }})"'
+
+ - name: Dry run Skandha build
+ run: yarn install && yarn build && yarn run bootstrap
+
- name: Log when unchanged
if: steps.check.outputs.changed == 'false'
run: echo "No version change :/ Please update version in package.json!" && exit 1
diff --git a/README.md b/README.md
index 270d2a15..d8e0c163 100644
--- a/README.md
+++ b/README.md
@@ -1,80 +1,89 @@
-
-
Skandha
-
-
-
-
-
-
-
-
- A modular, developer-friendly Typescript Bundler for Ethereum EIP-4337 Account Abstraction
-
-
-
-
-### Warning! This repo/software is under active development
-
-## βοΈ How to run (from Source code)
-
-Run with one-liner:
-
-```sh
-curl -fsSL https://skandha.run | bash
-```
-or follow steps below:
-
-1. install all dependencies by running `yarn`
-2. build `yarn build && yarn bootstrap`
-3. `cp config.json.default config.json`
-4. edit `config.json`
-5. (optional) run local geth-node from `test/geth-dev`
-6. run `./skandha`
-7. Skandha will run for all chains available in `config.json`
-8. Networks will be available at `http://localhost:14337/{chainId}/` (e.g. for dev `http://localhost:14337/1337/`)
-
-## π³ How to run (a Docker image)
-
-1. `cp config.json.default config.json`
-2. edit `config.json`
-3. `docker build -t etherspot/skandha .`
-4. `docker run --mount type=bind,source="$(pwd)"/config.json,target=/usr/app/config.json,readonly -dp 14337:14337 etherspot/skandha start`
-
-
-## π Additional features
-- [x] Unsafe mode - bypass opcode & stake validation
-- [x] Redirect RPC - Redirect ETH rpc calls to the underlying execution client. This is needed if you use UserOp.js
-- [x] P2P - Exchange of UserOps between all the nodes in the network. Heavily inspired by the Lodestar's implementation of p2p (https://github.com/ChainSafe/lodestar/)
-
-### β‘οΈ CLI Options
-- `--unsafeMode` - enables unsafeMode
-- `--redirectRpc` - enables redirecting eth rpc calls
-
-## π Relayer Configuration
-
-#### config.json
-
-```json
-{
- "networks": {
- "dev": { # network Id (check packages/types/src/networks/networks.ts)
- "entryPoints": [ # supported entry points
- "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
- ],
- "relayer": "0xprivateKey", # relayer private key, can access from here or via environment variables (SKANDHA_MUMBAI_RELAYER | SKANDHA_DEV_RELAYER | etc.)
- "beneficiary": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", # fee collector, avaiable via env var (SKANDHA_MUMBAI_BENEFICIARY | etc)
- "rpcEndpoint": "http://localhost:8545", # rpc provider, also available via env variable (SKANDHA_MUMBAI_RPC | etc)
- "minInclusionDenominator": 10, # optional, see EIP-4337
- "throttlingSlack": 10, # optional, see EIP-4337
- "banSlack": 10 # optional, see EIP-4337
- "minSignerBalance": 1, # optional, default is 0.1 ETH. If the relayer's balance drops lower than this, it will be selected as a fee collector
- "multicall": "0x", # optional, address of multicall3 contract, default is 0xcA11bde05977b3631167028862bE2a173976CA11 (see https://github.com/mds1/multicall#multicall3-contract-addresses)
- }
- }
-}
-```
-
-#### Mempool_ID of the canonical mempool on various networks
-
-- Sepolia | QmdDwVFoEEcgv5qnaTB8ncnXGMnqrhnA5nYpRr4ouWe4AT | https://ipfs.io/ipfs/QmdDwVFoEEcgv5qnaTB8ncnXGMnqrhnA5nYpRr4ouWe4AT?filename=sepolia_canonical_mempool.yaml
+
+
Skandha
+
+
+
+
+
+
+
+
+ A modular, developer-friendly Typescript Bundler for Ethereum EIP-4337 Account Abstraction
+
+
+
+
+### Warning! This repo/software is under active development
+
+## βοΈ How to run (from Source code)
+
+Run with one-liner:
+
+```sh
+curl -fsSL https://skandha.run | bash
+```
+or follow steps below:
+
+1. install all dependencies by running `yarn`
+2. build `yarn build && yarn bootstrap`
+3. `cp config.json.default config.json`
+4. edit `config.json`
+5. (optional) run local geth-node from `test/geth-dev`
+6. run `./skandha`
+7. Skandha will run for all chains available in `config.json`
+8. Networks will be available at `http://localhost:14337/{chainId}/` (e.g. for dev `http://localhost:14337/1337/`)
+
+## π³ How to run (a Docker image)
+
+1. `cp config.json.default config.json`
+2. edit `config.json`
+3. `docker build -t etherspot/skandha .`
+4. `docker run --mount type=bind,source="$(pwd)"/config.json,target=/usr/app/config.json,readonly -dp 14337:14337 etherspot/skandha start`
+
+
+## π Additional features
+- [x] Unsafe mode - bypass opcode & stake validation
+- [x] Redirect RPC - Redirect ETH rpc calls to the underlying execution client. This is needed if you use UserOp.js
+- [x] P2P - Exchange of UserOps between all the nodes in the network. Heavily inspired by the Lodestar's implementation of p2p (https://github.com/ChainSafe/lodestar/)
+
+### β‘οΈ CLI Options
+- `--unsafeMode` - enables unsafeMode
+- `--redirectRpc` - enables redirecting eth rpc calls
+
+## π Relayer Configuration
+
+#### config.json
+
+```yaml
+{
+ "networks": {
+ "dev": { # network Id (check packages/types/src/networks/networks.ts)
+ "entryPoints": [ # supported entry points
+ "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ ],
+ "relayer": "0xprivateKey", # relayer private key, can access from here or via environment variables (SKANDHA_MUMBAI_RELAYER | SKANDHA_DEV_RELAYER | etc.)
+ "beneficiary": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", # fee collector, avaiable via env var (SKANDHA_MUMBAI_BENEFICIARY | etc)
+ "rpcEndpoint": "http://localhost:8545", # rpc provider, also available via env variable (SKANDHA_MUMBAI_RPC | etc)
+ "minInclusionDenominator": 10, # optional, see EIP-4337
+ "throttlingSlack": 10, # optional, see EIP-4337
+ "banSlack": 10 # optional, see EIP-4337
+ "minSignerBalance": 1, # optional, default is 0.1 ETH. If the relayer's balance drops lower than this, it will be selected as a fee collector
+ "multicall": "0xcA11bde05977b3631167028862bE2a173976CA11", # optional, multicall3 contract (see https://github.com/mds1/multicall#multicall3-contract-addresses)
+ "estimationStaticBuffer": 21000, # adds certain amount of gas to callGasLimit on estimation
+ "validationGasLimit": 10e6, # gas limit during simulateHandleOps and simulateValidation calls
+ "receiptLookupRange": 1024, # limits the block range of getUserOperationByHash and getUserOperationReceipt
+ "etherscanApiKey": "", # etherscan api is used to fetch gas prices
+ "conditionalTransactions": false, # enable conditional transactions
+ "rpcEndpointSubmit": "", # rpc endpoint that is used only during submission of a bundle
+ "gasPriceMarkup": 0, # adds % markup on reported gas price via skandha_getGasPrice, 10000 = 100.00%, 500 = 5%
+ "enforceGasPrice": false, # do not bundle userops with low gas prices
+ "enforceGasPriceThreshold": 1000, # gas price threshold in bps. If set to 500, userops' gas price is allowed to be 5% lower than the network's gas price
+ }
+ }
+}
+```
+
+#### Mempool_ID of the canonical mempool on various networks
+
+- Sepolia | QmdDwVFoEEcgv5qnaTB8ncnXGMnqrhnA5nYpRr4ouWe4AT | https://ipfs.io/ipfs/QmdDwVFoEEcgv5qnaTB8ncnXGMnqrhnA5nYpRr4ouWe4AT?filename=sepolia_canonical_mempool.yaml
- Mumbai | QmQfRyE9iVTBqZ17hPSP4tuMzaez83Y5wD874ymyRtj9VE | https://ipfs.io/ipfs/QmQfRyE9iVTBqZ17hPSP4tuMzaez83Y5wD874ymyRtj9VE?filename=mumbai_canonical_mempool.yaml
\ No newline at end of file
diff --git a/lerna.json b/lerna.json
index 79a03998..c09428df 100644
--- a/lerna.json
+++ b/lerna.json
@@ -4,7 +4,7 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
- "version": "0.0.24",
+ "version": "0.0.44",
"stream": "true",
"command": {
"version": {
diff --git a/package.json b/package.json
index b5f26655..bc8ceb5b 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
{
"name": "root",
"private": true,
- "version": "0.0.24",
+ "version": "0.0.44",
"engines": {
- "node": ">=18.0.0"
- },
- "scripts": {
+ "node": ">=18.0.0"
+ },
+ "scripts": {
"clean": "rm -rf ./packages/*/lib ./packages/*/*.tsbuildinfo",
"bootstrap": "lerna bootstrap & lerna link",
"prebuild": "yarn workspace types run build",
diff --git a/packages/api/package.json b/packages/api/package.json
index 502a7d4e..49afe62f 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -1,6 +1,6 @@
{
"name": "api",
- "version": "0.0.24",
+ "version": "0.0.44",
"description": "The API module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://https://github.com/etherspot/skandha#readme",
@@ -35,12 +35,12 @@
"class-transformer": "0.5.1",
"class-validator": "0.14.0",
"ethers": "5.7.2",
- "executor": "^0.0.24",
+ "executor": "^0.0.44",
"fastify": "4.14.1",
"pino": "8.11.0",
"pino-pretty": "10.0.0",
"reflect-metadata": "0.1.13",
- "types": "^0.0.24"
+ "types": "^0.0.44"
},
"devDependencies": {
"@types/connect": "3.4.35"
diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts
index 5a79f813..92141c8a 100644
--- a/packages/api/src/app.ts
+++ b/packages/api/src/app.ts
@@ -10,6 +10,7 @@ import logger from "./logger";
import {
BundlerRPCMethods,
CustomRPCMethods,
+ HttpStatus,
RedirectedRPCMethods,
} from "./constants";
import { EthAPI, DebugAPI, Web3API, RedirectAPI } from "./modules";
@@ -131,11 +132,10 @@ export class ApiApp {
if (this.redirectRpc && method in RedirectedRPCMethods) {
const body = await redirectApi.redirect(method, params);
- return res.status(200).send({
- jsonrpc,
- id,
- ...body,
- });
+ if (body.error) {
+ return res.status(HttpStatus.OK).send({ ...body, id });
+ }
+ return res.status(HttpStatus.OK).send({ jsonrpc, id, ...body });
}
if (result === undefined) {
@@ -152,12 +152,20 @@ export class ApiApp {
entryPoint: params[1],
});
break;
- case BundlerRPCMethods.eth_estimateUserOperationGas:
- result = await ethApi.estimateUserOperationGas({
- userOp: params[0],
- entryPoint: params[1],
- });
- break;
+ case BundlerRPCMethods.eth_estimateUserOperationGas: {
+ if (this.testingMode) {
+ result = await ethApi.estimateUserOpGasAndValidateSignature({
+ userOp: params[0],
+ entryPoint: params[1],
+ });
+ } else {
+ result = await ethApi.estimateUserOperationGas({
+ userOp: params[0],
+ entryPoint: params[1],
+ });
+ }
+ break;
+ }
case BundlerRPCMethods.eth_getUserOperationReceipt:
result = await ethApi.getUserOperationReceipt(params[0]);
break;
@@ -176,6 +184,21 @@ export class ApiApp {
case CustomRPCMethods.skandha_getGasPrice:
result = await skandhaApi.getGasPrice();
break;
+ case CustomRPCMethods.skandha_feeHistory:
+ result = await skandhaApi.getFeeHistory({
+ entryPoint: params[0],
+ blockCount: params[1],
+ newestBlock: params[2],
+ });
+ break;
+ case CustomRPCMethods.skandha_config:
+ result = await skandhaApi.getConfig();
+ // skip hexlify for this particular rpc
+ return res.status(HttpStatus.OK).send({
+ jsonrpc,
+ id,
+ result,
+ });
default:
throw new RpcError(
`Method ${method} is not supported`,
@@ -185,7 +208,7 @@ export class ApiApp {
}
result = deepHexlify(result);
- return res.status(200).send({
+ return res.status(HttpStatus.OK).send({
jsonrpc,
id,
result,
diff --git a/packages/api/src/constants.ts b/packages/api/src/constants.ts
index 9651022c..e4f87ece 100644
--- a/packages/api/src/constants.ts
+++ b/packages/api/src/constants.ts
@@ -1,6 +1,8 @@
export const CustomRPCMethods = {
skandha_validateUserOperation: "skandha_validateUserOperation",
skandha_getGasPrice: "skandha_getGasPrice",
+ skandha_config: "skandha_config",
+ skandha_feeHistory: "skandha_feeHistory",
};
export const BundlerRPCMethods = {
@@ -59,3 +61,8 @@ export const RedirectedRPCMethods = {
eth_maxPriorityFeePerGas: "eth_maxPriorityFeePerGas",
eth_sendRawTransaction: "eth_sendRawTransaction",
};
+
+export enum HttpStatus {
+ OK = 200,
+ INTERNAL_SERVER_ERROR = 500,
+}
diff --git a/packages/api/src/dto/FeeHistory.dto.ts b/packages/api/src/dto/FeeHistory.dto.ts
new file mode 100644
index 00000000..606f75ea
--- /dev/null
+++ b/packages/api/src/dto/FeeHistory.dto.ts
@@ -0,0 +1,15 @@
+import { IsEthereumAddress, ValidateIf } from "class-validator";
+import { BigNumberish } from "ethers";
+import { IsBigNumber } from "../utils/is-bignumber";
+
+export class FeeHistoryArgs {
+ @IsEthereumAddress()
+ entryPoint!: string;
+
+ @IsBigNumber()
+ blockCount!: BigNumberish;
+
+ @ValidateIf((o) => o.newestBlock != "latest")
+ @IsBigNumber()
+ newestBlock!: BigNumberish | string;
+}
diff --git a/packages/api/src/modules/eth.ts b/packages/api/src/modules/eth.ts
index 7ae6f650..c896f990 100644
--- a/packages/api/src/modules/eth.ts
+++ b/packages/api/src/modules/eth.ts
@@ -21,6 +21,15 @@ export class EthAPI {
return await this.ethModule.sendUserOperation(args);
}
+ /**
+ * @params args sama as in sendUserOperation
+ */
+ async estimateUserOpGasAndValidateSignature(
+ args: SendUserOperationGasArgs
+ ): Promise {
+ return await this.ethModule.estimateUserOperationGasWithSignature(args);
+ }
+
/**
* Estimate the gas values for a UserOperation. Given UserOperation optionally without gas limits and gas prices, return the needed gas limits.
* The signature field is ignored by the wallet, so that the operation will not require userβs approval.
diff --git a/packages/api/src/modules/redirect.ts b/packages/api/src/modules/redirect.ts
index 622f95c3..6cf33c8d 100644
--- a/packages/api/src/modules/redirect.ts
+++ b/packages/api/src/modules/redirect.ts
@@ -22,8 +22,9 @@ export class RedirectAPI {
/** NETHERMIND ERROR PARSING */
if (
- body.error.data.startsWith("Reverted ") &&
- body.error.code == -32015
+ body.error.data &&
+ body.error.code == -32015 &&
+ body.error.data.startsWith("Reverted ")
) {
body.error.code = 3;
body.error.message = "execution reverted";
diff --git a/packages/api/src/modules/skandha.ts b/packages/api/src/modules/skandha.ts
index fec09a54..979b475f 100644
--- a/packages/api/src/modules/skandha.ts
+++ b/packages/api/src/modules/skandha.ts
@@ -1,8 +1,15 @@
import { Eth } from "executor/lib/modules/eth";
-import { GetGasPriceResponse } from "types/lib/api/interfaces";
+import {
+ GetConfigResponse,
+ GetFeeHistoryResponse,
+ GetGasPriceResponse,
+} from "types/lib/api/interfaces";
import { Skandha } from "executor/lib/modules";
+import RpcError from "types/lib/api/errors/rpc-error";
+import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
import { RpcMethodValidator } from "../utils/RpcMethodValidator";
import { SendUserOperationGasArgs } from "../dto/SendUserOperation.dto";
+import { FeeHistoryArgs } from "../dto/FeeHistory.dto";
export class SkandhaAPI {
constructor(private ethModule: Eth, private skandhaModule: Skandha) {}
@@ -19,7 +26,29 @@ export class SkandhaAPI {
return await this.ethModule.validateUserOp(args);
}
+ /**
+ * @param entryPoint Entry Point
+ * @param useropCount Number of blocks in the requested range
+ * @param newestBlock Highest number block of the requested range, or "latest"
+ * @returns
+ */
+ @RpcMethodValidator(FeeHistoryArgs)
+ async getFeeHistory(args: FeeHistoryArgs): Promise {
+ if (!this.ethModule.validateEntryPoint(args.entryPoint)) {
+ throw new RpcError("Invalid Entrypoint", RpcErrorCodes.INVALID_REQUEST);
+ }
+ return await this.skandhaModule.getFeeHistory(
+ args.entryPoint,
+ args.blockCount,
+ args.newestBlock
+ );
+ }
+
async getGasPrice(): Promise {
return await this.skandhaModule.getGasPrice();
}
+
+ async getConfig(): Promise {
+ return await this.skandhaModule.getConfig();
+ }
}
diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts
index 2bef36f0..dea6c8ff 100644
--- a/packages/api/src/server.ts
+++ b/packages/api/src/server.ts
@@ -3,6 +3,7 @@ import cors from "@fastify/cors";
import RpcError from "types/lib/api/errors/rpc-error";
import { ServerConfig } from "types/lib/api/interfaces";
import logger from "./logger";
+import { HttpStatus } from "./constants";
export class Server {
constructor(private app: FastifyInstance, private config: ServerConfig) {
@@ -70,16 +71,18 @@ export class Server {
data: err.data,
code: err.code,
};
- return res.status(200).send({
+ return res.status(HttpStatus.OK).send({
jsonrpc: body.jsonrpc,
id: body.id,
error,
});
}
- return res.status(err.statusCode ?? 500).send({
- error: "Unexpected behaviour",
- });
+ return res
+ .status(err.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR)
+ .send({
+ error: "Unexpected behaviour",
+ });
});
await this.app.listen({
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 40aa0a69..db9fe8c8 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "cli",
- "version": "0.0.24",
+ "version": "0.0.44",
"description": "> TODO: description",
"author": "zincoshine ",
"homepage": "https://https://github.com/etherspot/skandha#readme",
@@ -31,14 +31,13 @@
"url": "https://https://github.com/etherspot/skandha/issues"
},
"dependencies": {
- "api": "^0.0.24",
- "db": "^0.0.24",
- "executor": "^0.0.24",
- "node": "*",
+ "api": "^0.0.44",
+ "db": "^0.0.44",
+ "executor": "^0.0.44",
"find-up": "5.0.0",
"got": "12.5.3",
"js-yaml": "4.1.0",
- "types": "^0.0.24",
+ "types": "^0.0.44",
"yargs": "17.6.2"
},
"devDependencies": {
diff --git a/packages/cli/src/cmds/start/handler.ts b/packages/cli/src/cmds/start/handler.ts
new file mode 100644
index 00000000..dc263e37
--- /dev/null
+++ b/packages/cli/src/cmds/start/handler.ts
@@ -0,0 +1,86 @@
+/* eslint-disable no-console */
+import path, { resolve } from "node:path";
+import { Server } from "api/lib/server";
+import { ApiApp } from "api/lib/app";
+import logger from "api/lib/logger";
+import { Config } from "executor/lib/config";
+import {
+ Namespace,
+ getNamespaceByValue,
+ RocksDbController,
+ LocalDbController,
+} from "db/lib";
+import { ConfigOptions } from "executor/lib/interfaces";
+import { IDbController } from "types/lib";
+import { mkdir, readFile } from "../../util";
+import { IGlobalArgs } from "../../options";
+import { IBundlerArgs } from "./index";
+
+export async function bundlerHandler(
+ args: IBundlerArgs & IGlobalArgs
+): Promise {
+ const { dataDir, networksFile, testingMode, unsafeMode, redirectRpc } = args;
+
+ let config: Config;
+ try {
+ const configPath = path.resolve(dataDir, "..", networksFile);
+ const configOptions = readFile(configPath) as ConfigOptions;
+ config = new Config({
+ networks: configOptions.networks,
+ testingMode,
+ unsafeMode,
+ redirectRpc,
+ });
+ } catch (err) {
+ logger.debug("Config file not found. Proceeding with env vars...");
+ config = new Config({
+ networks: {},
+ testingMode,
+ unsafeMode,
+ redirectRpc,
+ });
+ }
+
+ if (unsafeMode) {
+ logger.warn(
+ "WARNING: Running in unsafe mode, skips opcode check and stake check"
+ );
+ }
+ if (redirectRpc) {
+ logger.warn(
+ "WARNING: RPC redirecting is enabled, redirects RPC whitelisted calls to RPC"
+ );
+ }
+
+ let db: IDbController;
+
+ if (testingMode) {
+ db = new LocalDbController(getNamespaceByValue(Namespace.userOps));
+ } else {
+ const dbPath = resolve(dataDir);
+ mkdir(dbPath);
+
+ db = new RocksDbController(
+ resolve(dataDir),
+ getNamespaceByValue(Namespace.userOps)
+ );
+ await db.start();
+ }
+
+ const server = await Server.init({
+ enableRequestLogging: args["api.enableRequestLogging"],
+ port: args["api.port"],
+ host: args["api.address"],
+ cors: args["api.cors"],
+ });
+
+ new ApiApp({
+ server: server.application,
+ config: config,
+ db,
+ testingMode,
+ redirectRpc,
+ });
+
+ await server.listen();
+}
diff --git a/packages/db/package.json b/packages/db/package.json
index a5e7be2e..cedaffa7 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -1,6 +1,6 @@
{
"name": "db",
- "version": "0.0.24",
+ "version": "0.0.44",
"description": "The DB module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://github.com/etherspot/etherspot-bundler#readme",
@@ -38,6 +38,6 @@
"devDependencies": {
"@types/rocksdb": "3.0.1",
"prettier": "^2.8.4",
- "types": "^0.0.24"
+ "types": "^0.0.44"
}
}
diff --git a/packages/executor/customTracer.js b/packages/executor/customTracer.js
deleted file mode 100644
index 0ef0cb79..00000000
--- a/packages/executor/customTracer.js
+++ /dev/null
@@ -1,171 +0,0 @@
-function tracer() {
- return {
- addrs: ['0xffffffffffffffffffffffffffffffffffffffff'],
- output: {
- '0xffffffffffffffffffffffffffffffffffffffff': {
- storage: {},
- number: 0,
- violation: {}
- }
- },
- prevOp: { op: '', data: '' },
- calls: [],
- numberCounter: 0,
-
- fault: function fault(log, db) {},
-
- result: function result(ctx, db) {
- return {
- trace: this.output,
- calls: this.calls
- };
- },
-
- step: function step(log, db) {
- var opcode = log.op.toString();
-
- if (log.getGas() < log.getCost()) { }
-
- if (this.prevOp.op == 'KECCAK256') {
- this.pKeccakAfter(log);
- }
-
- if (
- opcode.match(/^(EXT.*|CALL|CALLCODE|DELEGATECALL|STATICCALL|CREATE2)$/) !=
- null
- ) {
- var idx = opcode.startsWith("EXT") ? 0 : 1;
- var addr = toAddress(log.stack.peek(idx).toString(16));
- var hex = toHex(addr);
- if (this.output[hex] && this.output[hex].contractSize == null) {
- this.output[hex].contractSize = db.getCode(addr).length;
- }
- }
-
- if (log.getDepth() > 1) {
- if (this.prevOp.op === 'GAS' && !opcode.includes('CALL')) {
- const to = this.addrs[log.getDepth() - 1];
- if (!this.output[to].violation) {
- this.output[to].violation = {};
- }
- this.output[to].violation['GAS'] = true;
- }
- }
-
- this.prevOp.op = opcode;
- switch (opcode) {
- case 'SLOAD':
- case 'SSTORE':
- if (log.getDepth() !== 1) {
- this.pSloadStore(log);
- }
- break;
- case 'REVERT':
- case 'RETURN':
- if (log.getDepth() == 1) {
- var ofs = parseInt(log.stack.peek(0).toString());
- var len = parseInt(log.stack.peek(1).toString());
- var data = toHex(log.memory.slice(ofs, ofs + len)).slice(0, 1000);
- this.calls.push({
- type: opcode,
- gasUsed: 0,
- data: data,
- });
- this.addrs.pop();
- }
- break;
- case 'KECCAK256':
- if (log.getDepth() !== 1) {
- this.pKeccak(log);
- }
- break;
- case 'NUMBER':
- if (log.getDepth() == 1) {
- this.numberCounter += 1;
- break;
- }
- case 'GASPRICE':
- case 'GASLIMIT':
- case 'DIFFICULTY':
- case 'TIMESTAMP':
- case 'BASEFEE':
- case 'BLOCKHASH':
- case 'NUMBER':
- case 'SELFBALANCE':
- case 'BALANCE':
- case 'ORIGIN':
- case 'COINBASE':
- case 'SELFDESTRUCT':
- case 'RANDOM':
- case 'PREVRANDAO':
- case 'CREATE':
- case 'CREATE2': {
- const to = this.addrs[log.getDepth() - 1];
- if (!this.output[to].violation[opcode]) {
- this.output[to].violation[opcode] = 0;
- }
- this.output[to].violation[opcode] += 1;
- break;
- }
- default:
- break;
- }
- },
-
- pSloadStore: function(log) {
- var key = log.stack.peek(0).toString(16);
- const addr = log.contract.getAddress();
- const to = toHex(addr);
- if (!this.output[to].storage[key]) {
- this.output[to].storage[key] = 0;
- }
- this.output[to].storage[key] += 1;
- },
-
- pKeccak: function(log) {
- var _ofs = log.stack.peek(0);
- var _len = log.stack.peek(1);
- this.prevOp.data = toHex(log.memory.slice(_ofs, _ofs + _len));
- },
-
- pKeccakAfter: function(log) {
- var input = this.prevOp.data;
- var hash = log.stack.peek(0).toString(16);
- var to = this.addrs[log.getDepth() - 1];
- this.prevOp.op = '';
- if (!this.output[to].keccak) {
- this.output[to].keccak = {};
- }
- this.output[to].keccak[input] = hash;
- },
-
- enter: function enter(frame) {
- var to = toHex(frame.getTo());
- this.calls.push({
- type: frame.getType(),
- from: toHex(frame.getFrom()),
- to: to,
- method: toHex(frame.getInput()).slice(0, 10),
- gas: frame.getGas(),
- value: frame.getValue(),
- });
- this.addrs.push(to);
- this.initStorage(to);
- },
-
- exit: function exit(frame) {
- this.calls.push({
- type: frame.getError() != null ? "REVERT" : "RETURN",
- gasUsed: frame.getGasUsed(),
- data: toHex(frame.getOutput()).slice(0, 1000),
- });
- this.addrs.pop();
- },
-
- initStorage: function(to) {
- if (!this.output[to]) {
- this.output[to] = { storage: {}, number: this.numberCounter, violation: {} };
- }
- },
- }
-}
\ No newline at end of file
diff --git a/packages/executor/package.json b/packages/executor/package.json
index 14a5dc82..bba214f5 100644
--- a/packages/executor/package.json
+++ b/packages/executor/package.json
@@ -1,6 +1,6 @@
{
"name": "executor",
- "version": "0.0.24",
+ "version": "0.0.44",
"description": "The Relayer module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://https://github.com/etherspot/skandha#readme",
@@ -33,7 +33,7 @@
"dependencies": {
"async-mutex": "0.4.0",
"ethers": "5.7.2",
- "params": "^0.0.24",
- "types": "^0.0.24"
+ "params": "^0.0.44",
+ "types": "^0.0.44"
}
}
diff --git a/packages/executor/src/config.ts b/packages/executor/src/config.ts
index 2abb3626..9fcf9da0 100644
--- a/packages/executor/src/config.ts
+++ b/packages/executor/src/config.ts
@@ -13,12 +13,14 @@ export class Config {
networks: Networks;
testingMode: boolean;
unsafeMode: boolean;
+ redirectRpc: boolean;
constructor(private config: ConfigOptions) {
- this.supportedNetworks = this.parseSupportedNetworks();
- this.networks = this.parseNetworkConfigs();
this.testingMode = config.testingMode ?? false;
this.unsafeMode = config.unsafeMode ?? false;
+ this.redirectRpc = config.redirectRpc ?? false;
+ this.supportedNetworks = this.parseSupportedNetworks();
+ this.networks = this.parseNetworkConfigs();
}
getNetworkProvider(network: NetworkName): providers.JsonRpcProvider | null {
@@ -88,6 +90,9 @@ export class Config {
}
private parseSupportedNetworks(): NetworkName[] {
+ if (this.testingMode) {
+ return ["dev"];
+ }
const envNetworks = NETWORKS_ENV();
if (envNetworks) {
return envNetworks.map((key) => key as NetworkName);
@@ -131,8 +136,43 @@ export class Config {
conf.receiptLookupRange || bundlerDefaultConfigs.receiptLookupRange
)
);
+ conf.conditionalTransactions = Boolean(
+ fromEnvVar(
+ network,
+ "CONDITIONAL_TRANSACTIONS",
+ conf.conditionalTransactions ||
+ bundlerDefaultConfigs.conditionalTransactions
+ )
+ );
+ conf.rpcEndpointSubmit = fromEnvVar(
+ network,
+ "RPC_SUBMIT",
+ conf.rpcEndpointSubmit || bundlerDefaultConfigs.rpcEndpointSubmit
+ );
+ conf.gasPriceMarkup = Number(
+ fromEnvVar(
+ network,
+ "GAS_PRICE_MARKUP",
+ conf.gasPriceMarkup || bundlerDefaultConfigs.gasPriceMarkup
+ )
+ );
+ conf.enforceGasPrice = Boolean(
+ fromEnvVar(
+ network,
+ "ENFORCE_GAS_PRICE",
+ conf.enforceGasPrice || bundlerDefaultConfigs.enforceGasPrice
+ )
+ );
+ conf.enforceGasPriceThreshold = Number(
+ fromEnvVar(
+ network,
+ "ENFORCE_GAS_PRICE_THRESHOLD",
+ conf.enforceGasPriceThreshold ||
+ bundlerDefaultConfigs.enforceGasPriceThreshold
+ )
+ );
- return Object.assign(bundlerDefaultConfigs, conf);
+ return Object.assign({}, bundlerDefaultConfigs, conf);
}
}
@@ -142,11 +182,15 @@ const bundlerDefaultConfigs: BundlerConfig = {
banSlack: 10,
minSignerBalance: utils.parseEther("0.1"),
multicall: "0xcA11bde05977b3631167028862bE2a173976CA11", // default multicall address
- estimationBaseFeeDivisor: 25,
estimationStaticBuffer: 21000,
validationGasLimit: 10e6,
receiptLookupRange: 1024,
etherscanApiKey: "",
+ conditionalTransactions: false,
+ rpcEndpointSubmit: "",
+ gasPriceMarkup: 0,
+ enforceGasPrice: false,
+ enforceGasPriceThreshold: 1000,
};
const NETWORKS_ENV = (): string[] | undefined => {
diff --git a/packages/executor/src/executor.ts b/packages/executor/src/executor.ts
index 9b2f74b8..137328bb 100644
--- a/packages/executor/src/executor.ts
+++ b/packages/executor/src/executor.ts
@@ -118,7 +118,7 @@ export class Executor {
this.skandha = new Skandha(
this.network,
this.provider,
- this.networkConfig,
+ this.config,
this.logger
);
@@ -126,5 +126,19 @@ export class Executor {
this.bundlingService.setBundlingMode("manual");
this.logger.info(`${this.network}: set to manual bundling mode`);
}
+
+ if (this.networkConfig.conditionalTransactions) {
+ this.logger.info(`${this.network}: [x] CONDITIONAL TRANSACTIONS`);
+ }
+
+ if (this.networkConfig.rpcEndpointSubmit) {
+ this.logger.info(
+ `${this.network}: [x] SEPARATE RPC FOR SUBMITTING BUNDLES`
+ );
+ }
+
+ if (this.networkConfig.enforceGasPrice) {
+ this.logger.info(`${this.network}: [x] ENFORCING GAS PRICES`);
+ }
}
}
diff --git a/packages/executor/src/interfaces.ts b/packages/executor/src/interfaces.ts
index 7cb3faee..746a6a2d 100644
--- a/packages/executor/src/interfaces.ts
+++ b/packages/executor/src/interfaces.ts
@@ -1,6 +1,7 @@
-import { BigNumberish, BytesLike } from "ethers";
+import { BigNumber, BigNumberish, BytesLike } from "ethers";
import { NetworkName } from "types/lib";
import { Executor } from "./executor";
+import { MempoolEntry } from "./entities/MempoolEntry";
export interface Log {
blockNumber: number;
@@ -29,7 +30,7 @@ export interface TracerTracer {
contractSize?: number;
number?: number;
storage?: {
- [slot: string]: number;
+ [slot: string]: number | string;
};
keccak?: {
[slot: string]: any;
@@ -99,10 +100,6 @@ export interface NetworkConfig {
banSlack: number;
minSignerBalance: BigNumberish;
multicall: string;
- // reduces baseFee by a given number in % before dividing paid gas
- // use this as a buffer to callGasLimit
- // 25% by default
- estimationBaseFeeDivisor: number;
// adds certain amount of gas to callGasLimit
// 21000 by default
estimationStaticBuffer: number;
@@ -116,6 +113,21 @@ export interface NetworkConfig {
// etherscan api is used to fetch gas prices
// default = "" (empty string)
etherscanApiKey: string;
+ // enables contidional rpc
+ conditionalTransactions: boolean;
+ // rpc endpoint that is used only during submission of a bundle
+ rpcEndpointSubmit: string;
+ // adds % markup on reported gas price via skandha_getGasPrice
+ // 10000 = 100.00%
+ // 500 = 5%
+ gasPriceMarkup: number;
+ // do not bundle userops with low gas prices
+ enforceGasPrice: boolean;
+ // gas price threshold in bps
+ // 10000 = 100.00%, 500 = 5%
+ // if set to 500, then the userop's gas price is allowed to be
+ // 5% lower than the networks gas prices
+ enforceGasPriceThreshold: number;
}
export type BundlerConfig = Omit<
@@ -131,4 +143,57 @@ export interface ConfigOptions {
networks: Networks;
testingMode?: boolean;
unsafeMode: boolean;
+ redirectRpc: boolean;
+}
+
+export interface SlotMap {
+ [slot: string]: string;
+}
+
+export interface StorageMap {
+ [address: string]: string | SlotMap;
+}
+
+export interface ReferencedCodeHashes {
+ // addresses accessed during this user operation
+ addresses: string[];
+ // keccak over the code of all referenced addresses
+ hash: string;
+}
+
+export interface UserOpValidationResult {
+ returnInfo: {
+ preOpGas: BigNumberish;
+ prefund: BigNumberish;
+ sigFailed: boolean;
+ validAfter: number;
+ validUntil: number;
+ };
+
+ senderInfo: StakeInfo;
+ factoryInfo?: StakeInfo;
+ paymasterInfo?: StakeInfo;
+ aggregatorInfo?: StakeInfo;
+ referencedContracts?: ReferencedCodeHashes;
+ storageMap?: StorageMap;
+}
+
+export interface ExecutionResult {
+ preOpGas: BigNumber;
+ paid: number;
+ validAfter: number;
+ validUntil: number;
+ targetSuccess: boolean;
+ targetResult: string;
+}
+
+export interface StakeInfo {
+ addr: string;
+ stake: BigNumberish;
+ unstakeDelaySec: BigNumberish;
+}
+
+export interface Bundle {
+ entries: MempoolEntry[];
+ storageMap: StorageMap;
}
diff --git a/packages/executor/src/modules/eth.ts b/packages/executor/src/modules/eth.ts
index b3a0601e..dc504377 100644
--- a/packages/executor/src/modules/eth.ts
+++ b/packages/executor/src/modules/eth.ts
@@ -3,7 +3,7 @@ import { arrayify, hexlify } from "ethers/lib/utils";
import RpcError from "types/lib/api/errors/rpc-error";
import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
import {
- EntryPoint,
+ IEntryPoint,
UserOperationEventEvent,
UserOperationStruct,
} from "types/lib/executor/contracts/EntryPoint";
@@ -12,11 +12,12 @@ import {
UserOperationByHashResponse,
UserOperationReceipt,
} from "types/lib/api/interfaces";
-import { EntryPoint__factory } from "types/lib/executor/contracts/factories";
+import { IEntryPoint__factory } from "types/lib/executor/contracts/factories";
import { NetworkName } from "types/lib";
import { INodeAPI } from "types/lib/node";
import { IPVGEstimator } from "params/lib/types/IPVGEstimator";
import { estimateOptimismPVG, estimateArbitrumPVG } from "params/lib";
+import { getGasFee } from "params/lib";
import { NetworkConfig } from "../interfaces";
import { deepHexlify, getUserOpHash, packUserOp } from "../utils";
import { UserOpValidationService, MempoolService } from "../services";
@@ -39,9 +40,7 @@ export class Eth {
private logger: Logger,
private nodeApi?: INodeAPI
) {
- if (
- ["arbitrum", "arbitrumNitro", "arbitrumNova"].includes(this.networkName)
- ) {
+ if (["arbitrum", "arbitrumNova"].includes(this.networkName)) {
this.pvgEstimator = estimateArbitrumPVG(this.provider);
}
@@ -73,12 +72,13 @@ export class Eth {
}
this.logger.debug("Validating user op before sending to mempool...");
+ await this.userOpValidationService.validateGasFee(userOp);
const validationResult =
await this.userOpValidationService.simulateValidation(userOp, entryPoint);
// TODO: fetch aggregator
this.logger.debug("Validation successful. Saving in mempool...");
- const entryPointContract = EntryPoint__factory.connect(
+ const entryPointContract = IEntryPoint__factory.connect(
entryPoint,
this.provider
);
@@ -129,35 +129,33 @@ export class Eth {
const userOpComplemented: UserOperationStruct = {
paymasterAndData: userOp.paymasterAndData ?? "0x",
- verificationGasLimit: 10e6,
- maxFeePerGas: 0,
- maxPriorityFeePerGas: 0,
- preVerificationGas: 0,
...userOp,
+ callGasLimit: BigNumber.from(10e6),
+ preVerificationGas: BigNumber.from(1e6),
+ verificationGasLimit: BigNumber.from(10e6),
+ maxFeePerGas: 1,
+ maxPriorityFeePerGas: 1,
};
- if (BigNumber.from(userOpComplemented.callGasLimit).eq(0)) {
- userOpComplemented.callGasLimit = BigNumber.from(10e6);
- }
- if (BigNumber.from(userOpComplemented.preVerificationGas).eq(0)) {
- userOpComplemented.preVerificationGas = BigNumber.from(1e6);
- }
- if (BigNumber.from(userOpComplemented.verificationGasLimit).eq(0)) {
- userOpComplemented.verificationGasLimit = BigNumber.from(10e6);
- }
-
- if (userOpComplemented.signature === "0x") {
- userOpComplemented.signature = await this.getDummySignature({
- userOp: userOpComplemented,
- entryPoint: args.entryPoint,
- });
- }
+ userOpComplemented.signature = await this.getDummySignature({
+ userOp: userOpComplemented,
+ entryPoint: args.entryPoint,
+ });
const returnInfo = await this.userOpValidationService.validateForEstimation(
userOpComplemented,
entryPoint
);
+ // eslint-disable-next-line prefer-const
+ let { preOpGas, validAfter, validUntil, paid } = returnInfo;
+
+ const verificationGasLimit = BigNumber.from(preOpGas)
+ .sub(userOpComplemented.preVerificationGas)
+ .mul(130)
+ .div(100) // 130% markup
+ .toNumber();
+
let preVerificationGas: BigNumberish = this.calcPreVerificationGas(userOp);
userOpComplemented.preVerificationGas = preVerificationGas;
if (this.pvgEstimator) {
@@ -168,35 +166,19 @@ export class Eth {
);
}
- // eslint-disable-next-line prefer-const
- let { preOpGas, validAfter, validUntil, paid } = returnInfo;
-
let callGasLimit: BigNumber = BigNumber.from(0);
// calculate callGasLimit based on paid fee
- const block = await this.provider.getBlock("latest");
- const { estimationBaseFeeDivisor, estimationStaticBuffer } = this.config;
- const estimatedBaseFee = block.baseFeePerGas
- ?.mul(100)
- .div(100 + (estimationBaseFeeDivisor || 0));
-
- if (!estimatedBaseFee) {
- callGasLimit = BigNumber.from(paid).div(userOpComplemented.maxFeePerGas);
- } else {
- const lhs = BigNumber.from(userOpComplemented.maxFeePerGas);
- const rhs = estimatedBaseFee.add(userOpComplemented.maxPriorityFeePerGas);
- const divisor = lhs.lt(rhs) ? lhs : rhs; // min(maxFeePerGas, base + priorityFee)
- callGasLimit = BigNumber.from(paid).div(divisor);
- }
+ const { estimationStaticBuffer } = this.config;
+ callGasLimit = BigNumber.from(paid).div(userOpComplemented.maxFeePerGas);
callGasLimit = callGasLimit.sub(preOpGas).add(estimationStaticBuffer || 0);
if (callGasLimit.lt(0)) {
callGasLimit = BigNumber.from(estimationStaticBuffer || 0);
}
- // }
//< checking for execution revert
- const estimatedCallGasLimit = await this.provider
+ await this.provider
.estimateGas({
from: entryPoint,
to: userOp.sender,
@@ -209,27 +191,75 @@ export class Eth {
});
//>
- // if calculation on paid fee failed
- // fallback to estimateGas
- if (callGasLimit.eq(0)) {
- callGasLimit = estimatedCallGasLimit;
- }
+ // Binary search gas limits
+ const userOpToEstimate: UserOperationStruct = {
+ ...userOpComplemented,
+ preVerificationGas,
+ verificationGasLimit,
+ callGasLimit,
+ };
- const verificationGas = BigNumber.from(preOpGas).toNumber();
- validAfter = BigNumber.from(validAfter);
- validUntil = BigNumber.from(validUntil);
- if (validUntil === BigNumber.from(0)) {
- validUntil = undefined;
- }
- if (validAfter === BigNumber.from(0)) {
- validAfter = undefined;
- }
+ const gasFee = await getGasFee(
+ this.networkName,
+ this.provider,
+ this.config.etherscanApiKey,
+ {
+ entryPoint,
+ userOp: userOpToEstimate,
+ }
+ );
return {
preVerificationGas,
- verificationGas,
- validAfter,
- validUntil,
+ verificationGasLimit: userOpToEstimate.verificationGasLimit,
+ verificationGas: userOpToEstimate.verificationGasLimit,
+ validAfter: validAfter ? BigNumber.from(validAfter) : undefined,
+ validUntil: validUntil ? BigNumber.from(validUntil) : undefined,
+ callGasLimit: userOpToEstimate.callGasLimit,
+ maxFeePerGas: gasFee.maxFeePerGas,
+ maxPriorityFeePerGas: gasFee.maxPriorityFeePerGas,
+ };
+ }
+
+ /**
+ * Estimates userop gas and validates the signature
+ * @param args same as in sendUserOperation
+ */
+ async estimateUserOperationGasWithSignature(
+ args: SendUserOperationGasArgs
+ ): Promise {
+ const { userOp, entryPoint } = args;
+ if (!this.validateEntryPoint(entryPoint)) {
+ throw new RpcError("Invalid Entrypoint", RpcErrorCodes.INVALID_REQUEST);
+ }
+
+ const { returnInfo } =
+ await this.userOpValidationService.validateForEstimationWithSignature(
+ userOp,
+ entryPoint
+ );
+ const { preOpGas, validAfter, validUntil } = returnInfo;
+ const callGasLimit = await this.provider
+ .estimateGas({
+ from: entryPoint,
+ to: userOp.sender,
+ data: userOp.callData,
+ })
+ .then((b) => b.toNumber())
+ .catch((err) => {
+ const message =
+ err.message.match(/reason="(.*?)"/)?.at(1) ?? "execution reverted";
+ throw new RpcError(message, RpcErrorCodes.EXECUTION_REVERTED);
+ });
+ // const preVerificationGas = this.calcPreVerificationGas(userOp);
+ const verificationGasLimit = BigNumber.from(preOpGas).toNumber();
+
+ return {
+ preVerificationGas: this.calcPreVerificationGas(userOp),
+ verificationGasLimit,
+ verificationGas: verificationGasLimit,
+ validAfter: BigNumber.from(validAfter),
+ validUntil: BigNumber.from(validUntil),
callGasLimit,
};
}
@@ -392,7 +422,7 @@ export class Eth {
);
}
- private validateEntryPoint(entryPoint: string): boolean {
+ validateEntryPoint(entryPoint: string): boolean {
return (
this.config.entryPoints.findIndex(
(ep) => ep.toLowerCase() === entryPoint.toLowerCase()
@@ -445,10 +475,10 @@ export class Eth {
private async getUserOperationEvent(
userOpHash: string
- ): Promise<[EntryPoint | null, UserOperationEventEvent | null]> {
+ ): Promise<[IEntryPoint | null, UserOperationEventEvent | null]> {
let event: UserOperationEventEvent[] = [];
for (const addr of await this.getSupportedEntryPoints()) {
- const contract = EntryPoint__factory.connect(addr, this.provider);
+ const contract = IEntryPoint__factory.connect(addr, this.provider);
try {
const blockNumber = await this.provider.getBlockNumber();
let fromBlockNumber = blockNumber - this.config.receiptLookupRange;
diff --git a/packages/executor/src/modules/skandha.ts b/packages/executor/src/modules/skandha.ts
index 0c7979c3..dcfb51f9 100644
--- a/packages/executor/src/modules/skandha.ts
+++ b/packages/executor/src/modules/skandha.ts
@@ -1,25 +1,42 @@
-import { ethers } from "ethers";
+import { BigNumber, BigNumberish, ethers } from "ethers";
import { NetworkName } from "types/lib";
-import { GetGasPriceResponse } from "types/lib/api/interfaces";
+import {
+ GetConfigResponse,
+ GetFeeHistoryResponse,
+ GetGasPriceResponse,
+} from "types/lib/api/interfaces";
import RpcError from "types/lib/api/errors/rpc-error";
import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
+import { GasPriceMarkupOne } from "params/lib";
+import { getGasFee } from "params/lib";
+import { IEntryPoint__factory } from "types/lib/executor/contracts";
+import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
import { Logger, NetworkConfig } from "../interfaces";
-import { getGasFee } from "../utils/getGasFee";
+import { Config } from "../config";
// custom features of Skandha
export class Skandha {
+ networkConfig: NetworkConfig;
+
constructor(
private networkName: NetworkName,
private provider: ethers.providers.JsonRpcProvider,
- private config: NetworkConfig,
+ private config: Config,
private logger: Logger
- ) {}
+ ) {
+ const networkConfig = this.config.getNetworkConfig(this.networkName);
+ if (!networkConfig) {
+ throw new Error("No network config");
+ }
+ this.networkConfig = networkConfig;
+ }
async getGasPrice(): Promise {
+ const multiplier = this.networkConfig.gasPriceMarkup;
const gasFee = await getGasFee(
this.networkName,
this.provider,
- this.config.etherscanApiKey
+ this.networkConfig.etherscanApiKey
);
let { maxPriorityFeePerGas, maxFeePerGas } = gasFee;
@@ -36,9 +53,118 @@ export class Skandha {
}
}
+ if (multiplier && !BigNumber.from(multiplier).eq(0)) {
+ const bnMultiplier = GasPriceMarkupOne.add(multiplier);
+ maxFeePerGas = bnMultiplier.mul(maxFeePerGas).div(GasPriceMarkupOne);
+ maxPriorityFeePerGas = bnMultiplier
+ .mul(maxPriorityFeePerGas)
+ .div(GasPriceMarkupOne);
+ }
+
return {
maxPriorityFeePerGas,
maxFeePerGas,
};
}
+
+ async getConfig(): Promise {
+ const wallet = this.config.getRelayer(this.networkName);
+ const hasEtherscanApiKey = Boolean(this.networkConfig.etherscanApiKey);
+ const hasExecutionRpc = Boolean(this.networkConfig.rpcEndpointSubmit);
+ return {
+ flags: {
+ unsafeMode: this.config.unsafeMode,
+ testingMode: this.config.testingMode,
+ redirectRpc: this.config.redirectRpc,
+ },
+ entryPoints: this.networkConfig.entryPoints,
+ beneficiary: this.networkConfig.beneficiary,
+ relayer: wallet ? await wallet.getAddress() : "",
+ minInclusionDenominator: BigNumber.from(
+ this.networkConfig.minInclusionDenominator
+ ).toNumber(),
+ throttlingSlack: BigNumber.from(
+ this.networkConfig.throttlingSlack
+ ).toNumber(),
+ banSlack: BigNumber.from(this.networkConfig.banSlack).toNumber(),
+ minSignerBalance: `${ethers.utils.formatEther(
+ this.networkConfig.minSignerBalance
+ )} eth`,
+ multicall: this.networkConfig.multicall,
+ estimationStaticBuffer: BigNumber.from(
+ this.networkConfig.estimationStaticBuffer
+ ).toNumber(),
+ validationGasLimit: BigNumber.from(
+ this.networkConfig.validationGasLimit
+ ).toNumber(),
+ receiptLookupRange: BigNumber.from(
+ this.networkConfig.receiptLookupRange
+ ).toNumber(),
+ etherscanApiKey: hasEtherscanApiKey,
+ conditionalTransactions: this.networkConfig.conditionalTransactions,
+ rpcEndpointSubmit: hasExecutionRpc,
+ gasPriceMarkup: BigNumber.from(
+ this.networkConfig.gasPriceMarkup
+ ).toNumber(),
+ enforceGasPrice: this.networkConfig.enforceGasPrice,
+ enforceGasPriceThreshold: BigNumber.from(
+ this.networkConfig.enforceGasPriceThreshold
+ ).toNumber(),
+ };
+ }
+
+ /**
+ * see eth_feeHistory
+ * @param entryPoint Entry Point contract
+ * @param blockCount Number of blocks in the requested range
+ * @param newestBlock Highest number block of the requested range, or "latest"
+ */
+ async getFeeHistory(
+ entryPoint: string,
+ blockCount: BigNumberish,
+ newestBlock: BigNumberish | string
+ ): Promise {
+ const toBlockInfo = await this.provider.getBlock(newestBlock.toString());
+ const fromBlockNumber = BigNumber.from(toBlockInfo.number)
+ .sub(blockCount)
+ .toNumber();
+ const contract = IEntryPoint__factory.connect(entryPoint, this.provider);
+ const events = await contract.queryFilter(
+ contract.filters.UserOperationEvent(),
+ fromBlockNumber,
+ toBlockInfo.number
+ );
+ const txReceipts = await Promise.all(
+ events.map((event) => event.getTransaction())
+ );
+ const txDecoded = txReceipts
+ .map((receipt) => {
+ try {
+ return contract.interface.decodeFunctionData(
+ "handleOps",
+ receipt.data
+ );
+ } catch (err) {
+ this.logger.error(err);
+ return null;
+ }
+ })
+ .filter((el) => el !== null);
+
+ const actualGasPrice = events.map((event) =>
+ BigNumber.from(event.args.actualGasCost).div(event.args.actualGasUsed)
+ );
+ const userops = txDecoded
+ .map((handleOps) => handleOps!.ops as UserOperationStruct[])
+ .reduce((p, c) => {
+ return p.concat(c);
+ }, []);
+ return {
+ actualGasPrice,
+ maxFeePerGas: userops.map((userop) => userop.maxFeePerGas),
+ maxPriorityFeePerGas: userops.map(
+ (userop) => userop.maxPriorityFeePerGas
+ ),
+ };
+ }
}
diff --git a/packages/executor/src/services/BundlingService.ts b/packages/executor/src/services/BundlingService.ts
index d662388a..768c848b 100644
--- a/packages/executor/src/services/BundlingService.ts
+++ b/packages/executor/src/services/BundlingService.ts
@@ -1,23 +1,28 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
-import { BigNumber, BigNumberish, ethers, providers } from "ethers";
+import { BigNumber, ethers, providers } from "ethers";
import { NetworkName } from "types/lib";
-import { EntryPoint__factory } from "types/lib/executor/contracts/factories";
-import { EntryPoint } from "types/lib/executor/contracts/EntryPoint";
+import { IEntryPoint__factory } from "types/lib/executor/contracts/factories";
import { Mutex } from "async-mutex";
import { SendBundleReturn } from "types/lib/executor";
import { IMulticall3__factory } from "types/lib/executor/contracts/factories/IMulticall3__factory";
-import { chainsWithoutEIP1559 } from "params/lib";
+import { GasPriceMarkupOne, chainsWithoutEIP1559 } from "params/lib";
+import { IEntryPoint } from "types/lib/executor/contracts";
+import { getGasFee } from "params/lib";
+import { IGetGasFeeResult } from "params/lib/gas-price-oracles/oracles";
import { getAddr } from "../utils";
import { MempoolEntry } from "../entities/MempoolEntry";
import { ReputationStatus } from "../entities/interfaces";
import { Config } from "../config";
-import { BundlingMode, Logger, NetworkConfig } from "../interfaces";
-import { getGasFee } from "../utils/getGasFee";
-import { ReputationService } from "./ReputationService";
import {
+ Bundle,
+ BundlingMode,
+ Logger,
+ NetworkConfig,
UserOpValidationResult,
- UserOpValidationService,
-} from "./UserOpValidation";
+} from "../interfaces";
+import { mergeStorageMap } from "../utils/mergeStorageMap";
+import { ReputationService } from "./ReputationService";
+import { UserOpValidationService } from "./UserOpValidation";
import { MempoolService } from "./MempoolService";
export class BundlingService {
@@ -47,74 +52,131 @@ export class BundlingService {
async sendNextBundle(): Promise {
return await this.mutex.runExclusive(async () => {
+ const entries = await this.mempoolService.getSortedOps();
+ if (!entries.length) {
+ return null;
+ }
this.logger.debug("sendNextBundle");
- const bundle = await this.createBundle();
- if (bundle.length == 0) {
+ const gasFee = await getGasFee(
+ this.network,
+ this.provider,
+ this.networkConfig.etherscanApiKey
+ );
+ if (
+ !gasFee.gasPrice &&
+ !gasFee.maxFeePerGas &&
+ !gasFee.maxPriorityFeePerGas
+ ) {
+ this.logger.debug("Could not fetch gas prices...");
+ return null;
+ }
+ const bundle = await this.createBundle(gasFee);
+ if (bundle.entries.length == 0) {
this.logger.debug("sendNextBundle - no bundle");
return null;
}
- return await this.sendBundle(bundle);
+ return await this.sendBundle(bundle, gasFee);
});
}
- async sendBundle(bundle: MempoolEntry[]): Promise {
- if (!bundle.length) {
+ async sendBundle(
+ bundle: Bundle,
+ gasFee: IGetGasFeeResult
+ ): Promise {
+ const { entries, storageMap } = bundle;
+ if (!bundle.entries.length) {
return null;
}
- const entryPoint = bundle[0]!.entryPoint;
- const entryPointContract = EntryPoint__factory.connect(
+ const entryPoint = entries[0]!.entryPoint;
+ const entryPointContract = IEntryPoint__factory.connect(
entryPoint,
this.provider
);
const wallet = this.config.getRelayer(this.network)!;
const beneficiary = await this.selectBeneficiary();
try {
- const gasFee = await getGasFee(
- this.network,
- this.provider,
- this.networkConfig.etherscanApiKey
- );
const txRequest = entryPointContract.interface.encodeFunctionData(
"handleOps",
- [bundle.map((entry) => entry.userOp), beneficiary]
+ [entries.map((entry) => entry.userOp), beneficiary]
);
- const transaction = {
+ const transaction: ethers.providers.TransactionRequest = {
to: entryPoint,
data: txRequest,
type: 2,
maxPriorityFeePerGas: gasFee.maxPriorityFeePerGas,
maxFeePerGas: gasFee.maxFeePerGas,
- gasPrice: undefined as BigNumberish | undefined,
};
if (chainsWithoutEIP1559.some((network) => network === this.network)) {
- transaction.type = 1;
transaction.gasPrice = gasFee.gasPrice;
delete transaction.maxPriorityFeePerGas;
delete transaction.maxFeePerGas;
+ delete transaction.type;
}
- this.logger.debug(JSON.stringify(transaction, undefined, 2));
-
- const gasLimit = await this.estimateBundleGas(bundle);
- const tx = await wallet.sendTransaction({
+ const gasLimit = await this.estimateBundleGas(entries);
+ const tx = {
...transaction,
gasLimit,
- });
+ chainId: this.provider._network.chainId,
+ nonce: await wallet.getTransactionCount(),
+ };
+
+ let txHash: string;
+ // geth-dev doesn't support signTransaction
+ if (!this.config.testingMode) {
+ // check for execution revert
+ try {
+ await wallet.estimateGas(tx);
+ } catch (err) {
+ this.logger.error(err);
+ for (const entry of entries) {
+ await this.mempoolService.remove(entry);
+ }
+ return null;
+ }
+ const signedRawTx = await wallet.signTransaction(tx);
+
+ const method = !this.networkConfig.conditionalTransactions
+ ? "eth_sendRawTransaction"
+ : "eth_sendRawTransactionConditional";
+ const params = !this.networkConfig.conditionalTransactions
+ ? [signedRawTx]
+ : [signedRawTx, { knownAccounts: storageMap }];
- this.logger.debug(`Sent new bundle ${tx.hash}`);
+ this.logger.debug({
+ method,
+ ...tx,
+ params,
+ });
+
+ if (this.networkConfig.rpcEndpointSubmit) {
+ this.logger.debug("Sending to a separate rpc");
+ const provider = new ethers.providers.JsonRpcProvider(
+ this.networkConfig.rpcEndpointSubmit
+ );
+ txHash = await provider.send(method, params);
+ } else {
+ txHash = await this.provider.send(method, params);
+ }
+
+ this.logger.debug(`Sent new bundle ${txHash}`);
+ } else {
+ const resp = await wallet.sendTransaction(tx);
+ txHash = resp.hash;
+ }
- for (const entry of bundle) {
+ for (const entry of entries) {
await this.mempoolService.remove(entry);
}
const userOpHashes = await this.getUserOpHashes(
entryPointContract,
- bundle
+ entries
);
this.logger.debug(`User op hashes ${userOpHashes}`);
return {
- transactionHash: tx.hash,
+ transactionHash: txHash,
userOpHashes: userOpHashes,
};
} catch (err: any) {
@@ -123,7 +185,7 @@ export class BundlingService {
return null;
}
const { index, paymaster, reason } = err.errorArgs;
- const entry = bundle[index];
+ const entry = entries[index];
if (paymaster !== ethers.constants.AddressZero) {
await this.reputationService.crashedHandleOps(paymaster);
} else if (typeof reason === "string" && reason.startsWith("AA1")) {
@@ -142,19 +204,63 @@ export class BundlingService {
}
}
- async createBundle(): Promise {
+ async createBundle(gasFee: IGetGasFeeResult): Promise {
// TODO: support multiple entry points
// filter bundles by entry points
const entries = await this.mempoolService.getSortedOps();
- const bundle: MempoolEntry[] = [];
+ const bundle: Bundle = {
+ storageMap: {},
+ entries: [],
+ };
const paymasterDeposit: { [key: string]: BigNumber } = {};
const stakedEntityCount: { [key: string]: number } = {};
const senders = new Set();
+ const knownSenders = entries.map((it) => {
+ return it.userOp.sender.toLowerCase();
+ });
+
for (const entry of entries) {
+ // validate gas prices if enabled
+ if (this.networkConfig.enforceGasPrice) {
+ let { maxPriorityFeePerGas, maxFeePerGas } = gasFee;
+ const { enforceGasPriceThreshold } = this.networkConfig;
+ if (chainsWithoutEIP1559.some((network) => network === this.network)) {
+ maxFeePerGas = maxPriorityFeePerGas = gasFee.gasPrice;
+ }
+ // userop max fee per gas = userop.maxFee * (100 + threshold) / 100;
+ const userOpMaxFeePerGas = BigNumber.from(entry.userOp.maxFeePerGas)
+ .mul(GasPriceMarkupOne.add(enforceGasPriceThreshold))
+ .div(GasPriceMarkupOne);
+ // userop priority fee per gas = userop.priorityFee * (100 + threshold) / 100;
+ const userOpmaxPriorityFeePerGas = BigNumber.from(
+ entry.userOp.maxPriorityFeePerGas
+ )
+ .mul(GasPriceMarkupOne.add(enforceGasPriceThreshold))
+ .div(GasPriceMarkupOne);
+ if (
+ userOpMaxFeePerGas.lt(maxFeePerGas!) ||
+ userOpmaxPriorityFeePerGas.lt(maxPriorityFeePerGas!)
+ ) {
+ this.logger.debug(
+ {
+ sender: entry.userOp.sender,
+ nonce: entry.userOp.nonce.toString(),
+ userOpMaxFeePerGas: userOpMaxFeePerGas.toString(),
+ userOpmaxPriorityFeePerGas: userOpmaxPriorityFeePerGas.toString(),
+ maxPriorityFeePerGas: maxPriorityFeePerGas!.toString(),
+ maxFeePerGas: maxFeePerGas!.toString(),
+ },
+ "Skipping user op with low gas price"
+ );
+ continue;
+ }
+ }
+
const paymaster = getAddr(entry.userOp.paymasterAndData);
const factory = getAddr(entry.userOp.initCode);
+ // validate Paymaster
if (paymaster) {
const paymasterStatus = await this.reputationService.getStatus(
paymaster
@@ -166,17 +272,19 @@ export class BundlingService {
paymasterStatus === ReputationStatus.THROTTLED ||
(stakedEntityCount[paymaster] ?? 0) > 1
) {
- this.logger.debug("skipping throttled paymaster", {
- metadata: {
+ this.logger.debug(
+ {
sender: entry.userOp.sender,
nonce: entry.userOp.nonce,
paymaster,
},
- });
+ "skipping throttled paymaster"
+ );
continue;
}
}
+ // validate Factory
if (factory) {
const deployerStatus = await this.reputationService.getStatus(factory);
if (deployerStatus === ReputationStatus.BANNED) {
@@ -186,24 +294,23 @@ export class BundlingService {
deployerStatus === ReputationStatus.THROTTLED ||
(stakedEntityCount[factory] ?? 0) > 1
) {
- this.logger.debug("skipping throttled factory", {
- metadata: {
+ this.logger.debug(
+ {
sender: entry.userOp.sender,
nonce: entry.userOp.nonce,
factory,
},
- });
+ "skipping throttled factory"
+ );
continue;
}
}
if (senders.has(entry.userOp.sender)) {
- this.logger.debug("skipping already included sender", {
- metadata: {
- sender: entry.userOp.sender,
- nonce: entry.userOp.nonce,
- },
- });
+ this.logger.debug(
+ { sender: entry.userOp.sender, nonce: entry.userOp.nonce },
+ "skipping already included sender"
+ );
continue;
}
@@ -222,8 +329,24 @@ export class BundlingService {
continue;
}
+ // Check if userOp is trying to access storage of another userop
+ if (validationResult.storageMap) {
+ const sender = entry.userOp.sender.toLowerCase();
+ const conflictingSender = Object.keys(validationResult.storageMap)
+ .map((address) => address.toLowerCase())
+ .find((address) => {
+ return address !== sender && knownSenders.includes(address);
+ });
+ if (conflictingSender) {
+ this.logger.debug(
+ `UserOperation from ${entry.userOp.sender} sender accessed a storage of another known sender ${conflictingSender}`
+ );
+ continue;
+ }
+ }
+
// TODO: add total gas cap
- const entryPointContract = EntryPoint__factory.connect(
+ const entryPointContract = IEntryPoint__factory.connect(
entry.entryPoint,
this.provider
);
@@ -252,7 +375,21 @@ export class BundlingService {
}
senders.add(entry.userOp.sender);
- bundle.push(entry);
+ if (
+ this.networkConfig.conditionalTransactions &&
+ validationResult.storageMap
+ ) {
+ if (BigNumber.from(entry.userOp.nonce).gt(0)) {
+ const { storageHash } = await this.provider.send("eth_getProof", [
+ entry.userOp.sender,
+ [],
+ "latest",
+ ]);
+ bundle.storageMap[entry.userOp.sender.toLowerCase()] = storageHash;
+ }
+ mergeStorageMap(bundle.storageMap, validationResult.storageMap);
+ }
+ bundle.entries.push(entry);
}
return bundle;
}
@@ -318,7 +455,7 @@ export class BundlingService {
}
async getUserOpHashes(
- entryPoint: EntryPoint,
+ entryPoint: IEntryPoint,
userOps: MempoolEntry[]
): Promise {
try {
diff --git a/packages/executor/src/services/EventsService.ts b/packages/executor/src/services/EventsService.ts
index 759efe77..8be009e3 100644
--- a/packages/executor/src/services/EventsService.ts
+++ b/packages/executor/src/services/EventsService.ts
@@ -1,7 +1,7 @@
import { providers } from "ethers";
import { IDbController } from "types/lib";
-import { EntryPoint } from "types/lib/executor/contracts";
-import { EntryPoint__factory } from "types/lib/executor/contracts/factories";
+import { IEntryPoint } from "types/lib/executor/contracts";
+import { IEntryPoint__factory } from "types/lib/executor/contracts/factories";
import {
AccountDeployedEvent,
SignatureAggregatorChangedEvent,
@@ -11,7 +11,7 @@ import { TypedEvent } from "types/lib/executor/contracts/common";
import { ReputationService } from "./ReputationService";
export class EventsService {
- private entryPoints: EntryPoint[] = [];
+ private entryPoints: IEntryPoint[] = [];
private lastBlockPerEntryPoint: {
[address: string]: number;
} = {};
@@ -26,7 +26,7 @@ export class EventsService {
) {
this.LAST_BLOCK_KEY = `${this.chainId}:LAST_BLOCK_PER_ENTRY_POINTS`;
for (const entryPoint of this.entryPointAddrs) {
- const contract = EntryPoint__factory.connect(entryPoint, this.provider);
+ const contract = IEntryPoint__factory.connect(entryPoint, this.provider);
this.entryPoints.push(contract);
}
}
diff --git a/packages/executor/src/services/MempoolService.ts b/packages/executor/src/services/MempoolService.ts
index 884c96f5..0465efbe 100644
--- a/packages/executor/src/services/MempoolService.ts
+++ b/packages/executor/src/services/MempoolService.ts
@@ -6,8 +6,8 @@ import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
import { getAddr, now } from "../utils";
import { MempoolEntry } from "../entities/MempoolEntry";
import { IMempoolEntry, MempoolEntrySerialized } from "../entities/interfaces";
+import { StakeInfo } from "../interfaces";
import { ReputationService } from "./ReputationService";
-import { StakeInfo } from "./UserOpValidation";
export class MempoolService {
private MAX_MEMPOOL_USEROPS_PER_SENDER = 4;
diff --git a/packages/executor/src/services/ReputationService.ts b/packages/executor/src/services/ReputationService.ts
index 91e92968..0c183409 100644
--- a/packages/executor/src/services/ReputationService.ts
+++ b/packages/executor/src/services/ReputationService.ts
@@ -6,7 +6,7 @@ import {
ReputationEntrySerialized,
ReputationStatus,
} from "../entities/interfaces";
-import { StakeInfo } from "./UserOpValidation";
+import { StakeInfo } from "../interfaces";
export class ReputationService {
private REP_COLL_KEY: string; // prefix in rocksdb
@@ -142,7 +142,10 @@ export class ReputationService {
* @param info StakeInfo
* @returns null on success otherwise error
*/
- async checkStake(info: StakeInfo): Promise {
+ async checkStake(info: StakeInfo | undefined): Promise {
+ if (!info) {
+ return "unstaked";
+ }
if (!info.addr || (await this.isWhitelisted(info.addr))) {
return null;
}
diff --git a/packages/executor/src/services/GethTracer.ts b/packages/executor/src/services/UserOpValidation/GethTracer.ts
similarity index 59%
rename from packages/executor/src/services/GethTracer.ts
rename to packages/executor/src/services/UserOpValidation/GethTracer.ts
index 3dd54200..acb650ec 100644
--- a/packages/executor/src/services/GethTracer.ts
+++ b/packages/executor/src/services/UserOpValidation/GethTracer.ts
@@ -1,9 +1,11 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
-import { providers } from "ethers";
-import { TracerPrestateResponse, TracerResult } from "../interfaces";
+import { BigNumber, providers } from "ethers";
+import { BundlerCollectorReturn } from "types/lib/executor";
+import { TracerPrestateResponse } from "../../interfaces";
+
const tracer = readFileSync(
- resolve(process.cwd(), "packages", "executor", "customTracer.js")
+ resolve(process.cwd(), "packages", "executor", "tracer.js")
).toString();
if (tracer == null) {
throw new Error("Tracer not found");
@@ -16,10 +18,7 @@ const stringifiedTracer = tracer.match(regexp)![1];
// console.log(
// JSON.stringify(
// {
-// tracer: stringifiedTracer.replace(
-// /0xffffffffffffffffffffffffffffffffffffffff/g,
-// "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789"
-// ),
+// tracer: stringifiedTracer,
// },
// undefined,
// 2
@@ -31,19 +30,23 @@ export class GethTracer {
async debug_traceCall(
tx: providers.TransactionRequest
- ): Promise {
+ ): Promise {
+ const { gasLimit, ...txWithoutGasLimit } = tx;
+ const gas = `0x${BigNumber.from(gasLimit ?? 10e6)
+ .toNumber()
+ .toString(16)}`; // we're not using toHexString() of BigNumber, because it adds a leading zero which is not accepted by the nodes
const ret: any = await this.provider.send("debug_traceCall", [
- tx,
+ {
+ ...txWithoutGasLimit,
+ gas,
+ },
"latest",
{
- tracer: stringifiedTracer.replace(
- /0xffffffffffffffffffffffffffffffffffffffff/g,
- tx.to!.toLowerCase()
- ),
+ tracer: stringifiedTracer,
},
]);
- return ret as TracerResult;
+ return ret as BundlerCollectorReturn;
}
async debug_traceCallPrestate(
diff --git a/packages/executor/src/services/UserOpValidation/index.ts b/packages/executor/src/services/UserOpValidation/index.ts
new file mode 100644
index 00000000..6261f896
--- /dev/null
+++ b/packages/executor/src/services/UserOpValidation/index.ts
@@ -0,0 +1 @@
+export * from "./service";
diff --git a/packages/executor/src/services/UserOpValidation/service.ts b/packages/executor/src/services/UserOpValidation/service.ts
new file mode 100644
index 00000000..e1053b33
--- /dev/null
+++ b/packages/executor/src/services/UserOpValidation/service.ts
@@ -0,0 +1,134 @@
+import { BigNumber, providers } from "ethers";
+import { NetworkName } from "types/lib";
+import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
+import RpcError from "types/lib/api/errors/rpc-error";
+import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
+import { Config } from "../../config";
+import {
+ ExecutionResult,
+ Logger,
+ NetworkConfig,
+ UserOpValidationResult,
+} from "../../interfaces";
+import { ReputationService } from "../ReputationService";
+import {
+ EstimationService,
+ SafeValidationService,
+ UnsafeValidationService,
+} from "./validators";
+
+export class UserOpValidationService {
+ private networkConfig: NetworkConfig;
+
+ private estimationService: EstimationService;
+ private safeValidationService: SafeValidationService;
+ private unsafeValidationService: UnsafeValidationService;
+
+ constructor(
+ private provider: providers.Provider,
+ private reputationService: ReputationService,
+ private network: NetworkName,
+ private config: Config,
+ private logger: Logger
+ ) {
+ const networkConfig = config.getNetworkConfig(network);
+ if (!networkConfig) {
+ throw new Error(`No config found for ${network}`);
+ }
+ this.networkConfig = networkConfig;
+
+ this.estimationService = new EstimationService(this.provider, this.logger);
+ this.safeValidationService = new SafeValidationService(
+ this.provider,
+ this.reputationService,
+ this.network,
+ this.logger
+ );
+ this.unsafeValidationService = new UnsafeValidationService(
+ this.provider,
+ this.networkConfig,
+ this.logger
+ );
+ }
+
+ async validateForEstimation(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ return await this.estimationService.estimateUserOp(userOp, entryPoint);
+ }
+
+ async validateForEstimationWithSignature(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ return await this.unsafeValidationService.validateUnsafely(
+ userOp,
+ entryPoint
+ );
+ }
+
+ async simulateValidation(
+ userOp: UserOperationStruct,
+ entryPoint: string,
+ codehash?: string
+ ): Promise {
+ if (this.config.unsafeMode) {
+ return await this.unsafeValidationService.validateUnsafely(
+ userOp,
+ entryPoint
+ );
+ }
+ return await this.safeValidationService.validateSafely(
+ userOp,
+ entryPoint,
+ codehash
+ );
+ }
+
+ async validateGasFee(userOp: UserOperationStruct): Promise {
+ const block = await this.provider.getBlock("latest");
+ const { baseFeePerGas } = block;
+ let { maxFeePerGas, maxPriorityFeePerGas } = userOp;
+ maxFeePerGas = BigNumber.from(maxFeePerGas);
+ maxPriorityFeePerGas = BigNumber.from(maxPriorityFeePerGas);
+ if (!baseFeePerGas) {
+ if (!maxFeePerGas.eq(maxPriorityFeePerGas)) {
+ throw new RpcError(
+ "maxFeePerGas must be equal to maxPriorityFeePerGas",
+ RpcErrorCodes.INVALID_USEROP
+ );
+ }
+ return true;
+ }
+
+ if (maxFeePerGas.lt(baseFeePerGas)) {
+ throw new RpcError(
+ "maxFeePerGas must be greater or equal to baseFee",
+ RpcErrorCodes.INVALID_USEROP
+ );
+ }
+
+ return true;
+ }
+
+ async binarySearchVGL(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ if (this.config.unsafeMode) {
+ return this.estimationService.binarySearchVGL(userOp, entryPoint);
+ }
+ return this.estimationService.binarySearchVGLSafe(userOp, entryPoint);
+ }
+
+ async binarySearchCGL(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ if (this.config.unsafeMode) {
+ return userOp; // CGL search not supported in unsafeMode
+ }
+ return this.estimationService.binarySearchCGLSafe(userOp, entryPoint);
+ }
+}
diff --git a/packages/executor/src/services/UserOpValidation/utils.ts b/packages/executor/src/services/UserOpValidation/utils.ts
new file mode 100644
index 00000000..7448cb2e
--- /dev/null
+++ b/packages/executor/src/services/UserOpValidation/utils.ts
@@ -0,0 +1,331 @@
+import { BigNumber, BytesLike } from "ethers";
+import { AddressZero } from "params/lib";
+import RpcError from "types/lib/api/errors/rpc-error";
+import {
+ IEntryPoint,
+ IEntryPoint__factory,
+ IAccount__factory,
+ IAggregatedAccount__factory,
+ IAggregator__factory,
+ IPaymaster__factory,
+ SenderCreator__factory,
+} from "types/lib/executor/contracts";
+import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
+import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
+import { Interface, hexZeroPad, hexlify, keccak256 } from "ethers/lib/utils";
+import { BundlerCollectorReturn, CallEntry } from "types/lib/executor";
+import { UserOpValidationResult, StakeInfo } from "../../interfaces";
+import { getAddr } from "../../utils";
+
+export function nethermindErrorHandler(
+ epContract: IEntryPoint,
+ errorResult: any
+): any {
+ try {
+ let { error } = errorResult;
+ if (error && error.error) {
+ error = error.error;
+ }
+ if (error && error.code == -32015 && error.data.startsWith("Reverted ")) {
+ const parsed = epContract.interface.parseError(error.data.slice(9));
+ errorResult = {
+ ...parsed,
+ errorName: parsed.name,
+ errorArgs: parsed.args,
+ };
+ }
+ } catch (err) {
+ /* empty */
+ }
+ return errorResult;
+}
+
+export function parseErrorResult(
+ userOp: UserOperationStruct,
+ errorResult: { errorName: string; errorArgs: any }
+): UserOpValidationResult {
+ if (!errorResult?.errorName?.startsWith("ValidationResult")) {
+ // parse it as FailedOp
+ // if its FailedOp, then we have the paymaster param... otherwise its an Error(string)
+ let paymaster = errorResult.errorArgs?.paymaster;
+ if (paymaster === AddressZero) {
+ paymaster = undefined;
+ }
+ // eslint-disable-next-line
+ const msg: string =
+ errorResult.errorArgs?.reason ?? errorResult.toString();
+
+ if (paymaster == null) {
+ throw new RpcError(msg, RpcErrorCodes.VALIDATION_FAILED);
+ } else {
+ throw new RpcError(msg, RpcErrorCodes.REJECTED_BY_PAYMASTER, {
+ paymaster,
+ });
+ }
+ }
+
+ const {
+ returnInfo,
+ senderInfo,
+ factoryInfo,
+ paymasterInfo,
+ aggregatorInfo, // may be missing (exists only SimulationResultWithAggregator
+ } = errorResult.errorArgs;
+
+ // extract address from "data" (first 20 bytes)
+ // add it as "addr" member to the "stakeinfo" struct
+ // if no address, then return "undefined" instead of struct.
+ function fillEntity(data: BytesLike, info: StakeInfo): StakeInfo | undefined {
+ const addr = getAddr(data);
+ return addr == null
+ ? undefined
+ : {
+ ...info,
+ addr,
+ };
+ }
+
+ return {
+ returnInfo,
+ senderInfo: {
+ ...senderInfo,
+ addr: userOp.sender,
+ },
+ factoryInfo: fillEntity(userOp.initCode, factoryInfo),
+ paymasterInfo: fillEntity(userOp.paymasterAndData, paymasterInfo),
+ aggregatorInfo: fillEntity(
+ aggregatorInfo?.actualAggregator,
+ aggregatorInfo?.stakeInfo
+ ),
+ };
+}
+
+export function compareBytecode(
+ artifactBytecode: string,
+ contractBytecode: string
+): number {
+ if (artifactBytecode.length <= 2 || contractBytecode.length <= 2) return 0;
+
+ if (typeof artifactBytecode === "string")
+ artifactBytecode = artifactBytecode
+ // eslint-disable-next-line no-useless-escape
+ .replace(/\_\_\$/g, "000")
+ // eslint-disable-next-line no-useless-escape
+ .replace(/\$\_\_/g, "000");
+
+ let matchedBytes = 0;
+ for (let i = 0; i < artifactBytecode.length; i++) {
+ if (artifactBytecode[i] === contractBytecode[i]) matchedBytes++;
+ }
+ if (isNaN(matchedBytes / artifactBytecode.length)) {
+ return 0;
+ }
+
+ return matchedBytes / artifactBytecode.length;
+}
+
+export function toBytes32(b: BytesLike | number): string {
+ return hexZeroPad(hexlify(b).toLowerCase(), 32);
+}
+
+export function requireCond(
+ cond: boolean,
+ msg: string,
+ code?: number,
+ data: any = undefined
+): void {
+ if (!cond) {
+ throw new RpcError(msg, code, data);
+ }
+}
+
+/**
+ * parse all call operation in the trace.
+ * notes:
+ * - entries are ordered by the return (so nested call appears before its outer call
+ * - last entry is top-level return from "simulateValidation". it as ret and rettype, but no type or address
+ * @param tracerResults
+ */
+export function parseCallStack(
+ tracerResults: BundlerCollectorReturn
+): CallEntry[] {
+ const abi = Object.values(
+ [
+ ...IEntryPoint__factory.abi,
+ ...IAccount__factory.abi,
+ ...IAggregatedAccount__factory.abi,
+ ...IAggregator__factory.abi,
+ ...IPaymaster__factory.abi,
+ ].reduce((set, entry: any) => {
+ const key = `${entry.name}(${entry?.inputs
+ ?.map((i: any) => i.type)
+ .join(",")})`;
+ return {
+ ...set,
+ [key]: entry,
+ };
+ }, {})
+ ) as any;
+
+ const xfaces = new Interface(abi);
+
+ function callCatch(x: () => T, def: T1): T | T1 {
+ try {
+ return x();
+ } catch {
+ return def;
+ }
+ }
+
+ const out: CallEntry[] = [];
+ const stack: any[] = [];
+ tracerResults.calls
+ .filter((x) => !x.type.startsWith("depth"))
+ .forEach((c) => {
+ if (c.type.match(/REVERT|RETURN/) != null) {
+ const top = stack.splice(-1)[0] ?? {
+ type: "top",
+ method: "validateUserOp",
+ };
+ const returnData: string = (c as any).data;
+ if (top.type.match(/CREATE/) != null) {
+ out.push({
+ to: top.to,
+ from: top.from,
+ type: top.type,
+ method: "",
+ return: `len=${returnData.length}`,
+ });
+ } else {
+ const method = callCatch(
+ () => xfaces.getFunction(top.method),
+ top.method
+ );
+ if (c.type === "REVERT") {
+ const parsedError = callCatch(
+ () => xfaces.parseError(returnData),
+ returnData
+ );
+ out.push({
+ to: top.to,
+ from: top.from,
+ type: top.type,
+ method: method.name,
+ value: top.value,
+ revert: parsedError,
+ });
+ } else {
+ const ret = callCatch(
+ () => xfaces.decodeFunctionResult(method, returnData),
+ returnData
+ );
+ out.push({
+ to: top.to,
+ from: top.from,
+ type: top.type,
+ value: top.value,
+ method: method.name ?? method,
+ return: ret,
+ });
+ }
+ }
+ } else {
+ stack.push(c);
+ }
+ });
+
+ // TODO: verify that stack is empty at the end.
+
+ return out;
+}
+
+/**
+ * slots associated with each entity.
+ * keccak( A || ...) is associated with "A"
+ * removed rule: keccak( ... || ASSOC ) (for a previously associated hash) is also associated with "A"
+ *
+ * @param stakeInfoEntities stake info for (factory, account, paymaster). factory and paymaster can be null.
+ * @param keccak array of buffers that were given to keccak in the transaction
+ */
+export function parseEntitySlots(
+ stakeInfoEntities: { [addr: string]: StakeInfo | undefined },
+ keccak: string[]
+): { [addr: string]: Set } {
+ // for each entity (sender, factory, paymaster), hold the valid slot addresses
+ // valid: the slot was generated by keccak(entity || ...)
+ const entitySlots: { [addr: string]: Set } = {};
+
+ keccak.forEach((k) => {
+ Object.values(stakeInfoEntities).forEach((info) => {
+ const addr = info?.addr?.toLowerCase();
+ if (addr == null) return;
+ const addrPadded = toBytes32(addr);
+ if (entitySlots[addr] == null) {
+ entitySlots[addr] = new Set();
+ }
+
+ const currentEntitySlots = entitySlots[addr];
+
+ if (k.startsWith(addrPadded)) {
+ currentEntitySlots.add(keccak256(k));
+ }
+ });
+ });
+
+ return entitySlots;
+}
+
+export const callsFromEntryPointMethodSigs: { [key: string]: string } = {
+ factory: SenderCreator__factory.createInterface().getSighash("createSender"),
+ account: IAccount__factory.createInterface().getSighash("validateUserOp"),
+ paymaster: IPaymaster__factory.createInterface().getSighash(
+ "validatePaymasterUserOp"
+ ),
+};
+
+// return true if the given slot is associated with the given address, given the known keccak operations:
+// @param slot the SLOAD/SSTORE slot address we're testing
+// @param addr - the address we try to check for association with
+// @param reverseKeccak - a mapping we built for keccak values that contained the address
+export function isSlotAssociatedWith(
+ slot: string,
+ addr: string,
+ entitySlots: { [addr: string]: Set }
+): boolean {
+ const addrPadded = hexZeroPad(addr, 32).toLowerCase();
+ if (slot === addrPadded) {
+ return true;
+ }
+ const k = entitySlots[addr];
+ if (k == null) {
+ return false;
+ }
+ const slotN = BigNumber.from(slot);
+ // scan all slot entries to check of the given slot is within a structure, starting at that offset.
+ // assume a maximum size on a (static) structure size.
+ for (const k1 of k.keys()) {
+ const kn = BigNumber.from(k1);
+ if (slotN.gte(kn) && slotN.lt(kn.add(128))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+export function parseValidationResult(
+ entryPointContract: IEntryPoint,
+ userOp: UserOperationStruct,
+ data: string
+): UserOpValidationResult {
+ const { name: errorName, args: errorArgs } =
+ entryPointContract.interface.parseError(data);
+ const errFullName = `${errorName}(${errorArgs.toString()})`;
+ const errResult = parseErrorResult(userOp, {
+ errorName,
+ errorArgs,
+ });
+ if (!errorName.includes("Result")) {
+ throw new Error(errFullName);
+ }
+ return errResult;
+}
diff --git a/packages/executor/src/services/UserOpValidation/validators/estimation.ts b/packages/executor/src/services/UserOpValidation/validators/estimation.ts
new file mode 100644
index 00000000..3b4ceb61
--- /dev/null
+++ b/packages/executor/src/services/UserOpValidation/validators/estimation.ts
@@ -0,0 +1,218 @@
+import { AddressZero, BytesZero } from "params/lib";
+import RpcError from "types/lib/api/errors/rpc-error";
+import { IEntryPoint__factory } from "types/lib/executor/contracts";
+import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
+import { BundlerCollectorReturn, ExitInfo } from "types/lib/executor";
+import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
+import { BigNumber, providers } from "ethers";
+import { nethermindErrorHandler } from "../utils";
+import { ExecutionResult, Logger } from "../../../interfaces";
+import { GethTracer } from "../GethTracer";
+
+const isVGLLow = (err: Error): boolean => {
+ const { message } = err;
+ if (!message) return false;
+ return (
+ message.indexOf("OOG") > -1 ||
+ message.indexOf("AA40") > -1 ||
+ message.indexOf("ogg.validation") > -1
+ );
+};
+
+const isCGLLow = (err: Error): boolean => {
+ const { message } = err;
+ if (!message) return false;
+ return (
+ message.indexOf("OOG") > -1 ||
+ message.indexOf("AA40") > -1 ||
+ message.indexOf("ogg.execution") > -1
+ );
+};
+
+export class EstimationService {
+ private gethTracer: GethTracer;
+
+ constructor(private provider: providers.Provider, private logger: Logger) {
+ this.gethTracer = new GethTracer(
+ this.provider as providers.JsonRpcProvider
+ );
+ }
+
+ async estimateUserOp(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ const entryPointContract = IEntryPoint__factory.connect(
+ entryPoint,
+ this.provider
+ );
+
+ const errorResult = await entryPointContract.callStatic
+ .simulateHandleOp(userOp, AddressZero, BytesZero)
+ .catch((e: any) => nethermindErrorHandler(entryPointContract, e));
+
+ if (errorResult.errorName === "FailedOp") {
+ throw new RpcError(
+ errorResult.errorArgs.at(-1),
+ RpcErrorCodes.VALIDATION_FAILED
+ );
+ }
+
+ if (errorResult.errorName !== "ExecutionResult") {
+ throw errorResult;
+ }
+
+ return errorResult.errorArgs;
+ }
+
+ // Binary search verificationGasLimit
+ async binarySearchVGL(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ const { verificationGasLimit } = userOp;
+ let [left, right] = [
+ BigNumber.from(verificationGasLimit).div(2), // the estimated VGL doesn't differ that much from the actual VGL, so we can add some markup here
+ BigNumber.from(verificationGasLimit),
+ ];
+ let lastOptimalVGL: BigNumber | undefined;
+ while (left.lt(right)) {
+ const mid = left.add(right).div(2);
+ try {
+ await this.estimateUserOp(
+ { ...userOp, verificationGasLimit: mid },
+ entryPoint
+ );
+ lastOptimalVGL = mid;
+ break;
+ } catch (err) {
+ if (isVGLLow(err as Error)) {
+ left = mid.add(1);
+ } else {
+ right = mid.sub(1);
+ }
+ }
+ }
+
+ userOp.verificationGasLimit = lastOptimalVGL || userOp.verificationGasLimit;
+ return userOp;
+ }
+
+ async binarySearchVGLSafe(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ const { verificationGasLimit } = userOp;
+ let [left, right] = [
+ BigNumber.from(verificationGasLimit).div(2),
+ BigNumber.from(verificationGasLimit),
+ ];
+ let lastOptimalVGL: BigNumber | undefined;
+ while (left.lt(right)) {
+ const mid = left.add(right).div(2);
+ try {
+ await this.checkForOOG(
+ { ...userOp, verificationGasLimit: mid },
+ entryPoint
+ );
+ lastOptimalVGL = mid;
+ break;
+ } catch (err) {
+ if (isVGLLow(err as Error)) {
+ left = mid.add(1);
+ } else {
+ right = mid.sub(1);
+ }
+ }
+ }
+
+ userOp.verificationGasLimit = lastOptimalVGL || userOp.verificationGasLimit;
+ return userOp;
+ }
+
+ // Binary search callGasLimit
+ // Only available in safe mode
+ async binarySearchCGLSafe(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ const { callGasLimit } = userOp;
+ let [left, right] = [
+ BigNumber.from(callGasLimit).div(5), // the estimated CGL doesn't differ that much from the actual CGL, so we can add some markup here
+ BigNumber.from(callGasLimit),
+ ];
+ let lastOptimalCGL: BigNumber | undefined;
+ let retries = 2; // keep trying to find the most optimal value
+ while (left.lt(right)) {
+ const mid = left.add(right).div(2);
+ userOp.callGasLimit = mid;
+ try {
+ await this.checkForOOG(userOp, entryPoint);
+ lastOptimalCGL = mid;
+ right = mid.sub(1);
+ } catch (err) {
+ if (isCGLLow(err as Error)) {
+ left = mid.add(1);
+ } else {
+ right = mid.sub(1);
+ }
+ if (lastOptimalCGL !== undefined && retries == 0) break;
+ if (lastOptimalCGL !== undefined) {
+ retries--;
+ }
+ }
+ }
+
+ userOp.callGasLimit = lastOptimalCGL || userOp.callGasLimit;
+ return userOp;
+ }
+
+ async checkForOOG(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ const entryPointContract = IEntryPoint__factory.connect(
+ entryPoint,
+ this.provider
+ );
+
+ const tx = {
+ data: entryPointContract.interface.encodeFunctionData(
+ "simulateHandleOp",
+ [userOp, AddressZero, BytesZero]
+ ),
+ to: entryPoint,
+ };
+
+ const traceCall: BundlerCollectorReturn =
+ await this.gethTracer.debug_traceCall(tx);
+ const lastResult = traceCall.calls.at(-1) as ExitInfo;
+ if (lastResult.type !== "REVERT") {
+ throw new RpcError(
+ "Invalid response. simulateCall must revert",
+ RpcErrorCodes.VALIDATION_FAILED
+ );
+ }
+ const data = (lastResult as ExitInfo).data;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const { name: errorName, args: errorArgs } =
+ entryPointContract.interface.parseError(data);
+ const errFullName = `${errorName}(${errorArgs.toString()})`;
+ if (!errorName?.startsWith("ExecutionResult")) {
+ throw new Error(errFullName);
+ }
+
+ traceCall.callsFromEntryPoint.forEach((currentLevel, index) => {
+ if (currentLevel.oog) {
+ if (index >= 1 && index < 3) {
+ throw new Error("oog.validation");
+ }
+ if (index == 3) {
+ throw new Error("oog.execution");
+ }
+ }
+ });
+
+ return ""; // successful validation
+ }
+}
diff --git a/packages/executor/src/services/UserOpValidation/validators/index.ts b/packages/executor/src/services/UserOpValidation/validators/index.ts
new file mode 100644
index 00000000..cd89c7e6
--- /dev/null
+++ b/packages/executor/src/services/UserOpValidation/validators/index.ts
@@ -0,0 +1,3 @@
+export * from "./estimation";
+export * from "./safe";
+export * from "./unsafe";
diff --git a/packages/executor/src/services/UserOpValidation/validators/safe.ts b/packages/executor/src/services/UserOpValidation/validators/safe.ts
new file mode 100644
index 00000000..2774f794
--- /dev/null
+++ b/packages/executor/src/services/UserOpValidation/validators/safe.ts
@@ -0,0 +1,417 @@
+import { IEntryPoint__factory } from "types/lib/executor/contracts";
+import {
+ IEntryPoint,
+ UserOperationStruct,
+} from "types/lib/executor/contracts/EntryPoint";
+import { BigNumber, ethers, providers } from "ethers";
+import { BundlerCollectorReturn, ExitInfo } from "types/lib/executor";
+import RpcError from "types/lib/api/errors/rpc-error";
+import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
+import { WhitelistedEntities } from "params/lib/whitelisted-entities";
+import { NetworkName } from "types/lib";
+import {
+ IWhitelistedEntities,
+ IWhitelistedEntity,
+} from "params/lib/types/IWhitelistedEntities";
+import {
+ Logger,
+ StorageMap,
+ UserOpValidationResult,
+} from "../../../interfaces";
+import { GethTracer } from "../GethTracer";
+import {
+ callsFromEntryPointMethodSigs,
+ isSlotAssociatedWith,
+ parseCallStack,
+ parseEntitySlots,
+ parseValidationResult,
+} from "../utils";
+import { ReputationService } from "../../ReputationService";
+
+/**
+ * Some opcodes like:
+ * - CREATE2
+ * are not included here because they are handled elsewhere.
+ * Do not include them in this list!!!
+ */
+const bannedOpCodes = new Set([
+ "GASPRICE",
+ "GASLIMIT",
+ "DIFFICULTY",
+ "TIMESTAMP",
+ "BASEFEE",
+ "BLOCKHASH",
+ "NUMBER",
+ "SELFBALANCE",
+ "BALANCE",
+ "ORIGIN",
+ "GAS",
+ "CREATE",
+ "COINBASE",
+ "SELFDESTRUCT",
+ "RANDOM",
+ "PREVRANDAO",
+]);
+
+// REF: https://github.com/eth-infinitism/bundler/blob/main/packages/bundler/src/modules/ValidationManager.ts
+export class SafeValidationService {
+ private gethTracer: GethTracer;
+
+ constructor(
+ private provider: providers.Provider,
+ private reputationService: ReputationService,
+ private network: NetworkName,
+ private logger: Logger
+ ) {
+ this.gethTracer = new GethTracer(
+ this.provider as providers.JsonRpcProvider
+ );
+ }
+
+ async validateSafely(
+ userOp: UserOperationStruct,
+ entryPoint: string,
+ codehash?: string
+ ): Promise {
+ const entryPointContract = IEntryPoint__factory.connect(
+ entryPoint,
+ this.provider
+ );
+ const simulationGas = BigNumber.from(userOp.preVerificationGas)
+ .add(userOp.verificationGasLimit)
+ .add(userOp.callGasLimit);
+
+ const tx: providers.TransactionRequest = {
+ to: entryPoint,
+ data: entryPointContract.interface.encodeFunctionData(
+ "simulateValidation",
+ [userOp]
+ ),
+ gasLimit: simulationGas,
+ };
+
+ const traceCall: BundlerCollectorReturn =
+ await this.gethTracer.debug_traceCall(tx);
+ const validationResult = await this.validateOpcodesAndStake(
+ traceCall,
+ entryPointContract,
+ userOp
+ );
+
+ const { returnInfo } = validationResult;
+ if (returnInfo.sigFailed) {
+ throw new RpcError(
+ "Invalid UserOp signature or paymaster signature",
+ RpcErrorCodes.INVALID_SIGNATURE
+ );
+ }
+
+ const now = Math.floor(Date.now() / 1000);
+ if (returnInfo.validUntil != null && returnInfo.validUntil < now) {
+ throw new RpcError("already expired", RpcErrorCodes.USEROP_EXPIRED);
+ }
+
+ if (returnInfo.validAfter != null && returnInfo.validAfter > now + 30) {
+ throw new RpcError("expires too soon", RpcErrorCodes.USEROP_EXPIRED);
+ }
+
+ if (validationResult.aggregatorInfo != null) {
+ const stakeErr = await this.reputationService.checkStake(
+ validationResult.aggregatorInfo
+ );
+ if (stakeErr) {
+ throw new RpcError(stakeErr, RpcErrorCodes.VALIDATION_FAILED);
+ }
+ }
+
+ const prestateTrace = await this.gethTracer.debug_traceCallPrestate(tx);
+ const addresses = traceCall.callsFromEntryPoint.flatMap((level) =>
+ Object.keys(level.contractSize)
+ );
+ const code = addresses.map((addr) => prestateTrace[addr]?.code).join(";");
+ const hash = ethers.utils.keccak256(
+ ethers.utils.hexlify(ethers.utils.toUtf8Bytes(code))
+ );
+
+ if (codehash && codehash !== hash) {
+ throw new RpcError(
+ "modified code after first validation",
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+
+ const storageMap: StorageMap = {};
+ traceCall.callsFromEntryPoint.forEach((level) => {
+ Object.keys(level.access).forEach((addr) => {
+ storageMap[addr] = storageMap[addr] ?? level.access[addr].reads;
+ });
+ });
+
+ return {
+ ...validationResult,
+ referencedContracts: {
+ addresses,
+ hash,
+ },
+ storageMap,
+ };
+ }
+
+ private async validateOpcodesAndStake(
+ traceCall: BundlerCollectorReturn,
+ entryPointContract: IEntryPoint,
+ userOp: UserOperationStruct
+ ): Promise {
+ const entryPoint = entryPointContract.address.toLowerCase();
+ if (traceCall == null || traceCall.callsFromEntryPoint == undefined) {
+ throw new Error(
+ "Could not validate transaction. Tracing is not available"
+ );
+ }
+
+ if (Object.values(traceCall.callsFromEntryPoint).length < 1) {
+ throw new RpcError(
+ "Unexpected traceCall result: no calls from entrypoint.",
+ RpcErrorCodes.INTERNAL_ERROR
+ );
+ }
+
+ const callStack = parseCallStack(traceCall);
+
+ const callInfoEntryPoint = callStack.find(
+ (call) =>
+ call.to === entryPoint &&
+ call.from !== entryPoint &&
+ call.method !== "0x" &&
+ call.method !== "depositTo"
+ );
+
+ if (callInfoEntryPoint != null) {
+ throw new RpcError(
+ `illegal call into EntryPoint during validation ${callInfoEntryPoint?.method}`,
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+
+ if (
+ callStack.some(
+ ({ to, value }) => to !== entryPoint && BigNumber.from(value ?? 0).gt(0)
+ )
+ ) {
+ throw new RpcError(
+ "May not may CALL with value",
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+
+ const sender = userOp.sender.toLowerCase();
+
+ // Parse error result from the last call
+ const lastResult = traceCall.calls.at(-1) as ExitInfo;
+ if (lastResult.type !== "REVERT") {
+ throw new RpcError(
+ "Invalid response. simulateCall must revert",
+ RpcErrorCodes.VALIDATION_FAILED
+ );
+ }
+ const data = (lastResult as ExitInfo).data;
+ const validationResult = parseValidationResult(
+ entryPointContract,
+ userOp,
+ data
+ );
+
+ const stakeInfoEntities = {
+ factory: validationResult.factoryInfo,
+ account: validationResult.senderInfo,
+ paymaster: validationResult.paymasterInfo,
+ };
+
+ const entitySlots: { [addr: string]: Set } = parseEntitySlots(
+ stakeInfoEntities,
+ traceCall.keccak
+ );
+
+ for (const [entityTitle, entStakes] of Object.entries(stakeInfoEntities)) {
+ const entityAddr = (entStakes?.addr || "").toLowerCase();
+ const currentNumLevel = traceCall.callsFromEntryPoint.find(
+ (info) =>
+ info.topLevelMethodSig === callsFromEntryPointMethodSigs[entityTitle]
+ );
+ if (currentNumLevel == null) {
+ if (entityTitle === "account") {
+ throw new RpcError(
+ "missing trace into validateUserOp",
+ RpcErrorCodes.EXECUTION_REVERTED
+ );
+ }
+ continue;
+ }
+ const opcodes = currentNumLevel.opcodes;
+ const access = currentNumLevel.access;
+
+ if (currentNumLevel.oog) {
+ throw new RpcError(
+ `${entityTitle} internally reverts on oog`,
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+
+ const whitelist: IWhitelistedEntity | undefined =
+ WhitelistedEntities[entityTitle as keyof IWhitelistedEntities];
+ if (
+ entityAddr &&
+ whitelist != null &&
+ whitelist[this.network] &&
+ whitelist[this.network]!.some(
+ (addr) => addr === ethers.utils.getAddress(entityAddr)
+ )
+ ) {
+ this.logger.debug(
+ `${entityTitle} is in whitelist. Skipping opcode validation...`
+ );
+ continue;
+ }
+
+ Object.keys(opcodes).forEach((opcode) => {
+ if (bannedOpCodes.has(opcode)) {
+ throw new RpcError(
+ `${entityTitle} uses banned opcode: ${opcode}`,
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+ });
+
+ // Special case for CREATE2
+ if (entityTitle === "factory") {
+ if (opcodes.CREATE2 > 1) {
+ throw new RpcError(
+ `${entityTitle} with too many CREATE2`,
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+ } else {
+ if (opcodes.CREATE2 > 0) {
+ throw new RpcError(
+ `${entityTitle} uses banned opcode: CREATE2`,
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+ }
+
+ for (const [addr, { reads, writes }] of Object.entries(access)) {
+ if (addr === sender) {
+ continue;
+ }
+
+ if (addr === entryPoint) {
+ continue;
+ }
+
+ // eslint-disable-next-line no-inner-declarations
+ function nameAddr(addr: string, _currentEntity: string): string {
+ const [title] =
+ Object.entries(stakeInfoEntities).find(
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ ([title, info]) => info?.addr.toLowerCase() === addr.toLowerCase()
+ ) ?? [];
+
+ return title ?? addr;
+ }
+
+ let requireStakeSlot: string | undefined;
+ for (const slot of [...Object.keys(writes), ...Object.keys(reads)]) {
+ if (isSlotAssociatedWith(slot, sender, entitySlots)) {
+ if (
+ userOp.initCode.length > 2 &&
+ !(
+ entityAddr === sender &&
+ (await this.reputationService.checkStake(
+ stakeInfoEntities.factory
+ )) === null
+ )
+ ) {
+ requireStakeSlot = slot;
+ }
+ } else if (isSlotAssociatedWith(slot, entityAddr, entitySlots)) {
+ requireStakeSlot = slot;
+ } else if (addr === entityAddr) {
+ requireStakeSlot = slot;
+ } else {
+ const readWrite = Object.keys(writes).includes(addr)
+ ? "write to"
+ : "read from";
+ throw new RpcError(
+ // eslint-disable-next-line prettier/prettier
+ `${entityTitle} has forbidden ${readWrite} ${nameAddr(addr, entityTitle)} slot ${slot}`,
+ RpcErrorCodes.INVALID_OPCODE,
+ {
+ [entityTitle]: entStakes?.addr,
+ }
+ );
+ }
+ }
+
+ if (requireStakeSlot != null) {
+ const stake = await this.reputationService.checkStake(entStakes);
+ if (stake != null) {
+ throw new RpcError(
+ `unstaked ${entityTitle} accessed ${nameAddr(
+ addr,
+ entityTitle
+ )} slot ${requireStakeSlot}`,
+ RpcErrorCodes.INVALID_OPCODE,
+ {
+ [entityTitle]: entStakes?.addr,
+ }
+ );
+ }
+ }
+ }
+
+ if (entityTitle === "paymaster") {
+ const validatePaymasterUserOp = callStack.find(
+ (call) =>
+ call.method === "validatePaymasterUserOp" && call.to === entityAddr
+ );
+ const context = validatePaymasterUserOp?.return?.context;
+ if (context != null && context !== "0x") {
+ const stake = await this.reputationService.checkStake(entStakes);
+ if (stake != null) {
+ throw new RpcError(
+ "unstaked paymaster must not return context",
+ RpcErrorCodes.INVALID_OPCODE,
+ {
+ [entityTitle]: entStakes?.addr,
+ }
+ );
+ }
+ }
+ }
+
+ for (const addr of Object.keys(currentNumLevel.contractSize)) {
+ if (
+ addr !== sender &&
+ currentNumLevel.contractSize[addr].contractSize <= 2
+ ) {
+ const { opcode } = currentNumLevel.contractSize[addr];
+ throw new RpcError(
+ `${entityTitle} accesses un-deployed contract address ${addr} with opcode ${opcode}`,
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+ }
+
+ for (const addr of Object.keys(currentNumLevel.extCodeAccessInfo)) {
+ if (addr === entryPoint) {
+ throw new RpcError(
+ `${entityTitle} accesses EntryPoint contract address ${addr} with opcode ${currentNumLevel.extCodeAccessInfo[addr]}`,
+ RpcErrorCodes.INVALID_OPCODE
+ );
+ }
+ }
+ }
+
+ return validationResult;
+ }
+}
diff --git a/packages/executor/src/services/UserOpValidation/validators/unsafe.ts b/packages/executor/src/services/UserOpValidation/validators/unsafe.ts
new file mode 100644
index 00000000..64df3447
--- /dev/null
+++ b/packages/executor/src/services/UserOpValidation/validators/unsafe.ts
@@ -0,0 +1,34 @@
+import { IEntryPoint__factory } from "types/lib/executor/contracts";
+import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
+import { providers } from "ethers";
+import {
+ Logger,
+ NetworkConfig,
+ UserOpValidationResult,
+} from "../../../interfaces";
+import { nethermindErrorHandler, parseErrorResult } from "../utils";
+
+export class UnsafeValidationService {
+ constructor(
+ private provider: providers.Provider,
+ private networkConfig: NetworkConfig,
+ private logger: Logger
+ ) {}
+
+ async validateUnsafely(
+ userOp: UserOperationStruct,
+ entryPoint: string
+ ): Promise {
+ const { validationGasLimit } = this.networkConfig;
+ const entryPointContract = IEntryPoint__factory.connect(
+ entryPoint,
+ this.provider
+ );
+ const errorResult = await entryPointContract.callStatic
+ .simulateValidation(userOp, {
+ gasLimit: validationGasLimit,
+ })
+ .catch((e: any) => nethermindErrorHandler(entryPointContract, e));
+ return parseErrorResult(userOp, errorResult);
+ }
+}
diff --git a/packages/executor/src/tracer/utils.ts b/packages/executor/src/tracer/utils.ts
deleted file mode 100644
index 5985bbdb..00000000
--- a/packages/executor/src/tracer/utils.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-export function compareBytecode(
- artifactBytecode: string,
- contractBytecode: string
-): number {
- if (artifactBytecode.length <= 2 || contractBytecode.length <= 2) return 0;
-
- if (typeof artifactBytecode === "string")
- artifactBytecode = artifactBytecode
- .replace(/\_\_\$/g, "000")
- .replace(/\$\_\_/g, "000");
-
- let matchedBytes = 0;
- for (let i = 0; i < artifactBytecode.length; i++) {
- if (artifactBytecode[i] === contractBytecode[i]) matchedBytes++;
- }
- if (isNaN(matchedBytes / artifactBytecode.length)) {
- return 0;
- }
-
- return matchedBytes / artifactBytecode.length;
-}
diff --git a/packages/executor/src/utils/gas-oracles/interfaces.ts b/packages/executor/src/utils/gas-oracles/interfaces.ts
deleted file mode 100644
index d619ae87..00000000
--- a/packages/executor/src/utils/gas-oracles/interfaces.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { BigNumberish } from "ethers";
-
-export type IGetGasFeeResult = {
- maxPriorityFeePerGas: BigNumberish | undefined;
- maxFeePerGas: BigNumberish | undefined;
- gasPrice: BigNumberish | undefined;
-};
-
-export type IOracle = (apiKey: string) => Promise;
diff --git a/packages/executor/src/utils/index.ts b/packages/executor/src/utils/index.ts
index 287aa60f..e89aa3e8 100644
--- a/packages/executor/src/utils/index.ts
+++ b/packages/executor/src/utils/index.ts
@@ -4,11 +4,11 @@ import {
hexlify,
keccak256,
} from "ethers/lib/utils";
-import { EntryPoint__factory } from "types/lib/executor/contracts/factories/EntryPoint__factory";
+import { IEntryPoint__factory } from "types/lib/executor/contracts/factories";
import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
const UserOpType = (
- EntryPoint__factory.abi.find(
+ IEntryPoint__factory.abi.find(
(entry: any) => entry.name === "simulateValidation"
) as any
).inputs?.[0];
@@ -105,8 +105,6 @@ export function packUserOp(
name: "userOp",
type: "tuple",
};
- // console.log('hard-coded userOpType', userOpType)
- // console.log('from ABI userOpType', UserOpType)
let encoded = defaultAbiCoder.encode(
[userOpType as any],
[
diff --git a/packages/executor/src/utils/mergeStorageMap.ts b/packages/executor/src/utils/mergeStorageMap.ts
new file mode 100644
index 00000000..b6c1f7fb
--- /dev/null
+++ b/packages/executor/src/utils/mergeStorageMap.ts
@@ -0,0 +1,37 @@
+import { SlotMap, StorageMap } from "../interfaces";
+
+// REF: https://github.com/eth-infinitism/bundler/blob/ba29f67567410787d8ccb4828fa5abb65118010e/packages/bundler/src/modules/moduleUtils.ts#L20-L50
+/**
+/ * merge all validationStorageMap objects into merged map
+ * - entry with "root" (string) is always preferred over entry with slot-map
+ * - merge slot entries
+ * NOTE: slot values are supposed to be the value before the transaction started.
+ * so same address/slot in different validations should carry the same value
+ * @param mergedStorageMap
+ * @param validationStorageMap
+ */
+export function mergeStorageMap(
+ mergedStorageMap: StorageMap,
+ validationStorageMap: StorageMap
+): StorageMap {
+ Object.entries(validationStorageMap).forEach(([addr, validationEntry]) => {
+ if (typeof validationEntry === "string") {
+ // it's a root. override specific slots, if any
+ mergedStorageMap[addr] = validationEntry;
+ } else if (typeof mergedStorageMap[addr] === "string") {
+ // merged address already contains a root. ignore specific slot values
+ } else {
+ let slots: SlotMap;
+ if (mergedStorageMap[addr] == null) {
+ slots = mergedStorageMap[addr] = {};
+ } else {
+ slots = mergedStorageMap[addr] as SlotMap;
+ }
+
+ Object.entries(validationEntry).forEach(([slot, val]) => {
+ slots[slot] = val;
+ });
+ }
+ });
+ return mergedStorageMap;
+}
diff --git a/packages/executor/tracer.js b/packages/executor/tracer.js
new file mode 100644
index 00000000..666cb835
--- /dev/null
+++ b/packages/executor/tracer.js
@@ -0,0 +1,190 @@
+function bundlerCollectorTracer() {
+ return {
+ callsFromEntryPoint: [],
+ currentLevel: null,
+ keccak: [],
+ calls: [],
+ logs: [],
+ debug: [],
+ lastOp: '',
+ lastThreeOpcodes: [],
+ stopCollectingTopic: 'bb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972',
+ stopCollecting: false,
+ topLevelCallCounter: 0,
+ fault: function (log, db) {
+ this.debug.push('fault depth=', log.getDepth(), ' gas=', log.getGas(), ' cost=', log.getCost(), ' err=', log.getError());
+ },
+ result: function (ctx, db) {
+ return {
+ callsFromEntryPoint: this.callsFromEntryPoint,
+ keccak: this.keccak,
+ logs: this.logs,
+ calls: this.calls,
+ debug: this.debug
+ };
+ },
+ enter: function (frame) {
+ if (this.stopCollecting) {
+ return;
+ }
+ this.calls.push({
+ type: frame.getType(),
+ from: toHex(frame.getFrom()),
+ to: toHex(frame.getTo()),
+ method: toHex(frame.getInput()).slice(0, 10),
+ gas: frame.getGas(),
+ value: frame.getValue()
+ });
+ },
+ exit: function (frame) {
+ if (this.stopCollecting) {
+ return;
+ }
+ this.calls.push({
+ type: frame.getError() != null ? 'REVERT' : 'RETURN',
+ gasUsed: frame.getGasUsed(),
+ data: toHex(frame.getOutput()).slice(0, 4000)
+ });
+ },
+ countSlot: function (list, key) {
+ var _a;
+ list[key] = ((_a = list[key]) !== null && _a !== void 0 ? _a : 0) + 1;
+ },
+ step: function (log, db) {
+ var _a;
+ if (this.stopCollecting) {
+ return;
+ }
+ var opcode = log.op.toString();
+ var stackSize = log.stack.length();
+ var stackTop3 = [];
+ for (var i = 0; i < 3 && i < stackSize; i++) {
+ stackTop3.push(log.stack.peek(i));
+ }
+ this.lastThreeOpcodes.push({ opcode: opcode, stackTop3: stackTop3 });
+ if (this.lastThreeOpcodes.length > 3) {
+ this.lastThreeOpcodes.shift();
+ }
+ if (log.getGas() < log.getCost()) {
+ this.currentLevel.oog = true;
+ }
+ if (opcode === 'REVERT' || opcode === 'RETURN') {
+ if (log.getDepth() === 1) {
+ var ofs = parseInt(log.stack.peek(0).toString());
+ var len = parseInt(log.stack.peek(1).toString());
+ var data = toHex(log.memory.slice(ofs, ofs + len)).slice(0, 4000);
+ this.calls.push({
+ type: opcode,
+ gasUsed: 0,
+ data: data
+ });
+ }
+ this.lastThreeOpcodes = [];
+ }
+ if (log.getDepth() === 1) {
+ if (opcode === 'CALL' || opcode === 'STATICCALL') {
+ var addr = toAddress(log.stack.peek(1).toString(16));
+ var topLevelTargetAddress = toHex(addr);
+ var ofs = parseInt(log.stack.peek(3).toString());
+ var topLevelMethodSig = toHex(log.memory.slice(ofs, ofs + 4));
+ this.currentLevel = this.callsFromEntryPoint[this.topLevelCallCounter] = {
+ topLevelMethodSig: topLevelMethodSig,
+ topLevelTargetAddress: topLevelTargetAddress,
+ access: {},
+ opcodes: {},
+ extCodeAccessInfo: {},
+ contractSize: {}
+ };
+ this.topLevelCallCounter++;
+ }
+ else if (opcode === 'LOG1') {
+ var topic = log.stack.peek(2).toString(16);
+ if (topic === this.stopCollectingTopic) {
+ this.stopCollecting = true;
+ }
+ }
+ this.lastOp = '';
+ return;
+ }
+ var lastOpInfo = this.lastThreeOpcodes[this.lastThreeOpcodes.length - 2];
+ if (((_a = lastOpInfo === null || lastOpInfo === void 0 ? void 0 : lastOpInfo.opcode) === null || _a === void 0 ? void 0 : _a.match(/^(EXT.*)$/)) != null) {
+ var addr = toAddress(lastOpInfo.stackTop3[0].toString(16));
+ var addrHex = toHex(addr);
+ var last3opcodesString = this.lastThreeOpcodes.map(function (x) { return x.opcode; }).join(' ');
+ if (last3opcodesString.match(/^(\w+) EXTCODESIZE ISZERO$/) == null) {
+ this.currentLevel.extCodeAccessInfo[addrHex] = opcode;
+ }
+ else {
+ }
+ }
+ var isAllowedPrecompiled = function (address) {
+ var addrHex = toHex(address);
+ var addressInt = parseInt(addrHex);
+ return addressInt > 0 && addressInt < 10;
+ };
+ if (opcode.match(/^(EXT.*|CALL|CALLCODE|DELEGATECALL|STATICCALL)$/) != null) {
+ var idx = opcode.startsWith('EXT') ? 0 : 1;
+ var addr = toAddress(log.stack.peek(idx).toString(16));
+ var addrHex = toHex(addr);
+ if (this.currentLevel.contractSize[addrHex] == null && !isAllowedPrecompiled(addr)) {
+ this.currentLevel.contractSize[addrHex] = {
+ contractSize: db.getCode(addr).length,
+ opcode: opcode
+ };
+ }
+ }
+ if (this.lastOp === 'GAS' && !opcode.includes('CALL')) {
+ this.countSlot(this.currentLevel.opcodes, 'GAS');
+ }
+ if (opcode !== 'GAS') {
+ if (opcode.match(/^(DUP\d+|PUSH\d+|SWAP\d+|POP|ADD|SUB|MUL|DIV|EQ|LTE?|S?GTE?|SLT|SH[LR]|AND|OR|NOT|ISZERO)$/) == null) {
+ this.countSlot(this.currentLevel.opcodes, opcode);
+ }
+ }
+ this.lastOp = opcode;
+ if (opcode === 'SLOAD' || opcode === 'SSTORE') {
+ var slot = toWord(log.stack.peek(0).toString(16));
+ var slotHex = toHex(slot);
+ var addr = log.contract.getAddress();
+ var addrHex = toHex(addr);
+ var access = this.currentLevel.access[addrHex];
+ if (access == null) {
+ access = {
+ reads: {},
+ writes: {}
+ };
+ this.currentLevel.access[addrHex] = access;
+ }
+ if (opcode === 'SLOAD') {
+ if (access.reads[slotHex] == null && access.writes[slotHex] == null) {
+ access.reads[slotHex] = toHex(db.getState(addr, slot));
+ }
+ }
+ else {
+ this.countSlot(access.writes, slotHex);
+ }
+ }
+ if (opcode === 'KECCAK256') {
+ var ofs = parseInt(log.stack.peek(0).toString());
+ var len = parseInt(log.stack.peek(1).toString());
+ if (len > 20 && len < 512) {
+ this.keccak.push(toHex(log.memory.slice(ofs, ofs + len)));
+ }
+ }
+ else if (opcode.startsWith('LOG')) {
+ var count = parseInt(opcode.substring(3));
+ var ofs = parseInt(log.stack.peek(0).toString());
+ var len = parseInt(log.stack.peek(1).toString());
+ var topics = [];
+ for (var i = 0; i < count; i++) {
+ topics.push('0x' + log.stack.peek(2 + i).toString(16));
+ }
+ var data = toHex(log.memory.slice(ofs, ofs + len));
+ this.logs.push({
+ topics: topics,
+ data: data
+ });
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/packages/params/package.json b/packages/params/package.json
index 307d40a9..26329a4b 100644
--- a/packages/params/package.json
+++ b/packages/params/package.json
@@ -1,6 +1,6 @@
{
"name": "params",
- "version": "0.0.24",
+ "version": "0.0.44",
"description": "Various bundler parameters",
"author": "Etherspot",
"homepage": "https://github.com/etherspot/skandha#readme",
@@ -23,10 +23,11 @@
"dependencies": {
"@arbitrum/sdk": "3.1.4",
"@eth-optimism/sdk": "3.0.0",
+ "@mantleio/sdk": "0.2.1",
"ethers": "5.7.2",
- "types": "^0.0.24",
"utils": "*",
- "@chainsafe/ssz": "0.10.1"
+ "@chainsafe/ssz": "0.10.1",
+ "types": "^0.0.44"
},
"scripts": {
"clean": "rm -rf lib && rm -f *.tsbuildinfo",
diff --git a/packages/params/src/constants.ts b/packages/params/src/constants.ts
index 944673e9..a6b61615 100644
--- a/packages/params/src/constants.ts
+++ b/packages/params/src/constants.ts
@@ -1,4 +1,6 @@
-import { constants } from "ethers";
+import { BigNumber, constants } from "ethers";
export const AddressZero = constants.AddressZero;
export const BytesZero = "0x";
+
+export const GasPriceMarkupOne = BigNumber.from(10000); // 100.00%
diff --git a/packages/params/src/eip1559.ts b/packages/params/src/eip1559.ts
index 1b9ffe1f..cd26bdbd 100644
--- a/packages/params/src/eip1559.ts
+++ b/packages/params/src/eip1559.ts
@@ -8,4 +8,7 @@ export const chainsWithoutEIP1559: NetworkName[] = [
"polygonzkevm",
"mantle",
"mantleTestnet",
+ "scroll",
+ "scrollAlpha",
+ "scrollSepolia",
];
diff --git a/packages/params/src/gas-estimation/arbitrum.ts b/packages/params/src/gas-estimation/arbitrum.ts
index 87642480..e88b1150 100644
--- a/packages/params/src/gas-estimation/arbitrum.ts
+++ b/packages/params/src/gas-estimation/arbitrum.ts
@@ -2,7 +2,7 @@ import { NodeInterface__factory } from "@arbitrum/sdk/dist/lib/abi/factories/Nod
import { NODE_INTERFACE_ADDRESS } from "@arbitrum/sdk/dist/lib/dataEntities/constants";
import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
import { BigNumber, BigNumberish, ethers } from "ethers";
-import { EntryPoint__factory } from "types/lib/executor/contracts";
+import { IEntryPoint__factory } from "types/lib/executor/contracts";
import { IPVGEstimator, IPVGEstimatorWrapper } from "../types/IPVGEstimator";
export const estimateArbitrumPVG: IPVGEstimatorWrapper = (
@@ -18,7 +18,7 @@ export const estimateArbitrumPVG: IPVGEstimatorWrapper = (
userOp: UserOperationStruct,
initial: BigNumberish
): Promise => {
- const entryPoint = EntryPoint__factory.connect(entryPointAddr, provider);
+ const entryPoint = IEntryPoint__factory.connect(entryPointAddr, provider);
const handleOpsData = entryPoint.interface.encodeFunctionData("handleOps", [
[userOp],
dummyWallet.address,
diff --git a/packages/params/src/gas-estimation/mantle.ts b/packages/params/src/gas-estimation/mantle.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/packages/params/src/gas-estimation/optimism.ts b/packages/params/src/gas-estimation/optimism.ts
index 744428db..fb165da6 100644
--- a/packages/params/src/gas-estimation/optimism.ts
+++ b/packages/params/src/gas-estimation/optimism.ts
@@ -1,6 +1,6 @@
import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
import { BigNumber, BigNumberish, ethers } from "ethers";
-import { EntryPoint__factory } from "types/lib/executor/contracts";
+import { IEntryPoint__factory } from "types/lib/executor/contracts";
import { estimateL1Gas } from "@eth-optimism/sdk";
import { IPVGEstimator, IPVGEstimatorWrapper } from "../types/IPVGEstimator";
@@ -13,7 +13,7 @@ export const estimateOptimismPVG: IPVGEstimatorWrapper = (
userOp: UserOperationStruct,
initial: BigNumberish
): Promise => {
- const entryPoint = EntryPoint__factory.connect(entryPointAddr, provider);
+ const entryPoint = IEntryPoint__factory.connect(entryPointAddr, provider);
const handleOpsData = entryPoint.interface.encodeFunctionData("handleOps", [
[userOp],
dummyWallet.address,
diff --git a/packages/executor/src/utils/getGasFee.ts b/packages/params/src/gas-price-oracles/getGasFee.ts
similarity index 82%
rename from packages/executor/src/utils/getGasFee.ts
rename to packages/params/src/gas-price-oracles/getGasFee.ts
index efdda72a..aae0bdc2 100644
--- a/packages/executor/src/utils/getGasFee.ts
+++ b/packages/params/src/gas-price-oracles/getGasFee.ts
@@ -1,15 +1,16 @@
import { providers } from "ethers";
import { NetworkName } from "types/lib";
-import { IGetGasFeeResult, oracles } from "./gas-oracles";
+import { IGetGasFeeResult, IOracleOptions, oracles } from "./oracles";
export const getGasFee = async (
network: NetworkName,
provider: providers.JsonRpcProvider,
- apiKey = ""
+ apiKey = "",
+ options?: IOracleOptions
): Promise => {
if (oracles[network]) {
try {
- return await oracles[network]!(apiKey);
+ return await oracles[network]!(apiKey, provider, options);
} catch (err) {
// eslint-disable-next-line no-console
console.error(`Couldn't fetch fee data for ${network}: ${err}`);
diff --git a/packages/params/src/gas-price-oracles/index.ts b/packages/params/src/gas-price-oracles/index.ts
new file mode 100644
index 00000000..1d0095d9
--- /dev/null
+++ b/packages/params/src/gas-price-oracles/index.ts
@@ -0,0 +1 @@
+export * from "./getGasFee";
diff --git a/packages/executor/src/utils/gas-oracles/arbitrum.ts b/packages/params/src/gas-price-oracles/oracles/arbitrum.ts
similarity index 100%
rename from packages/executor/src/utils/gas-oracles/arbitrum.ts
rename to packages/params/src/gas-price-oracles/oracles/arbitrum.ts
diff --git a/packages/executor/src/utils/gas-oracles/index.ts b/packages/params/src/gas-price-oracles/oracles/index.ts
similarity index 82%
rename from packages/executor/src/utils/gas-oracles/index.ts
rename to packages/params/src/gas-price-oracles/oracles/index.ts
index 4ba70b9c..0901c5ed 100644
--- a/packages/executor/src/utils/gas-oracles/index.ts
+++ b/packages/params/src/gas-price-oracles/oracles/index.ts
@@ -7,6 +7,7 @@ import { getMaticGasFee } from "./matic";
import { getMumbaiGasFee } from "./mumbai";
import { getOptimismGasFee } from "./optimism";
import { IOracle } from "./interfaces";
+import { getMantleGasFee } from "./mantle";
export const oracles: {
[key in NetworkName]?: IOracle;
@@ -15,4 +16,6 @@ export const oracles: {
mumbai: getMumbaiGasFee,
optimism: getOptimismGasFee,
arbitrum: getArbitrumGasFee,
+ mantle: getMantleGasFee,
+ mantleTestnet: getMantleGasFee,
};
diff --git a/packages/params/src/gas-price-oracles/oracles/interfaces.ts b/packages/params/src/gas-price-oracles/oracles/interfaces.ts
new file mode 100644
index 00000000..fbdccbac
--- /dev/null
+++ b/packages/params/src/gas-price-oracles/oracles/interfaces.ts
@@ -0,0 +1,19 @@
+import { BigNumberish, ethers } from "ethers";
+import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
+
+export type IGetGasFeeResult = {
+ maxPriorityFeePerGas: BigNumberish | undefined;
+ maxFeePerGas: BigNumberish | undefined;
+ gasPrice: BigNumberish | undefined;
+};
+
+export type IOracle = (
+ apiKey: string,
+ provider?: ethers.providers.JsonRpcProvider,
+ options?: IOracleOptions
+) => Promise;
+
+export type IOracleOptions = {
+ entryPoint: string;
+ userOp: UserOperationStruct;
+};
diff --git a/packages/params/src/gas-price-oracles/oracles/mantle.ts b/packages/params/src/gas-price-oracles/oracles/mantle.ts
new file mode 100644
index 00000000..cfbeb045
--- /dev/null
+++ b/packages/params/src/gas-price-oracles/oracles/mantle.ts
@@ -0,0 +1,55 @@
+import { BigNumber, ethers } from "ethers";
+import { MantleGasOracleABI } from "types/lib/executor/abis";
+import mantleSDK from "@mantleio/sdk";
+import { IGetGasFeeResult, IOracle } from "./interfaces";
+
+const oracleAddress = "0x420000000000000000000000000000000000000F";
+const minGasPrice = 50000000;
+
+export const getMantleGasFee: IOracle = async (
+ apiKey,
+ provider,
+ options
+): Promise => {
+ if (!provider) throw new Error("No provider");
+
+ const oracle = new ethers.Contract(
+ oracleAddress,
+ MantleGasOracleABI,
+ provider
+ );
+
+ let gasPrice = await oracle.callStatic.gasPrice();
+ if (gasPrice && BigNumber.from(gasPrice).lt(minGasPrice)) {
+ gasPrice = BigNumber.from(minGasPrice);
+ }
+
+ if (options) {
+ const tx = {
+ from: options.entryPoint,
+ to: options.userOp.sender,
+ data: options.userOp.callData,
+ };
+ try {
+ const mantleProvider = mantleSDK.asL2Provider(provider);
+ const L1Price = await mantleProvider.getL1GasPrice();
+ const L1Cost = L1Price.mul(2562); // constant l1 gas used, set by Mantle
+ const L2Cost = await mantleProvider.estimateL2GasCost(tx);
+ const { callGasLimit, preVerificationGas, verificationGasLimit } =
+ options.userOp;
+ const totalGasLimit = BigNumber.from(callGasLimit)
+ .add(preVerificationGas)
+ .add(verificationGasLimit);
+ gasPrice = L1Cost.add(L2Cost).div(totalGasLimit);
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.log("Error during estimating total gas cost on Mantle", err);
+ }
+ }
+
+ return {
+ maxPriorityFeePerGas: gasPrice,
+ gasPrice: gasPrice,
+ maxFeePerGas: gasPrice,
+ };
+};
diff --git a/packages/executor/src/utils/gas-oracles/matic.ts b/packages/params/src/gas-price-oracles/oracles/matic.ts
similarity index 100%
rename from packages/executor/src/utils/gas-oracles/matic.ts
rename to packages/params/src/gas-price-oracles/oracles/matic.ts
diff --git a/packages/executor/src/utils/gas-oracles/mumbai.ts b/packages/params/src/gas-price-oracles/oracles/mumbai.ts
similarity index 100%
rename from packages/executor/src/utils/gas-oracles/mumbai.ts
rename to packages/params/src/gas-price-oracles/oracles/mumbai.ts
diff --git a/packages/executor/src/utils/gas-oracles/optimism.ts b/packages/params/src/gas-price-oracles/oracles/optimism.ts
similarity index 100%
rename from packages/executor/src/utils/gas-oracles/optimism.ts
rename to packages/params/src/gas-price-oracles/oracles/optimism.ts
diff --git a/packages/executor/src/utils/gas-oracles/utils.ts b/packages/params/src/gas-price-oracles/oracles/utils.ts
similarity index 100%
rename from packages/executor/src/utils/gas-oracles/utils.ts
rename to packages/params/src/gas-price-oracles/oracles/utils.ts
diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts
index a6294878..23a62ffe 100644
--- a/packages/params/src/index.ts
+++ b/packages/params/src/index.ts
@@ -5,3 +5,4 @@ export * from "./mempools";
export * from "./eip1559";
export * from "./gas-estimation";
export * from "./constants";
+export * from "./gas-price-oracles";
diff --git a/packages/params/src/whitelisted-entities/factories.ts b/packages/params/src/whitelisted-entities/factories.ts
index 72ea2c13..4f238c82 100644
--- a/packages/params/src/whitelisted-entities/factories.ts
+++ b/packages/params/src/whitelisted-entities/factories.ts
@@ -1,3 +1,36 @@
+import { getAddress } from "ethers/lib/utils";
import { IWhitelistedEntity } from "../types/IWhitelistedEntities";
-export const WhitelistedFactories: IWhitelistedEntity = {};
+export const WhitelistedFactories: IWhitelistedEntity = {
+ // Etherspot Paymasters
+ // ref: https://github.com/etherspot/etherspot-prime-contracts/blob/master/DEPLOYMENTS.md
+ mainnet: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ arbitrum: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ optimism: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ matic: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ fuse: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ xdai: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ mantle: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ avalanche: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ bsc: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ base: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ linea: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ goerli: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ sepolia: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ arbitrumNitro: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ optimismGoerli: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ mumbai: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ fuseSparknet: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ baseGoerli: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ chiado: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ fuji: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ bscTest: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ lineaTestnet: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ scrollSepolia: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ mantleTestnet: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ flare: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ flareCoston: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ flareCoston2: [getAddress("0x7f6d8F107fE8551160BD5351d5F1514A6aD5d40E")],
+ bifrost: [getAddress("0x527bAb8bDC50A809d7c35D0129173BBed55C5EAE")],
+ bifrostTestnet: [getAddress("0x527bAb8bDC50A809d7c35D0129173BBed55C5EAE")],
+};
diff --git a/packages/types/package.json b/packages/types/package.json
index de8b9c53..d19c365f 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "types",
- "version": "0.0.24",
+ "version": "0.0.44",
"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 4dbfefa3..aff9fc3c 100644
--- a/packages/types/src/api/interfaces.ts
+++ b/packages/types/src/api/interfaces.ts
@@ -1,14 +1,16 @@
import { BigNumberish, providers } from "ethers";
import { UserOperationStruct } from "../executor/contracts/EntryPoint";
-export type EstimatedUserOperationGas = {
- preVerificationGas: BigNumberish;
- verificationGas: BigNumberish;
- deadline?: BigNumberish;
- callGasLimit: BigNumberish;
- validAfter?: BigNumberish;
- validUntil?: BigNumberish;
-};
+export type EstimatedUserOperationGas =
+ | {
+ preVerificationGas: BigNumberish;
+ verificationGas: BigNumberish;
+ verificationGasLimit: BigNumberish;
+ callGasLimit: BigNumberish;
+ validAfter?: BigNumberish;
+ validUntil?: BigNumberish;
+ }
+ | GetGasPriceResponse;
export type UserOperationByHashResponse = {
userOperation: UserOperationStruct;
@@ -23,6 +25,12 @@ export type GetGasPriceResponse = {
maxPriorityFeePerGas: BigNumberish;
};
+export type GetFeeHistoryResponse = {
+ actualGasPrice: BigNumberish[];
+ maxFeePerGas: BigNumberish[];
+ maxPriorityFeePerGas: BigNumberish[];
+};
+
export type UserOperationReceipt = {
userOpHash: string;
sender: string;
@@ -36,6 +44,31 @@ export type UserOperationReceipt = {
receipt: providers.TransactionReceipt;
};
+export type GetConfigResponse = {
+ flags: {
+ redirectRpc: boolean;
+ testingMode: boolean;
+ unsafeMode: boolean;
+ };
+ entryPoints: string[];
+ beneficiary: string;
+ relayer: string;
+ minInclusionDenominator: number;
+ throttlingSlack: number;
+ banSlack: number;
+ minSignerBalance: string;
+ multicall: string;
+ estimationStaticBuffer: number;
+ validationGasLimit: number;
+ receiptLookupRange: number;
+ etherscanApiKey: boolean; // true if set
+ conditionalTransactions: boolean;
+ rpcEndpointSubmit: boolean; // true if not empty string
+ gasPriceMarkup: number;
+ enforceGasPrice: boolean;
+ enforceGasPriceThreshold: number;
+};
+
export type SupportedEntryPoints = string[];
export type EthChainIdResponse = { chainId: number };
diff --git a/packages/types/src/executor/abis/MantleGasOracle.ts b/packages/types/src/executor/abis/MantleGasOracle.ts
new file mode 100644
index 00000000..f3083d3a
--- /dev/null
+++ b/packages/types/src/executor/abis/MantleGasOracle.ts
@@ -0,0 +1,237 @@
+export const MantleGasOracleABI = [
+ {
+ type: "constructor",
+ inputs: [{ type: "address", name: "_owner", internalType: "address" }],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "IsBurning",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "charge",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "daGasPrice",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "daSwitch",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "decimals",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "gasPrice",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "getL1Fee",
+ inputs: [{ type: "bytes", name: "_data", internalType: "bytes" }],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "getL1GasUsed",
+ inputs: [{ type: "bytes", name: "_data", internalType: "bytes" }],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "l1BaseFee",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "overhead",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "address", name: "", internalType: "address" }],
+ name: "owner",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "renounceOwnership",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "uint256", name: "", internalType: "uint256" }],
+ name: "scalar",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "view",
+ outputs: [{ type: "address", name: "", internalType: "address" }],
+ name: "sccAddress",
+ inputs: [],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setCharge",
+ inputs: [{ type: "uint256", name: "_charge", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setDAGasPrice",
+ inputs: [{ type: "uint256", name: "_daGasPrice", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setDaSwitch",
+ inputs: [{ type: "uint256", name: "_daSwitch", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setDecimals",
+ inputs: [{ type: "uint256", name: "_decimals", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setGasPrice",
+ inputs: [{ type: "uint256", name: "_gasPrice", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setIsBurning",
+ inputs: [{ type: "uint256", name: "_isBurning", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setL1BaseFee",
+ inputs: [{ type: "uint256", name: "_baseFee", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setOverhead",
+ inputs: [{ type: "uint256", name: "_overhead", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "setScalar",
+ inputs: [{ type: "uint256", name: "_scalar", internalType: "uint256" }],
+ },
+ {
+ type: "function",
+ stateMutability: "nonpayable",
+ outputs: [],
+ name: "transferOwnership",
+ inputs: [{ type: "address", name: "newOwner", internalType: "address" }],
+ },
+ {
+ type: "event",
+ name: "ChargeUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "DAGasPriceUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "DASwitchUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "DecimalsUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "GasPriceUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "IsBurningUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "L1BaseFeeUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "OverheadUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "OwnershipTransferred",
+ inputs: [
+ { type: "address", name: "previousOwner", indexed: true },
+ { type: "address", name: "newOwner", indexed: true },
+ ],
+ anonymous: false,
+ },
+ {
+ type: "event",
+ name: "ScalarUpdated",
+ inputs: [{ type: "uint256", name: "", indexed: false }],
+ anonymous: false,
+ },
+];
diff --git a/packages/types/src/executor/abis/index.ts b/packages/types/src/executor/abis/index.ts
new file mode 100644
index 00000000..270e24d6
--- /dev/null
+++ b/packages/types/src/executor/abis/index.ts
@@ -0,0 +1 @@
+export * from "./MantleGasOracle";
diff --git a/packages/types/src/executor/contracts/EntryPoint.ts b/packages/types/src/executor/contracts/EntryPoint.ts
index f92bc232..61588898 100644
--- a/packages/types/src/executor/contracts/EntryPoint.ts
+++ b/packages/types/src/executor/contracts/EntryPoint.ts
@@ -109,69 +109,11 @@ export declare namespace IEntryPoint {
};
}
-export declare namespace EntryPoint {
- export type MemoryUserOpStruct = {
- sender: string;
- nonce: BigNumberish;
- callGasLimit: BigNumberish;
- verificationGasLimit: BigNumberish;
- preVerificationGas: BigNumberish;
- paymaster: string;
- maxFeePerGas: BigNumberish;
- maxPriorityFeePerGas: BigNumberish;
- };
-
- export type MemoryUserOpStructOutput = [
- string,
- BigNumber,
- BigNumber,
- BigNumber,
- BigNumber,
- string,
- BigNumber,
- BigNumber
- ] & {
- sender: string;
- nonce: BigNumber;
- callGasLimit: BigNumber;
- verificationGasLimit: BigNumber;
- preVerificationGas: BigNumber;
- paymaster: string;
- maxFeePerGas: BigNumber;
- maxPriorityFeePerGas: BigNumber;
- };
-
- export type UserOpInfoStruct = {
- mUserOp: EntryPoint.MemoryUserOpStruct;
- userOpHash: BytesLike;
- prefund: BigNumberish;
- contextOffset: BigNumberish;
- preOpGas: BigNumberish;
- };
-
- export type UserOpInfoStructOutput = [
- EntryPoint.MemoryUserOpStructOutput,
- string,
- BigNumber,
- BigNumber,
- BigNumber
- ] & {
- mUserOp: EntryPoint.MemoryUserOpStructOutput;
- userOpHash: string;
- prefund: BigNumber;
- contextOffset: BigNumber;
- preOpGas: BigNumber;
- };
-}
-
-export interface EntryPointInterface extends utils.Interface {
+export interface IEntryPointInterface extends utils.Interface {
functions: {
- "SIG_VALIDATION_FAILED()": FunctionFragment;
- "_validateSenderAndPaymaster(bytes,address,bytes)": FunctionFragment;
"addStake(uint32)": FunctionFragment;
"balanceOf(address)": FunctionFragment;
"depositTo(address)": FunctionFragment;
- "deposits(address)": FunctionFragment;
"getDepositInfo(address)": FunctionFragment;
"getNonce(address,uint192)": FunctionFragment;
"getSenderAddress(bytes)": FunctionFragment;
@@ -179,8 +121,6 @@ export interface EntryPointInterface extends utils.Interface {
"handleAggregatedOps(((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],address,bytes)[],address)": FunctionFragment;
"handleOps((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],address)": FunctionFragment;
"incrementNonce(uint192)": FunctionFragment;
- "innerHandleOp(bytes,((address,uint256,uint256,uint256,uint256,address,uint256,uint256),bytes32,uint256,uint256,uint256),bytes)": FunctionFragment;
- "nonceSequenceNumber(address,uint192)": FunctionFragment;
"simulateHandleOp((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes),address,bytes)": FunctionFragment;
"simulateValidation((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes))": FunctionFragment;
"unlockStake()": FunctionFragment;
@@ -190,12 +130,9 @@ export interface EntryPointInterface extends utils.Interface {
getFunction(
nameOrSignatureOrTopic:
- | "SIG_VALIDATION_FAILED"
- | "_validateSenderAndPaymaster"
| "addStake"
| "balanceOf"
| "depositTo"
- | "deposits"
| "getDepositInfo"
| "getNonce"
| "getSenderAddress"
@@ -203,8 +140,6 @@ export interface EntryPointInterface extends utils.Interface {
| "handleAggregatedOps"
| "handleOps"
| "incrementNonce"
- | "innerHandleOp"
- | "nonceSequenceNumber"
| "simulateHandleOp"
| "simulateValidation"
| "unlockStake"
@@ -212,18 +147,6 @@ export interface EntryPointInterface extends utils.Interface {
| "withdrawTo"
): FunctionFragment;
- encodeFunctionData(
- functionFragment: "SIG_VALIDATION_FAILED",
- values?: undefined
- ): string;
- encodeFunctionData(
- functionFragment: "_validateSenderAndPaymaster",
- values: [
- BytesLike,
- string,
- BytesLike
- ]
- ): string;
encodeFunctionData(
functionFragment: "addStake",
values: [BigNumberish]
@@ -236,10 +159,6 @@ export interface EntryPointInterface extends utils.Interface {
functionFragment: "depositTo",
values: [string]
): string;
- encodeFunctionData(
- functionFragment: "deposits",
- values: [string]
- ): string;
encodeFunctionData(
functionFragment: "getDepositInfo",
values: [string]
@@ -268,18 +187,6 @@ export interface EntryPointInterface extends utils.Interface {
functionFragment: "incrementNonce",
values: [BigNumberish]
): string;
- encodeFunctionData(
- functionFragment: "innerHandleOp",
- values: [
- BytesLike,
- EntryPoint.UserOpInfoStruct,
- BytesLike
- ]
- ): string;
- encodeFunctionData(
- functionFragment: "nonceSequenceNumber",
- values: [string, BigNumberish]
- ): string;
encodeFunctionData(
functionFragment: "simulateHandleOp",
values: [
@@ -305,18 +212,9 @@ export interface EntryPointInterface extends utils.Interface {
values: [string, BigNumberish]
): string;
- decodeFunctionResult(
- functionFragment: "SIG_VALIDATION_FAILED",
- data: BytesLike
- ): Result;
- decodeFunctionResult(
- functionFragment: "_validateSenderAndPaymaster",
- data: BytesLike
- ): Result;
decodeFunctionResult(functionFragment: "addStake", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "depositTo", data: BytesLike): Result;
- decodeFunctionResult(functionFragment: "deposits", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getDepositInfo",
data: BytesLike
@@ -339,14 +237,6 @@ export interface EntryPointInterface extends utils.Interface {
functionFragment: "incrementNonce",
data: BytesLike
): Result;
- decodeFunctionResult(
- functionFragment: "innerHandleOp",
- data: BytesLike
- ): Result;
- decodeFunctionResult(
- functionFragment: "nonceSequenceNumber",
- data: BytesLike
- ): Result;
decodeFunctionResult(
functionFragment: "simulateHandleOp",
data: BytesLike
@@ -508,12 +398,12 @@ export type WithdrawnEvent = TypedEvent<
export type WithdrawnEventFilter = TypedEventFilter;
-export interface EntryPoint extends BaseContract {
+export interface IEntryPoint extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise;
- interface: EntryPointInterface;
+ interface: IEntryPointInterface;
queryFilter(
event: TypedEventFilter,
@@ -535,17 +425,8 @@ export interface EntryPoint extends BaseContract {
removeListener: OnEvent;
functions: {
- SIG_VALIDATION_FAILED(overrides?: CallOverrides): Promise<[BigNumber]>;
-
- _validateSenderAndPaymaster(
- initCode: BytesLike,
- sender: string,
- paymasterAndData: BytesLike,
- overrides?: CallOverrides
- ): Promise<[void]>;
-
addStake(
- unstakeDelaySec: BigNumberish,
+ _unstakeDelaySec: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise;
@@ -559,19 +440,6 @@ export interface EntryPoint extends BaseContract {
overrides?: PayableOverrides & { from?: string }
): Promise;
- deposits(
- arg0: string,
- overrides?: CallOverrides
- ): Promise<
- [BigNumber, boolean, BigNumber, number, number] & {
- deposit: BigNumber;
- staked: boolean;
- stake: BigNumber;
- unstakeDelaySec: number;
- withdrawTime: number;
- }
- >;
-
getDepositInfo(
account: string,
overrides?: CallOverrides
@@ -614,19 +482,6 @@ export interface EntryPoint extends BaseContract {
overrides?: Overrides & { from?: string }
): Promise;
- innerHandleOp(
- callData: BytesLike,
- opInfo: EntryPoint.UserOpInfoStruct,
- context: BytesLike,
- overrides?: Overrides & { from?: string }
- ): Promise;
-
- nonceSequenceNumber(
- arg0: string,
- arg1: BigNumberish,
- overrides?: CallOverrides
- ): Promise<[BigNumber]>;
-
simulateHandleOp(
op: UserOperationStruct,
target: string,
@@ -655,17 +510,8 @@ export interface EntryPoint extends BaseContract {
): Promise;
};
- SIG_VALIDATION_FAILED(overrides?: CallOverrides): Promise;
-
- _validateSenderAndPaymaster(
- initCode: BytesLike,
- sender: string,
- paymasterAndData: BytesLike,
- overrides?: CallOverrides
- ): Promise;
-
addStake(
- unstakeDelaySec: BigNumberish,
+ _unstakeDelaySec: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise;
@@ -679,19 +525,6 @@ export interface EntryPoint extends BaseContract {
overrides?: PayableOverrides & { from?: string }
): Promise;
- deposits(
- arg0: string,
- overrides?: CallOverrides
- ): Promise<
- [BigNumber, boolean, BigNumber, number, number] & {
- deposit: BigNumber;
- staked: boolean;
- stake: BigNumber;
- unstakeDelaySec: number;
- withdrawTime: number;
- }
- >;
-
getDepositInfo(
account: string,
overrides?: CallOverrides
@@ -730,19 +563,6 @@ export interface EntryPoint extends BaseContract {
overrides?: Overrides & { from?: string }
): Promise;
- innerHandleOp(
- callData: BytesLike,
- opInfo: EntryPoint.UserOpInfoStruct,
- context: BytesLike,
- overrides?: Overrides & { from?: string }
- ): Promise;
-
- nonceSequenceNumber(
- arg0: string,
- arg1: BigNumberish,
- overrides?: CallOverrides
- ): Promise;
-
simulateHandleOp(
op: UserOperationStruct,
target: string,
@@ -771,17 +591,8 @@ export interface EntryPoint extends BaseContract {
): Promise;
callStatic: {
- SIG_VALIDATION_FAILED(overrides?: CallOverrides): Promise;
-
- _validateSenderAndPaymaster(
- initCode: BytesLike,
- sender: string,
- paymasterAndData: BytesLike,
- overrides?: CallOverrides
- ): Promise;
-
addStake(
- unstakeDelaySec: BigNumberish,
+ _unstakeDelaySec: BigNumberish,
overrides?: CallOverrides
): Promise;
@@ -795,19 +606,6 @@ export interface EntryPoint extends BaseContract {
overrides?: CallOverrides
): Promise;
- deposits(
- arg0: string,
- overrides?: CallOverrides
- ): Promise<
- [BigNumber, boolean, BigNumber, number, number] & {
- deposit: BigNumber;
- staked: boolean;
- stake: BigNumber;
- unstakeDelaySec: number;
- withdrawTime: number;
- }
- >;
-
getDepositInfo(
account: string,
overrides?: CallOverrides
@@ -846,19 +644,6 @@ export interface EntryPoint extends BaseContract {
overrides?: CallOverrides
): Promise;
- innerHandleOp(
- callData: BytesLike,
- opInfo: EntryPoint.UserOpInfoStruct,
- context: BytesLike,
- overrides?: CallOverrides
- ): Promise;
-
- nonceSequenceNumber(
- arg0: string,
- arg1: BigNumberish,
- overrides?: CallOverrides
- ): Promise;
-
simulateHandleOp(
op: UserOperationStruct,
target: string,
@@ -994,17 +779,8 @@ export interface EntryPoint extends BaseContract {
};
estimateGas: {
- SIG_VALIDATION_FAILED(overrides?: CallOverrides): Promise;
-
- _validateSenderAndPaymaster(
- initCode: BytesLike,
- sender: string,
- paymasterAndData: BytesLike,
- overrides?: CallOverrides
- ): Promise;
-
addStake(
- unstakeDelaySec: BigNumberish,
+ _unstakeDelaySec: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise;
@@ -1018,11 +794,6 @@ export interface EntryPoint extends BaseContract {
overrides?: PayableOverrides & { from?: string }
): Promise;
- deposits(
- arg0: string,
- overrides?: CallOverrides
- ): Promise;
-
getDepositInfo(
account: string,
overrides?: CallOverrides
@@ -1061,19 +832,6 @@ export interface EntryPoint extends BaseContract {
overrides?: Overrides & { from?: string }
): Promise;
- innerHandleOp(
- callData: BytesLike,
- opInfo: EntryPoint.UserOpInfoStruct,
- context: BytesLike,
- overrides?: Overrides & { from?: string }
- ): Promise;
-
- nonceSequenceNumber(
- arg0: string,
- arg1: BigNumberish,
- overrides?: CallOverrides
- ): Promise;
-
simulateHandleOp(
op: UserOperationStruct,
target: string,
@@ -1103,19 +861,8 @@ export interface EntryPoint extends BaseContract {
};
populateTransaction: {
- SIG_VALIDATION_FAILED(
- overrides?: CallOverrides
- ): Promise;
-
- _validateSenderAndPaymaster(
- initCode: BytesLike,
- sender: string,
- paymasterAndData: BytesLike,
- overrides?: CallOverrides
- ): Promise;
-
addStake(
- unstakeDelaySec: BigNumberish,
+ _unstakeDelaySec: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise;
@@ -1129,11 +876,6 @@ export interface EntryPoint extends BaseContract {
overrides?: PayableOverrides & { from?: string }
): Promise;
- deposits(
- arg0: string,
- overrides?: CallOverrides
- ): Promise;
-
getDepositInfo(
account: string,
overrides?: CallOverrides
@@ -1172,19 +914,6 @@ export interface EntryPoint extends BaseContract {
overrides?: Overrides & { from?: string }
): Promise;
- innerHandleOp(
- callData: BytesLike,
- opInfo: EntryPoint.UserOpInfoStruct,
- context: BytesLike,
- overrides?: Overrides & { from?: string }
- ): Promise;
-
- nonceSequenceNumber(
- arg0: string,
- arg1: BigNumberish,
- overrides?: CallOverrides
- ): Promise;
-
simulateHandleOp(
op: UserOperationStruct,
target: string,
diff --git a/packages/types/src/executor/contracts/IAccount.ts b/packages/types/src/executor/contracts/IAccount.ts
index 385ca1d9..67b79066 100644
--- a/packages/types/src/executor/contracts/IAccount.ts
+++ b/packages/types/src/executor/contracts/IAccount.ts
@@ -1,7 +1,7 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
-import {
+import type {
BaseContract,
BigNumber,
BigNumberish,
@@ -13,9 +13,14 @@ import {
Signer,
utils,
} from "ethers";
-import { FunctionFragment, Result } from "@ethersproject/abi";
-import { Listener, Provider } from "@ethersproject/providers";
-import { TypedEventFilter, TypedEvent, TypedListener, OnEvent } from "./common";
+import type { FunctionFragment, Result } from "@ethersproject/abi";
+import type { Listener, Provider } from "@ethersproject/providers";
+import type {
+ TypedEventFilter,
+ TypedEvent,
+ TypedListener,
+ OnEvent
+} from "./common";
export type UserOperationStruct = {
sender: string;
@@ -59,12 +64,18 @@ export type UserOperationStructOutput = [
export interface IAccountInterface extends utils.Interface {
functions: {
- "validateUserOp((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes32,address,uint256)": FunctionFragment;
+ "validateUserOp((address,uint256,bytes,bytes,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes32,uint256)": FunctionFragment;
};
+ getFunction(nameOrSignatureOrTopic: "validateUserOp"): FunctionFragment;
+
encodeFunctionData(
functionFragment: "validateUserOp",
- values: [UserOperationStruct, BytesLike, string, BigNumberish]
+ values: [
+ UserOperationStruct,
+ BytesLike,
+ BigNumberish
+ ]
): string;
decodeFunctionResult(
@@ -105,25 +116,22 @@ export interface IAccount extends BaseContract {
validateUserOp(
userOp: UserOperationStruct,
userOpHash: BytesLike,
- aggregator: string,
missingAccountFunds: BigNumberish,
- overrides?: Overrides & { from?: string | Promise }
+ overrides?: Overrides & { from?: string }
): Promise;
};
validateUserOp(
userOp: UserOperationStruct,
userOpHash: BytesLike,
- aggregator: string,
missingAccountFunds: BigNumberish,
- overrides?: Overrides & { from?: string | Promise }
+ overrides?: Overrides & { from?: string }
): Promise;
callStatic: {
validateUserOp(
userOp: UserOperationStruct,
userOpHash: BytesLike,
- aggregator: string,
missingAccountFunds: BigNumberish,
overrides?: CallOverrides
): Promise;
@@ -135,9 +143,8 @@ export interface IAccount extends BaseContract {
validateUserOp(
userOp: UserOperationStruct,
userOpHash: BytesLike,
- aggregator: string,
missingAccountFunds: BigNumberish,
- overrides?: Overrides & { from?: string | Promise }
+ overrides?: Overrides & { from?: string }
): Promise;
};
@@ -145,9 +152,8 @@ export interface IAccount extends BaseContract {
validateUserOp(
userOp: UserOperationStruct,
userOpHash: BytesLike,
- aggregator: string,
missingAccountFunds: BigNumberish,
- overrides?: Overrides & { from?: string | Promise }
+ overrides?: Overrides & { from?: string }
): Promise;
};
}
diff --git a/packages/types/src/executor/contracts/SenderCreator.ts b/packages/types/src/executor/contracts/SenderCreator.ts
new file mode 100644
index 00000000..74ebdbf8
--- /dev/null
+++ b/packages/types/src/executor/contracts/SenderCreator.ts
@@ -0,0 +1,104 @@
+/* Autogenerated file. Do not edit manually. */
+/* tslint:disable */
+/* eslint-disable */
+import type {
+ BaseContract,
+ BigNumber,
+ BytesLike,
+ CallOverrides,
+ ContractTransaction,
+ Overrides,
+ PopulatedTransaction,
+ Signer,
+ utils,
+} from "ethers";
+import type { FunctionFragment, Result } from "@ethersproject/abi";
+import type { Listener, Provider } from "@ethersproject/providers";
+import type {
+ TypedEventFilter,
+ TypedEvent,
+ TypedListener,
+ OnEvent
+} from "./common";
+
+export interface SenderCreatorInterface extends utils.Interface {
+ functions: {
+ "createSender(bytes)": FunctionFragment;
+ };
+
+ getFunction(nameOrSignatureOrTopic: "createSender"): FunctionFragment;
+
+ encodeFunctionData(
+ functionFragment: "createSender",
+ values: [BytesLike]
+ ): string;
+
+ decodeFunctionResult(
+ functionFragment: "createSender",
+ data: BytesLike
+ ): Result;
+
+ events: {};
+}
+
+export interface SenderCreator extends BaseContract {
+ connect(signerOrProvider: Signer | Provider | string): this;
+ attach(addressOrName: string): this;
+ deployed(): Promise;
+
+ interface: SenderCreatorInterface;
+
+ queryFilter(
+ event: TypedEventFilter,
+ fromBlockOrBlockhash?: string | number | undefined,
+ toBlock?: string | number | undefined
+ ): Promise>;
+
+ listeners(
+ eventFilter?: TypedEventFilter
+ ): Array>;
+ listeners(eventName?: string): Array;
+ removeAllListeners(
+ eventFilter: TypedEventFilter
+ ): this;
+ removeAllListeners(eventName?: string): this;
+ off: OnEvent;
+ on: OnEvent;
+ once: OnEvent;
+ removeListener: OnEvent;
+
+ functions: {
+ createSender(
+ initCode: BytesLike,
+ overrides?: Overrides & { from?: string }
+ ): Promise;
+ };
+
+ createSender(
+ initCode: BytesLike,
+ overrides?: Overrides & { from?: string }
+ ): Promise;
+
+ callStatic: {
+ createSender(
+ initCode: BytesLike,
+ overrides?: CallOverrides
+ ): Promise;
+ };
+
+ filters: {};
+
+ estimateGas: {
+ createSender(
+ initCode: BytesLike,
+ overrides?: Overrides & { from?: string }
+ ): Promise;
+ };
+
+ populateTransaction: {
+ createSender(
+ initCode: BytesLike,
+ overrides?: Overrides & { from?: string }
+ ): Promise;
+ };
+}
diff --git a/packages/types/src/executor/contracts/factories/EntryPoint__factory.ts b/packages/types/src/executor/contracts/factories/EntryPoint__factory.ts
index d2f4b61c..e9597bec 100644
--- a/packages/types/src/executor/contracts/factories/EntryPoint__factory.ts
+++ b/packages/types/src/executor/contracts/factories/EntryPoint__factory.ts
@@ -1,11 +1,12 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
-import { Signer, utils, Contract, ContractFactory, Overrides } from "ethers";
-import type { Provider, TransactionRequest } from "@ethersproject/providers";
+
+import { Contract, Signer, utils } from "ethers";
+import type { Provider } from "@ethersproject/providers";
import type {
- EntryPoint,
- EntryPointInterface,
+ IEntryPoint,
+ IEntryPointInterface,
} from "../EntryPoint";
const _abi = [
@@ -543,47 +544,11 @@ const _abi = [
name: "Withdrawn",
type: "event",
},
- {
- inputs: [],
- name: "SIG_VALIDATION_FAILED",
- outputs: [
- {
- internalType: "uint256",
- name: "",
- type: "uint256",
- },
- ],
- stateMutability: "view",
- type: "function",
- },
- {
- inputs: [
- {
- internalType: "bytes",
- name: "initCode",
- type: "bytes",
- },
- {
- internalType: "address",
- name: "sender",
- type: "address",
- },
- {
- internalType: "bytes",
- name: "paymasterAndData",
- type: "bytes",
- },
- ],
- name: "_validateSenderAndPaymaster",
- outputs: [],
- stateMutability: "view",
- type: "function",
- },
{
inputs: [
{
internalType: "uint32",
- name: "unstakeDelaySec",
+ name: "_unstakeDelaySec",
type: "uint32",
},
],
@@ -624,45 +589,6 @@ const _abi = [
stateMutability: "payable",
type: "function",
},
- {
- inputs: [
- {
- internalType: "address",
- name: "",
- type: "address",
- },
- ],
- name: "deposits",
- outputs: [
- {
- internalType: "uint112",
- name: "deposit",
- type: "uint112",
- },
- {
- internalType: "bool",
- name: "staked",
- type: "bool",
- },
- {
- internalType: "uint112",
- name: "stake",
- type: "uint112",
- },
- {
- internalType: "uint32",
- name: "unstakeDelaySec",
- type: "uint32",
- },
- {
- internalType: "uint48",
- name: "withdrawTime",
- type: "uint48",
- },
- ],
- stateMutability: "view",
- type: "function",
- },
{
inputs: [
{
@@ -1002,128 +928,6 @@ const _abi = [
stateMutability: "nonpayable",
type: "function",
},
- {
- inputs: [
- {
- internalType: "bytes",
- name: "callData",
- type: "bytes",
- },
- {
- components: [
- {
- components: [
- {
- internalType: "address",
- name: "sender",
- type: "address",
- },
- {
- internalType: "uint256",
- name: "nonce",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "callGasLimit",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "verificationGasLimit",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "preVerificationGas",
- type: "uint256",
- },
- {
- internalType: "address",
- name: "paymaster",
- type: "address",
- },
- {
- internalType: "uint256",
- name: "maxFeePerGas",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "maxPriorityFeePerGas",
- type: "uint256",
- },
- ],
- internalType: "struct EntryPoint.MemoryUserOp",
- name: "mUserOp",
- type: "tuple",
- },
- {
- internalType: "bytes32",
- name: "userOpHash",
- type: "bytes32",
- },
- {
- internalType: "uint256",
- name: "prefund",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "contextOffset",
- type: "uint256",
- },
- {
- internalType: "uint256",
- name: "preOpGas",
- type: "uint256",
- },
- ],
- internalType: "struct EntryPoint.UserOpInfo",
- name: "opInfo",
- type: "tuple",
- },
- {
- internalType: "bytes",
- name: "context",
- type: "bytes",
- },
- ],
- name: "innerHandleOp",
- outputs: [
- {
- internalType: "uint256",
- name: "actualGasCost",
- type: "uint256",
- },
- ],
- stateMutability: "nonpayable",
- type: "function",
- },
- {
- inputs: [
- {
- internalType: "address",
- name: "",
- type: "address",
- },
- {
- internalType: "uint192",
- name: "",
- type: "uint192",
- },
- ],
- name: "nonceSequenceNumber",
- outputs: [
- {
- internalType: "uint256",
- name: "",
- type: "uint256",
- },
- ],
- stateMutability: "view",
- type: "function",
- },
{
inputs: [
{
@@ -1312,58 +1116,17 @@ const _abi = [
stateMutability: "nonpayable",
type: "function",
},
- {
- stateMutability: "payable",
- type: "receive",
- },
] as const;
-const _bytecode =
- "0x60a080604052346200008957600160025561022c8181016001600160401b038111838210176200007357829162005d18833903906000f080156200006757608052604051615c8990816200008f82396080518181816113df01528181613e9501526141b60152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b600080fdfe60806040526004361015610023575b361561001957600080fd5b610021615531565b005b60003560e01c80630396cb60146101b35780630bd28e3b146101aa5780631b2e01b8146101a15780631d732756146101985780631fad948c1461018f578063205c28781461018657806335567e1a1461017d5780634b1d7cf5146101745780635287ce121461016b57806370a08231146101625780638f41ec5a14610159578063957122ab146101505780639b249f6914610147578063a61935311461013e578063b760faf914610135578063bb9fe6bf1461012c578063c23a5cea14610123578063d6383f941461011a578063ee219423146101115763fc7e286d0361000e5761010c611bcd565b61000e565b5061010c6119b5565b5061010c61184d565b5061010c6116b4565b5061010c611536565b5061010c6114f7565b5061010c6114d6565b5061010c611337565b5061010c611164565b5061010c611129565b5061010c6110a4565b5061010c610f54565b5061010c610bf8565b5061010c610b33565b5061010c610994565b5061010c6108ba565b5061010c6106e7565b5061010c610467565b5061010c610385565b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760043563ffffffff8116808203610359576103547fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01916102716102413373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9161024d811515615697565b61026a610261600185015463ffffffff1690565b63ffffffff1690565b11156156fc565b54926103366dffffffffffffffffffffffffffff946102f461029834888460781c166121d5565b966102a4881515615761565b6102b0818911156157c6565b6102d4816102bc6105ec565b941684906dffffffffffffffffffffffffffff169052565b6001602084015287166dffffffffffffffffffffffffffff166040830152565b63ffffffff83166060820152600060808201526103313373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61582b565b6040805194855263ffffffff90911660208501523393918291820190565b0390a2005b600080fd5b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361035957565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760043577ffffffffffffffffffffffffffffffffffffffffffffffff81168103610359576104149033600052600160205260406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b61041e8154612491565b9055005b73ffffffffffffffffffffffffffffffffffffffff81160361035957565b6024359061044d82610422565b565b60c4359061044d82610422565b359061044d82610422565b50346103595760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760206104fc6004356104a881610422565b73ffffffffffffffffffffffffffffffffffffffff6104c561035e565b91166000526001835260406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b54604051908152f35b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff82111761055157604052565b610559610505565b604052565b610100810190811067ffffffffffffffff82111761055157604052565b67ffffffffffffffff811161055157604052565b6060810190811067ffffffffffffffff82111761055157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761055157604052565b6040519061044d82610535565b6040519060c0820182811067ffffffffffffffff82111761055157604052565b604051906040820182811067ffffffffffffffff82111761055157604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff8111610675575b01160190565b61067d610505565b61066f565b92919261068e82610639565b9161069c60405193846105ab565b829481845281830111610359578281602093846000960137010152565b9181601f840112156103595782359167ffffffffffffffff8311610359576020838186019501011161035957565b5034610359576101c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595767ffffffffffffffff60043581811161035957366023820112156103595761074a903690602481600401359101610682565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101808112610359576101006040519161078783610535565b12610359576040516107988161055e565b6107a0610440565b815260443560208201526064356040820152608435606082015260a43560808201526107ca61044f565b60a082015260e43560c08201526101043560e082015281526101243560208201526101443560408201526101643560608201526101843560808201526101a4359182116103595761083e9261082661082e9336906004016106b9565b9290916128b1565b6040519081529081906020820190565b0390f35b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126103595760043567ffffffffffffffff9283821161035957806023830112156103595781600401359384116103595760248460051b830101116103595760240191906024356108b781610422565b90565b5034610359576108c936610842565b6108d4929192611e3a565b6108dd83611d2d565b60005b84811061095d57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f9728480a183915b85831061092d576109238585611ed7565b6100216001600255565b909193600190610953610941878987611dec565b61094b8886611dca565b51908861233f565b0194019190610912565b8061098b610984610972600194869896611dca565b5161097e848a88611dec565b84613448565b9083612f30565b019290926108e0565b50346103595760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610359576004356109d081610422565b6024359060009133835282602052604083206dffffffffffffffffffffffffffff81541692838311610ad557848373ffffffffffffffffffffffffffffffffffffffff829593610a788496610a3f610a2c8798610ad29c6121c0565b6dffffffffffffffffffffffffffff1690565b6dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b6040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af1610acc611ea7565b50615ba2565b80f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b50346103595760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610359576020600435610b7181610422565b73ffffffffffffffffffffffffffffffffffffffff610b8e61035e565b911660005260018252610bc98160406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b503461035957610c0736610842565b610c0f611e3a565b6000805b838210610df657610c249150611d2d565b7fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000805b848110610d5c57505060008093815b818110610c9357610923868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2611ed7565b610cf7610ca182848a6124cb565b610ccc610cb3610cb36020840161256d565b73ffffffffffffffffffffffffffffffffffffffff1690565b7f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a280612519565b906000915b808310610d1457505050610d0f90612491565b610c5c565b90919497610d4f610d49610d5592610d438c8b610d3c82610d368e8b8d611dec565b92611dca565b519161233f565b906121d5565b99612491565b95612491565b9190610cfc565b610d678186886124cb565b6020610d7f610d768380612519565b9290930161256d565b9173ffffffffffffffffffffffffffffffffffffffff60009316905b828410610db45750505050610daf90612491565b610c4d565b90919294610d4f81610de985610de2610dd0610dee968d611dca565b51610ddc8c8b8a611dec565b85613448565b908b613148565b612491565b929190610d9b565b610e018285876124cb565b90610e0c8280612519565b92610e1c610cb36020830161256d565b9173ffffffffffffffffffffffffffffffffffffffff8316610e416001821415612577565b610e62575b505050610e5c91610e56916121d5565b91612491565b90610c13565b909592610e7b6040999693999895989788810190611fc8565b92908a3b156103595789938b918a5193849283927fe3563a4f00000000000000000000000000000000000000000000000000000000845260049e8f850193610ec294612711565b03815a93600094fa9081610f3b575b50610f255786517f86a9f75000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a16818a0190815281906020010390fd5b0390fd5b9497509295509093509181610e56610e5c610e46565b80610f48610f4e9261057b565b8061111e565b38610ed1565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595761083e73ffffffffffffffffffffffffffffffffffffffff600435610fa881610422565b608060409283928351610fba81610535565b60009381858093528260208201528287820152826060820152015216815280602052209061104965ffffffffffff6001835194610ff686610535565b80546dffffffffffffffffffffffffffff8082168852607082901c60ff161515602089015260789190911c1685870152015463ffffffff8116606086015260201c16608084019065ffffffffffff169052565b5191829182919091608065ffffffffffff8160a08401956dffffffffffffffffffffffffffff808251168652602082015115156020870152604082015116604086015263ffffffff6060820151166060860152015116910152565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595773ffffffffffffffffffffffffffffffffffffffff6004356110f581610422565b16600052600060205260206dffffffffffffffffffffffffffff60406000205416604051908152f35b600091031261035957565b50346103595760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035957602060405160018152f35b50346103595760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035957600467ffffffffffffffff8135818111610359576111b590369084016106b9565b9050602435916111c483610422565b604435908111610359576111db90369085016106b9565b92909115908161132d575b506112c6576014821015611236575b610f21836040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352820160409060208152600060208201520190565b6112466112529261124c92612b88565b90612b96565b60601c90565b3b1561125f5738806111f5565b610f21906040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352820160609060208152601b60208201527f41413330207061796d6173746572206e6f74206465706c6f796564000000000060408201520190565b610f21836040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352820160609060208152601960208201527f41413230206163636f756e74206e6f74206465706c6f7965640000000000000060408201520190565b90503b15386111e6565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760043567ffffffffffffffff81116103595761138960249136906004016106b9565b906113bf6040519283927f570e1a3600000000000000000000000000000000000000000000000000000000845260048401612d2c565b0360208273ffffffffffffffffffffffffffffffffffffffff92816000857f0000000000000000000000000000000000000000000000000000000000000000165af1918215611471575b600092611441575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b61146391925060203d811161146a575b61145b81836105ab565b810190612d17565b9038611411565b503d611451565b611479612183565b611409565b90816101609103126103595790565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610359576004359067ffffffffffffffff8211610359576108b79160040161147e565b50346103595760206114ef6114ea3661148d565b612a0c565b604051908152f35b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595761002160043561153181610422565b61562b565b5034610359576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126116b1573381528060205260408120600181019063ffffffff825416908115611653576115f06115b5611618936115a76115a2855460ff9060701c1690565b61598f565b65ffffffffffff42166159f4565b84547fffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffff16602082901b69ffffffffffff000000001617909455565b7fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff8154169055565b60405165ffffffffffff91909116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a280f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b80fd5b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610359576004356116f081610422565b610ad273ffffffffffffffffffffffffffffffffffffffff6117323373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b926117ea611755610a2c86546dffffffffffffffffffffffffffff9060781c1690565b94611761861515615a0e565b6117c26001820161179a65ffffffffffff611786835465ffffffffffff9060201c1690565b16611792811515615a73565b421015615ad8565b80547fffffffffffffffffffffffffffffffffffffffffffff00000000000000000000169055565b7fffffff0000000000000000000000000000ffffffffffffffffffffffffffffff8154169055565b6040805173ffffffffffffffffffffffffffffffffffffffff831681526020810186905233917fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda391a2600080809581948294165af1611847611ea7565b50615b3d565b50346103595760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595767ffffffffffffffff6004358181116103595761189e90369060040161147e565b602435916118ab83610422565b604435908111610359576118c6610f219136906004016106b9565b6118ce611caa565b6118d785612e2b565b6118ea6118e48287613240565b906153ba565b946118fa826000924384526121e2565b96438252819360609573ffffffffffffffffffffffffffffffffffffffff8316611981575b50505050608001519361194e6040611940602084015165ffffffffffff1690565b92015165ffffffffffff1690565b906040519687967f8b7ac980000000000000000000000000000000000000000000000000000000008852600488016127e1565b8395508394965061199b60409492939451809481936127d3565b03925af19060806119aa611ea7565b92919038808061191f565b5034610359576119c43661148d565b6119cc611caa565b6119d582612e2b565b6119df8183613240565b825160a00151919391611a0c9073ffffffffffffffffffffffffffffffffffffffff166154dc565b6154dc565b90611a30611a07855173ffffffffffffffffffffffffffffffffffffffff90511690565b94611a39612b50565b50611a68611a4c60409586810190611fc8565b90600060148310611bc55750611246611a079261124c92612b88565b91611a72916153ba565b805173ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff821660018114916080880151978781015191886020820151611ac79065ffffffffffff1690565b91015165ffffffffffff16916060015192611ae06105f9565b9a8b5260208b0152841515898b015265ffffffffffff1660608a015265ffffffffffff16608089015260a088015215159081611bbc575b50611b515750610f2192519485947fe0cff05f00000000000000000000000000000000000000000000000000000000865260048601612cbd565b9190610f2193611b60846154dc565b611b87611b6b610619565b73ffffffffffffffffffffffffffffffffffffffff9096168652565b6020850152519586957ffaecb4e400000000000000000000000000000000000000000000000000000000875260048701612c2b565b90501538611b17565b9150506154dc565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595773ffffffffffffffffffffffffffffffffffffffff600435611c1e81610422565b16600052600060205260a0604060002065ffffffffffff60018254920154604051926dffffffffffffffffffffffffffff90818116855260ff8160701c161515602086015260781c16604084015263ffffffff8116606084015260201c166080820152f35b60209067ffffffffffffffff8111611c9d575b60051b0190565b611ca5610505565b611c96565b60405190611cb782610535565b604051608083610100830167ffffffffffffffff811184821017611d20575b60405260009283815283602082015283604082015283606082015283838201528360a08201528360c08201528360e082015281528260208201528260408201528260608201520152565b611d28610505565b611cd6565b90611d3782611c83565b611d4460405191826105ab565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611d728294611c83565b019060005b828110611d8357505050565b602090611d8e611caa565b82828501015201611d77565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020918151811015611ddf575b60051b010190565b611de7611d9a565b611dd7565b9190811015611e2d575b60051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea181360301821215610359570190565b611e35611d9a565b611df6565b6002805414611e495760028055565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b3d15611ed2573d90611eb882610639565b91611ec660405193846105ab565b82523d6000602084013e565b606090565b73ffffffffffffffffffffffffffffffffffffffff168015611f6a57600080809381935af1611f04611ea7565b5015611f0c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610359570180359067ffffffffffffffff82116103595760200191813603831361035957565b90816020910312610359575190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b60005b83811061207a5750506000910152565b818101518382015260200161206a565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f6020936120c681518092818752878088019101612067565b0116010190565b906120e76080916108b796946101c0808652850191612028565b9360e0815173ffffffffffffffffffffffffffffffffffffffff80825116602087015260208201516040870152604082015160608701526060820151858701528482015160a087015260a08201511660c086015260c081015182860152015161010084015260208101516101208401526040810151610140840152606081015161016084015201516101808201526101a081840391015261208a565b506040513d6000823e3d90fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b919082039182116121cd57565b61044d612190565b919082018092116121cd57565b905a918160206121fb6060830151936060810190611fc8565b906122348560405195869485947f1d732756000000000000000000000000000000000000000000000000000000008652600486016120cd565b03816000305af16000918161230f575b50612308575060206000803e7fdeaddead000000000000000000000000000000000000000000000000000000006000511461229b5761229561228a6108b7945a906121c0565b6080840151906121d5565b91614afc565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152600f60408201527f41413935206f7574206f6620676173000000000000000000000000000000000060608201520190565b9250505090565b61233191925060203d8111612338575b61232981836105ab565b810190612019565b9038612244565b503d61231f565b909291925a9380602061235b6060830151946060810190611fc8565b906123948660405195869485947f1d732756000000000000000000000000000000000000000000000000000000008652600486016120cd565b03816000305af160009181612471575b5061246a575060206000803e7fdeaddead00000000000000000000000000000000000000000000000000000000600051146123fc576123f66123eb6108b795965a906121c0565b6080830151906121d5565b92614ddf565b610f21836040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152600f60408201527f41413935206f7574206f6620676173000000000000000000000000000000000060608201520190565b9450505050565b61248a91925060203d81116123385761232981836105ab565b90386123a4565b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124bf570190565b6124c7612190565b0190565b919081101561250c575b60051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215610359570190565b612514611d9a565b6124d5565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610359570180359067ffffffffffffffff821161035957602001918160051b3603831361035957565b356108b781610422565b1561257e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561035957016020813591019167ffffffffffffffff821161035957813603831361035957565b6108b7916126578161263d8461045c565b73ffffffffffffffffffffffffffffffffffffffff169052565b602082013560208201526126f26126a361268861267760408601866125dc565b610160806040880152860191612028565b61269560608601866125dc565b908583036060870152612028565b6080840135608084015260a084013560a084015260c084013560c084015260e084013560e084015261010080850135908401526101206126e5818601866125dc565b9185840390860152612028565b9161270361014091828101906125dc565b929091818503910152612028565b949391929083604087016040885252606086019360608160051b8801019482600090815b848310612754575050505050508460206108b795968503910152612028565b9091929394977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08b820301855288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea1843603018112156127cf57600191846127bd920161262c565b98602090810196950193019190612735565b8280fd5b908092918237016000815290565b9290936108b796959260c0958552602085015265ffffffffffff8092166040850152166060830152151560808201528160a0820152019061208a565b1561282457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b9060406108b79260008152816020820152019061208a565b6040906108b793928152816020820152019061208a565b909291925a936128c230331461281d565b8151946040860151955a6113886060830151890101116129e2576108b7966000958051612909575b50505090612903915a9003608084015101943691610682565b91615047565b612938916129349161292f855173ffffffffffffffffffffffffffffffffffffffff1690565b615c12565b1590565b612944575b80806128ea565b61290392919450612953615c24565b908151612967575b5050600193909161293d565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20173ffffffffffffffffffffffffffffffffffffffff6020870151926129d860206129c6835173ffffffffffffffffffffffffffffffffffffffff1690565b9201519560405193849316968361289a565b0390a3388061295b565b7fdeaddead0000000000000000000000000000000000000000000000000000000060005260206000fd5b612a22612a1c6040830183611fc8565b90615c07565b90612a33612a1c6060830183611fc8565b90612ae9612a48612a1c610120840184611fc8565b60405194859360208501956101008201359260e08301359260c08101359260a08201359260808301359273ffffffffffffffffffffffffffffffffffffffff60208201359135168c9693909a9998959261012098959273ffffffffffffffffffffffffffffffffffffffff6101408a019d168952602089015260408801526060870152608086015260a085015260c084015260e08301526101008201520152565b0391612b1b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938481018352826105ab565b51902060408051602081019283523091810191909152466060820152608092830181529091612b4a90826105ab565b51902090565b604051906040820182811067ffffffffffffffff821117612b7b575b60405260006020838281520152565b612b83610505565b612b6c565b906014116103595790601490565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009035818116939260148110612bcb57505050565b60140360031b82901b16169150565b9060c060a06108b793805184526020810151602085015260408101511515604085015265ffffffffffff80606083015116606086015260808201511660808501520151918160a0820152019061208a565b9294612c8c61044d95612c7a610100959998612c68612c54602097610140808c528b0190612bda565b9b878a019060208091805184520151910152565b80516060890152602001516080880152565b805160a08701526020015160c0860152565b73ffffffffffffffffffffffffffffffffffffffff81511660e0850152015191019060208091805184520151910152565b612d0661044d94612cf4612cdf60a0959998969960e0865260e0860190612bda565b98602085019060208091805184520151910152565b80516060840152602001516080830152565b019060208091805184520151910152565b9081602091031261035957516108b781610422565b9160206108b7938181520191612028565b90612d6c73ffffffffffffffffffffffffffffffffffffffff916108b797959694606085526060850191612028565b941660208201526040818503910152612028565b60009060033d11612d8d57565b905060046000803e60005160e01c90565b600060443d106108b7576040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc91823d016004833e815167ffffffffffffffff918282113d602484011117612e1a57818401948551938411612e22573d85010160208487010111612e1a57506108b7929101602001906105ab565b949350505050565b50949350505050565b612e386040820182611fc8565b612e50612e448461256d565b93610120810190611fc8565b9290303b1561035957600093612e949160405196879586957f957122ab00000000000000000000000000000000000000000000000000000000875260048701612d3d565b0381305afa9081612f1d575b5061044d576001612eaf612d80565b6308c379a014612ec8575b612ec057565b61044d612183565b612ed0612d9e565b80612edc575b50612eba565b80516000925015612ed657610f21906040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301612882565b80610f48612f2a9261057b565b38612ea0565b9190612f3b9061317f565b73ffffffffffffffffffffffffffffffffffffffff929183166130da5761306c57612f659061317f565b9116612ffe57612f725750565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f6500000000000000000000000000000000000000000000000000000000000000608482015260a490fd5b610f21826040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601460408201527f41413334207369676e6174757265206572726f7200000000000000000000000060608201520190565b610f21836040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601760408201527f414132322065787069726564206f72206e6f742064756500000000000000000060608201520190565b610f21846040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601460408201527f41413234207369676e6174757265206572726f7200000000000000000000000060608201520190565b9291906131549061317f565b909273ffffffffffffffffffffffffffffffffffffffff808095169116036130da5761306c57612f65905b80156131d25761318e9061535f565b73ffffffffffffffffffffffffffffffffffffffff65ffffffffffff8060408401511642119081156131c2575b5091511691565b90506020830151164210386131bb565b50600090600090565b156131e257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b916000915a9381519061325382826136b3565b61325c81612a0c565b602084015261329a6effffffffffffffffffffffffffffff60808401516060850151176040850151176101008401359060e0850135171711156131db565b6132a382613775565b6132ae818584613836565b97906132df6129346132d4875173ffffffffffffffffffffffffffffffffffffffff1690565b60208801519061546c565b6133db576132ec43600052565b73ffffffffffffffffffffffffffffffffffffffff61332460a0606097015173ffffffffffffffffffffffffffffffffffffffff1690565b166133c1575b505a810360a0840135106133545760809360c092604087015260608601525a900391013501910152565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601e60408201527f41413430206f76657220766572696669636174696f6e4761734c696d6974000060608201520190565b909350816133d2929750858461455c565b9590923861332a565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601a60408201527f4141323520696e76616c6964206163636f756e74206e6f6e636500000000000060608201520190565b9290916000925a825161345b81846136b3565b61346483612a0c565b60208501526134a26effffffffffffffffffffffffffffff60808301516060840151176040840151176101008601359060e0870135171711156131db565b6134ab81613775565b6134b78186868b613ba2565b98906134e86129346134dd865173ffffffffffffffffffffffffffffffffffffffff1690565b60208701519061546c565b6135e0576134f543600052565b73ffffffffffffffffffffffffffffffffffffffff61352d60a0606096015173ffffffffffffffffffffffffffffffffffffffff1690565b166135c5575b505a840360a08601351061355f5750604085015260608401526080919060c0905a900391013501910152565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601e60448201527f41413430206f76657220766572696669636174696f6e4761734c696d697400006064820152608490fd5b909250816135d79298508686856147ef565b96909138613533565b610f21826040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601a60408201527f4141323520696e76616c6964206163636f756e74206e6f6e636500000000000060608201520190565b1561365557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b613725906136dd6136c38261256d565b73ffffffffffffffffffffffffffffffffffffffff168452565b602081013560208401526080810135604084015260a0810135606084015260c0810135608084015260e081013560c084015261010081013560e0840152610120810190611fc8565b90811561376a5761374f61124c6112468460a09461374a601461044d9998101561364e565b612b88565b73ffffffffffffffffffffffffffffffffffffffff16910152565b505060a06000910152565b60a081015173ffffffffffffffffffffffffffffffffffffffff16156137b75760c060035b60ff60408401519116606084015102016080830151019101510290565b60c0600161379a565b6137d86040929594939560608352606083019061262c565b9460208201520152565b9061044d602f60405180947f414132332072657665727465643a20000000000000000000000000000000000060208301526138268151809260208686019101612067565b810103600f8101855201836105ab565b916000926000925a936139046020835193613865855173ffffffffffffffffffffffffffffffffffffffff1690565b9561387d6138766040830183611fc8565b9084613e0d565b60a086015173ffffffffffffffffffffffffffffffffffffffff16906138a243600052565b85809373ffffffffffffffffffffffffffffffffffffffff809416159889613b3a575b60600151908601516040517f3a871cdd0000000000000000000000000000000000000000000000000000000081529788968795869390600485016137c0565b03938a1690f1829181613b1a575b50613b115750600190613923612d80565b6308c379a014613abd575b50613a50575b613941575b50505a900391565b61396b9073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b613986610a2c82546dffffffffffffffffffffffffffff1690565b8083116139e3576139dc926dffffffffffffffffffffffffffff9103166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b3880613939565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601760408201527f41413231206469646e2774207061792070726566756e6400000000000000000060608201520190565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601660408201527f4141323320726576657274656420286f72204f4f47290000000000000000000060608201520190565b613ac5612d9e565b9081613ad1575061392e565b610f2191613adf91506137e2565b6040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301612882565b95506139349050565b613b3391925060203d81116123385761232981836105ab565b9038613912565b9450613b80610a2c613b6c8c73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b546dffffffffffffffffffffffffffff1690565b8b811115613b975750856060835b969150506138c5565b606087918d03613b8e565b90926000936000935a94613beb6020835193613bd2855173ffffffffffffffffffffffffffffffffffffffff1690565b9561387d613be36040830183611fc8565b90848c61412b565b03938a1690f1829181613ded575b50613de45750600190613c0a612d80565b6308c379a014613d8e575b50613d20575b613c29575b5050505a900391565b613c539073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b91613c6f610a2c84546dffffffffffffffffffffffffffff1690565b90818311613cba575082547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000169190036dffffffffffffffffffffffffffff16179055388080613c20565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152608490fd5b610f21846040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601660408201527f4141323320726576657274656420286f72204f4f47290000000000000000000060608201520190565b613d96612d9e565b9081613da25750613c15565b8691613dae91506137e2565b90610f216040519283927f220266b60000000000000000000000000000000000000000000000000000000084526004840161289a565b9650613c1b9050565b613e0691925060203d81116123385761232981836105ab565b9038613bf9565b909180613e1957505050565b81515173ffffffffffffffffffffffffffffffffffffffff1692833b6140be57606083510151604051907f570e1a3600000000000000000000000000000000000000000000000000000000825260208280613e78878760048401612d2c565b0381600073ffffffffffffffffffffffffffffffffffffffff95867f00000000000000000000000000000000000000000000000000000000000000001690f19182156140b1575b600092614091575b508082169586156140245716809503613fb7573b15613f4a5761124c6112467fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d93613f1193612b88565b602083810151935160a001516040805173ffffffffffffffffffffffffffffffffffffffff9485168152939091169183019190915290a3565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152602060408201527f4141313520696e6974436f6465206d757374206372656174652073656e64657260608201520190565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152602060408201527f4141313420696e6974436f6465206d7573742072657475726e2073656e64657260608201520190565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601b60408201527f4141313320696e6974436f6465206661696c6564206f72204f4f47000000000060608201520190565b6140aa91925060203d811161146a5761145b81836105ab565b9038613ec7565b6140b9612183565b613ebf565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601f60408201527f414131302073656e64657220616c726561647920636f6e73747275637465640060608201520190565b9290918161413a575b50505050565b82515173ffffffffffffffffffffffffffffffffffffffff1693843b6143e257606084510151604051907f570e1a3600000000000000000000000000000000000000000000000000000000825260208280614199888860048401612d2c565b0381600073ffffffffffffffffffffffffffffffffffffffff95867f00000000000000000000000000000000000000000000000000000000000000001690f19182156143d5575b6000926143b5575b5080821696871561434757168096036142d9573b15614273575061124c6112467fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d9361423393612b88565b602083810151935160a001516040805173ffffffffffffffffffffffffffffffffffffffff9485168152939091169183019190915290a338808080614134565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152608490fd5b610f21826040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152602060408201527f4141313420696e6974436f6465206d7573742072657475726e2073656e64657260608201520190565b610f21846040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601b60408201527f4141313320696e6974436f6465206661696c6564206f72204f4f47000000000060608201520190565b6143ce91925060203d811161146a5761145b81836105ab565b90386141e8565b6143dd612183565b6141e0565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152608490fd5b1561444f57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4141343120746f6f206c6974746c6520766572696669636174696f6e476173006044820152fd5b919060408382031261035957825167ffffffffffffffff81116103595783019080601f83011215610359578151916144e483610639565b916144f260405193846105ab565b838352602084830101116103595760209261451291848085019101612067565b92015190565b9061044d602f60405180947f414133332072657665727465643a20000000000000000000000000000000000060208301526138268151809260208686019101612067565b93919260609460009460009380519261459b60a08a86015195614580888811614448565b015173ffffffffffffffffffffffffffffffffffffffff1690565b916145c68373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b946145e2610a2c87546dffffffffffffffffffffffffffff1690565b968588106147825773ffffffffffffffffffffffffffffffffffffffff60208a98946146588a966dffffffffffffffffffffffffffff8b6146919e03166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b015194604051998a98899788937ff465c77e000000000000000000000000000000000000000000000000000000008552600485016137c0565b0395169103f190818391849361475c575b506147555750506001906146b4612d80565b6308c379a014614733575b506146c657565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601660408201527f4141333320726576657274656420286f72204f4f47290000000000000000000060608201520190565b61473b612d9e565b908161474757506146bf565b610f2191613adf9150614518565b9450925050565b90925061477b91503d8085833e61477381836105ab565b8101906144ad565b91386146a2565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601e60408201527f41413331207061796d6173746572206465706f73697420746f6f206c6f77000060608201520190565b91949293909360609560009560009382519061481660a08b84015193614580848611614448565b936148418573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61485c610a2c82546dffffffffffffffffffffffffffff1690565b8781106149b7579273ffffffffffffffffffffffffffffffffffffffff60208a989693946146588a966dffffffffffffffffffffffffffff8d6148d69e9c9a03166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b0395169103f1908183918493614999575b506149915750506001906148f9612d80565b6308c379a014614972575b5061490c5750565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601660448201527f4141333320726576657274656420286f72204f4f4729000000000000000000006064820152608490fd5b61497a612d9e565b90816149865750614904565b613dae925050614518565b955093505050565b9092506149b091503d8085833e61477381836105ab565b91386148e7565b610f218a6040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601e60408201527f41413331207061796d6173746572206465706f73697420746f6f206c6f77000060608201520190565b60031115614a2f57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b929190614a7c6040916002865260606020870152606086019061208a565b930152565b939291906003811015614a2f57604091614a7c91865260606020870152606086019061208a565b9061044d603660405180947f4141353020706f73744f702072657665727465643a20000000000000000000006020830152614aec8151809260208686019101612067565b81010360168101855201836105ab565b929190925a93600091805191614b1183615318565b9260a0810195614b35875173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff93908481169081614ca457505050614b76825173ffffffffffffffffffffffffffffffffffffffff1690565b985b5a90030193840297604084019089825110614c37577f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f94614bc26020928c614c329551039061553a565b015194896020614c04614be9865173ffffffffffffffffffffffffffffffffffffffff1690565b9a5173ffffffffffffffffffffffffffffffffffffffff1690565b9401519785604051968796169a16988590949392606092608083019683521515602083015260408201520152565b0390a4565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152602060408201527f414135312070726566756e642062656c6f772061637475616c476173436f737460608201520190565b9a918051614cb4575b5050614b78565b6060850151600099509091803b15614ddb579189918983614d07956040518097819682957fa9a234090000000000000000000000000000000000000000000000000000000084528c029060048401614a5e565b0393f19081614dc8575b50614dc3576001614d20612d80565b6308c379a014614da4575b614d37575b3880614cad565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601260408201527f4141353020706f73744f7020726576657274000000000000000000000000000060608201520190565b614dac612d9e565b80614db75750614d2b565b613adf610f2191614aa8565b614d30565b80610f48614dd59261057b565b38614d11565b8980fd5b9392915a90600092805190614df382615318565b9360a0830196614e17885173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff95908681169081614f0d57505050614e58845173ffffffffffffffffffffffffffffffffffffffff1690565b915b5a9003019485029860408301908a825110614ea757507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f949392614bc2614c32938c60209451039061553a565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152602060448201527f414135312070726566756e642062656c6f772061637475616c476173436f73746064820152608490fd5b93918051614f1d575b5050614e5a565b606087015160009a509091803b1561504357918a918a83614f70956040518097819682957fa9a234090000000000000000000000000000000000000000000000000000000084528c029060048401614a5e565b0393f19081615030575b5061502b576001614f89612d80565b6308c379a01461500e575b614fa0575b3880614f16565b610f218b6040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601260408201527f4141353020706f73744f7020726576657274000000000000000000000000000060608201520190565b615016612d9e565b806150215750614f94565b613dae8d91614aa8565b614f99565b80610f4861503d9261057b565b38614f7a565b8a80fd5b909392915a9480519161505983615318565b9260a081019561507d875173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff938185169182615165575050506150bd825173ffffffffffffffffffffffffffffffffffffffff1690565b985b5a90030193840297604084019089825110614c37577f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f946151096020928c614c329551039061553a565b61511288614a25565b015194896020615139614be9865173ffffffffffffffffffffffffffffffffffffffff1690565b940151604080519182529815602082015297880152606087015290821695909116939081906080820190565b9a918151615175575b50506150bf565b8784026151818a614a25565b60028a1461520c576060860151823b15610359576151d493600080948d604051978896879586937fa9a2340900000000000000000000000000000000000000000000000000000000855260048501614a81565b0393f180156151ff575b6151ec575b505b388061516e565b80610f486151f99261057b565b386151e3565b615207612183565b6151de565b6060860151823b156103595761525793600080948d604051978896879586937fa9a2340900000000000000000000000000000000000000000000000000000000855260048501614a81565b0393f19081615305575b50615300576001615270612d80565b6308c379a0146152ed575b156151e5576040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601260408201527f4141353020706f73744f7020726576657274000000000000000000000000000060608201520190565b6152f5612d9e565b80614db7575061527b565b6151e5565b80610f486153129261057b565b38615261565b60e060c082015191015180821461533c57480180821015615337575090565b905090565b5090565b6040519061534d8261058f565b60006040838281528260208201520152565b615367615340565b5065ffffffffffff808260a01c1680156153b3575b604051926153898461058f565b73ffffffffffffffffffffffffffffffffffffffff8116845260d01c602084015216604082015290565b508061537c565b6153cf6153d5916153c9615340565b5061535f565b9161535f565b9073ffffffffffffffffffffffffffffffffffffffff9182825116928315615461575b65ffffffffffff928391826040816020850151169301511693836040816020840151169201511690808410615459575b50808511615451575b506040519561543f8761058f565b16855216602084015216604082015290565b935038615431565b925038615428565b8151811693506153f8565b73ffffffffffffffffffffffffffffffffffffffff16600052600160205267ffffffffffffffff6154c88260401c60406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b918254926154d584612491565b9055161490565b9073ffffffffffffffffffffffffffffffffffffffff6154fa612b50565b9216600052600060205263ffffffff600160406000206dffffffffffffffffffffffffffff815460781c1685520154166020830152565b61044d3361562b565b73ffffffffffffffffffffffffffffffffffffffff16600052600060205260406000206dffffffffffffffffffffffffffff8082541692830180931161561e575b8083116155c05761044d92166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f6465706f736974206f766572666c6f77000000000000000000000000000000006044820152fd5b615626612190565b61557b565b73ffffffffffffffffffffffffffffffffffffffff9061564b348261553a565b168060005260006020527f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c460206dffffffffffffffffffffffffffff60406000205416604051908152a2565b1561569e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b1561570357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b1561576857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b156157cd57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b9065ffffffffffff6080600161044d9461588b6dffffffffffffffffffffffffffff86511682906dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b602085015115156eff000000000000000000000000000082549160701b16807fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff83161783557fffffff000000000000000000000000000000ffffffffffffffffffffffffffff7cffffffffffffffffffffffffffff000000000000000000000000000000604089015160781b16921617178155019263ffffffff6060820151167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000008554161784550151167fffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffff69ffffffffffff0000000083549260201b169116179055565b1561599657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b91909165ffffffffffff808094169116019182116121cd57565b15615a1557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b15615a7a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b15615adf57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b15615b4457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b15615ba957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b816040519182372090565b9060009283809360208451940192f190565b3d610800808211615c4b575b50604051906020818301016040528082526000602083013e90565b905038615c3056fea2646970667358221220a706d8b02d7086d80e9330811f5af84b2614abdc5e9a1f2260126070a31d7cee64736f6c634300081100336080806040523461001657610210908161001c8239f35b600080fdfe6080604052600436101561001257600080fd5b6000803560e01c63570e1a361461002857600080fd5b346100c95760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100c95760043567ffffffffffffffff918282116100c957366023830112156100c95781600401359283116100c95736602484840101116100c9576100c561009e84602485016100fc565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390f35b80fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90806014116101bb5767ffffffffffffffff917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec82018381116101cd575b604051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81600b8701160116850190858210908211176101c0575b604052808452602084019036848401116101bb576020946000600c819682946014880187378301015251923560601c5af19060005191156101b557565b60009150565b600080fd5b6101c86100cc565b610178565b6101d56100cc565b61013a56fea26469706673582212201927e80b76ab9b71c952137dd676621a9fdf520c25928815636594036eb1c40364736f6c63430008110033";
-
-type EntryPointConstructorParams =
- | [signer?: Signer]
- | ConstructorParameters;
-
-const isSuperArgs = (
- xs: EntryPointConstructorParams
-): xs is ConstructorParameters => xs.length > 1;
-
-export class EntryPoint__factory extends ContractFactory {
- constructor(...args: EntryPointConstructorParams) {
- if (isSuperArgs(args)) {
- super(...args);
- } else {
- super(_abi, _bytecode, args[0]);
- }
- }
-
- override deploy(
- overrides?: Overrides & { from?: string }
- ): Promise {
- return super.deploy(overrides || {}) as Promise;
- }
- override getDeployTransaction(
- overrides?: Overrides & { from?: string }
- ): TransactionRequest {
- return super.getDeployTransaction(overrides || {});
- }
- override attach(address: string): EntryPoint {
- return super.attach(address) as EntryPoint;
- }
- override connect(signer: Signer): EntryPoint__factory {
- return super.connect(signer) as EntryPoint__factory;
- }
-
- static readonly bytecode = _bytecode;
+export class IEntryPoint__factory {
static readonly abi = _abi;
- static createInterface(): EntryPointInterface {
- return new utils.Interface(_abi) as EntryPointInterface;
+ static createInterface(): IEntryPointInterface {
+ return new utils.Interface(_abi) as IEntryPointInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
- ): EntryPoint {
- return new Contract(address, _abi, signerOrProvider) as EntryPoint;
+ ): IEntryPoint {
+ return new Contract(address, _abi, signerOrProvider) as IEntryPoint;
}
}
diff --git a/packages/types/src/executor/contracts/factories/IAccount__factory.ts b/packages/types/src/executor/contracts/factories/IAccount__factory.ts
index d039c550..2078b0db 100644
--- a/packages/types/src/executor/contracts/factories/IAccount__factory.ts
+++ b/packages/types/src/executor/contracts/factories/IAccount__factory.ts
@@ -3,8 +3,11 @@
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
-import { Provider } from "@ethersproject/providers";
-import type { IAccount, IAccountInterface } from "../IAccount";
+import type { Provider } from "@ethersproject/providers";
+import type {
+ IAccount,
+ IAccountInterface,
+} from "../IAccount";
const _abi = [
{
@@ -76,11 +79,6 @@ const _abi = [
name: "userOpHash",
type: "bytes32",
},
- {
- internalType: "address",
- name: "aggregator",
- type: "address",
- },
{
internalType: "uint256",
name: "missingAccountFunds",
@@ -91,14 +89,14 @@ const _abi = [
outputs: [
{
internalType: "uint256",
- name: "sigTimeRange",
+ name: "validationData",
type: "uint256",
},
],
stateMutability: "nonpayable",
type: "function",
},
-];
+] as const;
export class IAccount__factory {
static readonly abi = _abi;
diff --git a/packages/types/src/executor/contracts/factories/SenderCreator__factory.ts b/packages/types/src/executor/contracts/factories/SenderCreator__factory.ts
new file mode 100644
index 00000000..2cc0cb37
--- /dev/null
+++ b/packages/types/src/executor/contracts/factories/SenderCreator__factory.ts
@@ -0,0 +1,81 @@
+/* Autogenerated file. Do not edit manually. */
+/* tslint:disable */
+/* eslint-disable */
+import { Signer, utils, Contract, ContractFactory, Overrides } from "ethers";
+import type { Provider, TransactionRequest } from "@ethersproject/providers";
+import type {
+ SenderCreator,
+ SenderCreatorInterface,
+} from "../SenderCreator";
+
+const _abi = [
+ {
+ inputs: [
+ {
+ internalType: "bytes",
+ name: "initCode",
+ type: "bytes",
+ },
+ ],
+ name: "createSender",
+ outputs: [
+ {
+ internalType: "address",
+ name: "sender",
+ type: "address",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
+
+const _bytecode =
+ "0x608060405234801561001057600080fd5b50610213806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063570e1a3614610030575b600080fd5b61004361003e3660046100f9565b61006c565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b60008061007c601482858761016b565b61008591610195565b60601c90506000610099846014818861016b565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525084519495509360209350849250905082850182875af190506000519350806100f057600093505b50505092915050565b6000806020838503121561010c57600080fd5b823567ffffffffffffffff8082111561012457600080fd5b818501915085601f83011261013857600080fd5b81358181111561014757600080fd5b86602082850101111561015957600080fd5b60209290920196919550909350505050565b6000808585111561017b57600080fd5b8386111561018857600080fd5b5050820193919092039150565b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081358181169160148510156101d55780818660140360031b1b83161692505b50509291505056fea2646970667358221220bb601edbce3d32fb4f7fc26d1b5dbc04e0f66210945754b8dfafdc97fa3d759e64736f6c634300080f0033";
+
+type SenderCreatorConstructorParams =
+ | [signer?: Signer]
+ | ConstructorParameters;
+
+const isSuperArgs = (
+ xs: SenderCreatorConstructorParams
+): xs is ConstructorParameters => xs.length > 1;
+
+export class SenderCreator__factory extends ContractFactory {
+ constructor(...args: SenderCreatorConstructorParams) {
+ if (isSuperArgs(args)) {
+ super(...args);
+ } else {
+ super(_abi, _bytecode, args[0]);
+ }
+ }
+
+ override deploy(
+ overrides?: Overrides & { from?: string }
+ ): Promise {
+ return super.deploy(overrides || {}) as Promise;
+ }
+ override getDeployTransaction(
+ overrides?: Overrides & { from?: string }
+ ): TransactionRequest {
+ return super.getDeployTransaction(overrides || {});
+ }
+ override attach(address: string): SenderCreator {
+ return super.attach(address) as SenderCreator;
+ }
+ override connect(signer: Signer): SenderCreator__factory {
+ return super.connect(signer) as SenderCreator__factory;
+ }
+
+ static readonly bytecode = _bytecode;
+ static readonly abi = _abi;
+ static createInterface(): SenderCreatorInterface {
+ return new utils.Interface(_abi) as SenderCreatorInterface;
+ }
+ static connect(
+ address: string,
+ signerOrProvider: Signer | Provider
+ ): SenderCreator {
+ return new Contract(address, _abi, signerOrProvider) as SenderCreator;
+ }
+}
diff --git a/packages/types/src/executor/contracts/factories/index.ts b/packages/types/src/executor/contracts/factories/index.ts
index a8374ac4..2ab27c4b 100644
--- a/packages/types/src/executor/contracts/factories/index.ts
+++ b/packages/types/src/executor/contracts/factories/index.ts
@@ -1,7 +1,9 @@
/* eslint-disable camelcase */
-export { EntryPoint__factory } from "./EntryPoint__factory";
+export { IEntryPoint__factory } from "./EntryPoint__factory";
export { EtherspotAccountFactory__factory } from "./EtherspotAccountFactory__factory";
export { EtherspotAccount__factory } from "./EtherspotAccount__factory";
export { IAccount__factory } from "./IAccount__factory";
export { IAggregatedAccount__factory } from "./IAggregatedAccount__factory";
export { IAggregator__factory } from "./IAggregator__factory";
+export { IPaymaster__factory } from "./IPaymaster__factory";
+export { SenderCreator__factory } from "./SenderCreator__factory";
diff --git a/packages/types/src/executor/contracts/index.ts b/packages/types/src/executor/contracts/index.ts
index c5884102..4ae72259 100644
--- a/packages/types/src/executor/contracts/index.ts
+++ b/packages/types/src/executor/contracts/index.ts
@@ -1,7 +1,8 @@
-export { EntryPoint } from "./EntryPoint";
+export { IEntryPoint } from "./EntryPoint";
export { EtherspotAccount } from "./EtherspotAccount";
export { IAccount } from "./IAccount";
export { IAggregatedAccount } from "./IAggregatedAccount";
export { IAggregator } from "./IAggregator";
export { IPaymaster } from "./IPaymaster";
+export { SenderCreator } from "./SenderCreator";
export * from "./factories";
diff --git a/packages/types/src/executor/index.ts b/packages/types/src/executor/index.ts
index 0e6bb4d7..2041c8cf 100644
--- a/packages/types/src/executor/index.ts
+++ b/packages/types/src/executor/index.ts
@@ -8,3 +8,5 @@ export enum ReputationStatus {
throttled = "throttled",
banned = "banned",
}
+
+export * from "./validation";
diff --git a/packages/types/src/executor/validation/index.ts b/packages/types/src/executor/validation/index.ts
new file mode 100644
index 00000000..bb586d80
--- /dev/null
+++ b/packages/types/src/executor/validation/index.ts
@@ -0,0 +1,61 @@
+import { BigNumberish } from "ethers";
+
+export interface BundlerCollectorReturn {
+ callsFromEntryPoint: TopLevelCallInfo[];
+ keccak: string[];
+ calls: Array;
+ logs: LogInfo[];
+ debug: any[];
+}
+
+export interface MethodInfo {
+ type: string;
+ from: string;
+ to: string;
+ method: string;
+ value: any;
+ gas: number;
+}
+
+export interface ExitInfo {
+ type: "REVERT" | "RETURN";
+ gasUsed: number;
+ data: string;
+}
+
+export interface TopLevelCallInfo {
+ topLevelMethodSig: string;
+ topLevelTargetAddress: string;
+ opcodes: { [opcode: string]: number };
+ access: { [address: string]: AccessInfo };
+ contractSize: { [addr: string]: ContractSizeInfo };
+ extCodeAccessInfo: { [addr: string]: string };
+ oog?: boolean;
+}
+
+export interface ContractSizeInfo {
+ opcode: string;
+ contractSize: number;
+}
+
+export interface AccessInfo {
+ // slot value, just prior this operation
+ reads: { [slot: string]: string };
+ // count of writes.
+ writes: { [slot: string]: number };
+}
+
+export interface LogInfo {
+ topics: string[];
+ data: string;
+}
+
+export interface CallEntry {
+ to: string;
+ from: string;
+ type: string; // call opcode
+ method: string; // parsed method, or signash if unparsed
+ revert?: any; // parsed output from REVERT
+ return?: any; // parsed method output.
+ value?: BigNumberish;
+}
diff --git a/packages/types/src/networks/networks.ts b/packages/types/src/networks/networks.ts
index d06ab560..50e2cde9 100644
--- a/packages/types/src/networks/networks.ts
+++ b/packages/types/src/networks/networks.ts
@@ -26,12 +26,23 @@ export type NetworkName =
| "neonDevnet"
| "optimismGoerli"
| "dev"
+ | "base"
| "baseGoerli"
| "sepolia"
| "chiado"
| "polygonzkevm"
| "mantle"
- | "mantleTestnet";
+ | "mantleTestnet"
+ | "linea"
+ | "lineaTestnet"
+ | "scroll"
+ | "scrollSepolia"
+ | "scrollAlpha"
+ | "flare"
+ | "flareCoston"
+ | "flareCoston2"
+ | "bifrost"
+ | "bifrostTestnet";
export const networkNames: NetworkName[] = [
"mainnet",
@@ -61,12 +72,23 @@ export const networkNames: NetworkName[] = [
"neonDevnet",
"optimismGoerli",
"dev",
+ "base",
"baseGoerli",
"sepolia",
"chiado",
"polygonzkevm",
"mantle",
"mantleTestnet",
+ "linea",
+ "lineaTestnet",
+ "scroll",
+ "scrollSepolia",
+ "scrollAlpha",
+ "flare",
+ "flareCoston",
+ "flareCoston2",
+ "bifrost",
+ "bifrostTestnet",
];
export const NETWORK_NAME_TO_CHAIN_ID: {
@@ -99,12 +121,23 @@ export const NETWORK_NAME_TO_CHAIN_ID: {
neonDevnet: 245022926,
optimismGoerli: 420,
dev: 1337,
+ base: 8453,
baseGoerli: 84531,
sepolia: 11155111,
chiado: 10200,
polygonzkevm: 1442,
mantle: 5000,
mantleTestnet: 5001,
+ linea: 59144,
+ lineaTestnet: 59140,
+ scroll: 534352,
+ scrollSepolia: 534351,
+ scrollAlpha: 534353,
+ flare: 14,
+ flareCoston: 16,
+ flareCoston2: 114,
+ bifrost: 3068,
+ bifrostTestnet: 49088,
};
export const CHAIN_ID_TO_NETWORK_NAME = Object.fromEntries(
diff --git a/skandha b/skandha
index 0f47a32e..579dc5b4 100755
--- a/skandha
+++ b/skandha
@@ -2,6 +2,6 @@
# Convenience script to run the etherspot binary from built source
#
-# ./skandha --network goerli
+# ./skandha --redirectRpc
node --experimental-specifier-resolution=node ./packages/cli/bin/skandha.js "$@"
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index ebb7ff7e..5664c671 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -628,7 +628,7 @@
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
-"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0":
+"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.6.1", "@ethersproject/abstract-provider@^5.7.0":
version "5.7.0"
resolved "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz"
integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==
@@ -641,7 +641,7 @@
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/web" "^5.7.0"
-"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0":
+"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.6.2", "@ethersproject/abstract-signer@^5.7.0":
version "5.7.0"
resolved "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz"
integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==
@@ -797,14 +797,14 @@
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/sha2" "^5.7.0"
-"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0":
+"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.6.0", "@ethersproject/properties@^5.7.0":
version "5.7.0"
resolved "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz"
integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==
dependencies:
"@ethersproject/logger" "^5.7.0"
-"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.0":
+"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.6.8", "@ethersproject/providers@^5.7.0":
version "5.7.2"
resolved "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz"
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
@@ -888,7 +888,7 @@
"@ethersproject/constants" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
-"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0":
+"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0":
version "5.7.0"
resolved "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz"
integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==
@@ -933,7 +933,7 @@
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/wordlists" "^5.7.0"
-"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0":
+"@ethersproject/web@5.7.1", "@ethersproject/web@^5.6.1", "@ethersproject/web@^5.7.0":
version "5.7.1"
resolved "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz"
integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==
@@ -2476,6 +2476,40 @@
private-ip "^3.0.0"
uint8arraylist "^2.3.2"
+"@mantleio/contracts@0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@mantleio/contracts/-/contracts-0.2.1.tgz#0a9810c28a63ae86e267bcced88ab07c13dce5fb"
+ integrity sha512-NqWUO8Vhu2OVA+pH+k761uWwssQmKsSBRA1vO9wrKUvlHsWN50DL/wrIDHZBksDwcjqOFGeIxWmt9QLwndhAjw==
+ dependencies:
+ "@ethersproject/abstract-provider" "^5.6.1"
+ "@ethersproject/abstract-signer" "^5.6.2"
+ "@mantleio/core-utils" "0.1.0"
+
+"@mantleio/core-utils@0.1.0":
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/@mantleio/core-utils/-/core-utils-0.1.0.tgz#8f6f93e930b364eddf201bfe9a5d648943e8b7f9"
+ integrity sha512-6v/CuKe8W3UVYVn6RUR6pbrMbKDcdrUoj24wCuwM4JFbPHzpTqUSnSXDHrDs3LJpWLxsptynNdIGw90wzCKpdg==
+ dependencies:
+ "@ethersproject/abstract-provider" "^5.6.1"
+ "@ethersproject/properties" "^5.6.0"
+ "@ethersproject/providers" "^5.6.8"
+ "@ethersproject/transactions" "^5.6.2"
+ "@ethersproject/web" "^5.6.1"
+ bufio "^1.0.7"
+ chai "^4.3.4"
+ ethers "^5.6.8"
+
+"@mantleio/sdk@0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@mantleio/sdk/-/sdk-0.2.1.tgz#b1f1331cfc166ff5fbe4da565078952eb4611c52"
+ integrity sha512-bPC3eHEsRHSPAVv4EGC6lY43btiMaEK0e39yArz9LBtub8EkxyokDnD+XLdaeQrL8flucTH5DRcZAanqn9bq4g==
+ dependencies:
+ "@mantleio/contracts" "0.2.1"
+ "@mantleio/core-utils" "0.1.0"
+ lodash "^4.17.21"
+ merkletreejs "^0.2.27"
+ rlp "^2.2.7"
+
"@multiformats/mafmt@^11.0.2", "@multiformats/mafmt@^11.0.3":
version "11.1.2"
resolved "https://registry.npmjs.org/@multiformats/mafmt/-/mafmt-11.1.2.tgz"
@@ -3365,7 +3399,7 @@
"@types/json5@^0.0.29":
version "0.0.29"
- resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
+ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/long@^4.0.1":
@@ -5492,7 +5526,7 @@ ethereumjs-util@^7.1.0:
ethereum-cryptography "^0.1.3"
rlp "^2.2.4"
-ethers@5.7.2, ethers@^5.1.0, ethers@^5.7.0:
+ethers@5.7.2, ethers@^5.1.0, ethers@^5.6.8, ethers@^5.7.0:
version "5.7.2"
resolved "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==