Skip to content

Commit ef5041c

Browse files
authored
Merge pull request #28 from etherspot/redirect-eth-rpc-calls
feat: redirect eth rpc calls
2 parents 3ebf621 + 343054c commit ef5041c

13 files changed

Lines changed: 159 additions & 49 deletions

File tree

README.md

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,7 @@
2121
2. build `yarn build && yarn bootstrap`
2222
3. `cp config.json.default config.json`
2323
4. edit `config.json`
24-
5. (optional) run local geth-node
25-
```bash
26-
docker run --rm -ti --name geth -p 8545:8545 ethereum/client-go:v1.10.26 \
27-
--miner.gaslimit 12000000 \
28-
--http --http.api personal,eth,net,web3,debug \
29-
--http.vhosts '*,localhost,host.docker.internal' --http.addr "0.0.0.0" \
30-
--ignore-legacy-receipts --allow-insecure-unlock --rpc.allow-unprotected-txs \
31-
--dev \
32-
--nodiscover --maxpeers 0 \
33-
--networkid 1337
34-
```
24+
5. (optional) run local geth-node from `test/geth-dev`
3525
6. run `./skandha`
3626
7. Skandha will run for all chains available in `config.json`
3727
8. Networks will be available at `http://localhost:14337/{chainId}/` (e.g. for dev `http://localhost:14337/1337/`)
@@ -44,28 +34,15 @@ docker run --rm -ti --name geth -p 8545:8545 ethereum/client-go:v1.10.26 \
4434
4. `docker run --mount type=bind,source="$(pwd)"/config.json,target=/usr/app/config.json,readonly -dp 14337:14337 etherspot/skandha start`
4535

4636

47-
### RPC Methods Checklist
48-
49-
- [x] eth_chainId
50-
- [x] eth_supportedEntryPoints
51-
- [x] eth_sendUserOperation
52-
- [x] eth_estimateUserOperationGas
53-
- [x] eth_getUserOperationReceipt
54-
- [x] eth_getUserOperationByHash
55-
- [x] web3_clientVersion
56-
- [x] debug_bundler_clearState
57-
- [x] debug_bundler_dumpMempool
58-
- [x] debug_bundler_setReputation
59-
- [x] debug_bundler_dumpReputation
60-
- [x] debug_bundler_setBundlingMode
61-
- [x] debug_bundler_setBundleInterval
62-
- [x] debug_bundler_sendBundleNow
63-
64-
### Additional features
65-
- [x] Unsafe mode - bypass opcode & stake validation (Enabled by --unsafeMode)
37+
## Additional features
38+
- [x] Unsafe mode - bypass opcode & stake validation
39+
- [x] Redirect RPC - Redirect ETH rpc calls to the underlying execution client. This is needed if you use UserOp.js
6640

41+
### CLI Options
42+
- `--unsafeMode` - enables unsafeMode
43+
- `--redirectRpc` - enables redirecting eth rpc calls
6744

68-
### Relayer Configuration
45+
## Relayer Configuration
6946

7047
#### Config.json
7148

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"name": "root",
33
"private": true,
4-
"version": "0.0.2",
4+
"version": "0.0.3",
55
"engines": {
66
"node": ">=12.9.0"
77
},
88
"scripts": {
99
"clean": "rm -rf ./packages/*/lib ./packages/*/*.tsbuildinfo",
1010
"bootstrap": "lerna bootstrap & lerna link",
11-
"build": "yarn workspace db run build & lerna run build",
11+
"build": "yarn workspace types run build && yarn workspace db run build & lerna run build",
1212
"lint": "eslint --color --ext .ts packages/*/src/",
1313
"fix-lint": "eslint --ext .ts --fix packages/*/src/",
1414
"test:unit": "lerna run test:unit --no-bail --concurrency 1",

packages/api/src/app.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ import RpcError from "types/lib/api/errors/rpc-error";
66
import * as RpcErrorCodes from "types/lib/api/errors/rpc-error-codes";
77
import { FastifyInstance, RouteHandler } from "fastify";
88
import logger from "./logger";
9-
import { BundlerRPCMethods, CustomRPCMethods } from "./constants";
10-
import { EthAPI, DebugAPI, Web3API } from "./modules";
9+
import {
10+
BundlerRPCMethods,
11+
CustomRPCMethods,
12+
RedirectedRPCMethods,
13+
} from "./constants";
14+
import { EthAPI, DebugAPI, Web3API, RedirectAPI } from "./modules";
1115
import { deepHexlify } from "./utils";
1216

1317
export interface RpcHandlerOptions {
@@ -21,6 +25,7 @@ export interface EtherspotBundlerOptions {
2125
config: Config;
2226
db: IDbController;
2327
testingMode: boolean;
28+
redirectRpc: boolean;
2429
}
2530

2631
export interface RelayerAPI {
@@ -37,12 +42,14 @@ export class ApiApp {
3742
private relayers: RelayerAPI[] = [];
3843

3944
private testingMode = false;
45+
private redirectRpc = false;
4046

4147
constructor(options: EtherspotBundlerOptions) {
4248
this.server = options.server;
4349
this.config = options.config;
4450
this.db = options.db;
4551
this.testingMode = options.testingMode;
52+
this.redirectRpc = options.redirectRpc;
4653
this.setupRoutes();
4754
}
4855

@@ -74,6 +81,7 @@ export class ApiApp {
7481
const ethApi = new EthAPI(relayer.eth);
7582
const debugApi = new DebugAPI(relayer.debug);
7683
const web3Api = new Web3API(relayer.web3);
84+
const redirectApi = new RedirectAPI(network, this.config);
7785

7886
this.relayers.push({
7987
relayer,
@@ -120,6 +128,15 @@ export class ApiApp {
120128
}
121129
}
122130

131+
if (this.redirectRpc && method in RedirectedRPCMethods) {
132+
const body = await redirectApi.redirect(method, params);
133+
return res.status(200).send({
134+
jsonrpc,
135+
id,
136+
...body,
137+
});
138+
}
139+
123140
if (result === undefined) {
124141
switch (method) {
125142
case BundlerRPCMethods.eth_supportedEntryPoints:

packages/api/src/constants.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,42 @@ export const BundlerRPCMethods = {
1818
debug_bundler_setBundleInterval: "debug_bundler_setBundleInterval",
1919
debug_bundler_sendBundleNow: "debug_bundler_sendBundleNow",
2020
};
21+
22+
export const RedirectedRPCMethods = {
23+
web3_sha3: "web3_sha3",
24+
net_version: "net_version",
25+
net_listening: "net_listening",
26+
net_peerCount: "net_peerCount",
27+
eth_protocolVersion: "eth_protocolVersion",
28+
eth_gasPrice: "eth_gasPrice",
29+
eth_blockNumber: "eth_blockNumber",
30+
eth_getBalance: "eth_getBalance",
31+
eth_getStorageAt: "eth_getStorageAt",
32+
eth_getTransactionCount: "eth_getTransactionCount",
33+
eth_getBlockTransactionCountByHash: "eth_getBlockTransactionCountByHash",
34+
eth_getBlockTransactionCountByNumber: "eth_getBlockTransactionCountByNumber",
35+
eth_getUncleCountByBlockHash: "eth_getUncleCountByBlockHash",
36+
eth_getUncleCountByBlockNumber: "eth_getUncleCountByBlockNumber",
37+
eth_getCode: "eth_getCode",
38+
eth_sign: "eth_sign",
39+
eth_call: "eth_call",
40+
eth_estimateGas: "eth_estimateGas",
41+
eth_getBlockByHash: "eth_getBlockByHash",
42+
eth_getBlockByNumber: "eth_getBlockByNumber",
43+
eth_getTransactionByHash: "eth_getTransactionByHash",
44+
eth_getTransactionByBlockHashAndIndex:
45+
"eth_getTransactionByBlockHashAndIndex",
46+
eth_getTransactionByBlockNumberAndIndex:
47+
"eth_getTransactionByBlockNumberAndIndex",
48+
eth_getTransactionReceipt: "eth_getTransactionReceipt",
49+
eth_getUncleByBlockHashAndIndex: "eth_getUncleByBlockHashAndIndex",
50+
eth_getUncleByBlockNumberAndIndex: "eth_getUncleByBlockNumberAndIndex",
51+
eth_newFilter: "eth_newFilter",
52+
eth_newBlockFilter: "eth_newBlockFilter",
53+
eth_newPendingTransactionFilter: "eth_newPendingTransactionFilter",
54+
eth_uninstallFilter: "eth_uninstallFilter",
55+
eth_getFilterChanges: "eth_getFilterChanges",
56+
eth_getFilterLogs: "eth_getFilterLogs",
57+
eth_getLogs: "eth_getLogs",
58+
eth_maxPriorityFeePerGas: "eth_maxPriorityFeePerGas",
59+
};

packages/api/src/modules/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./debug";
22
export * from "./web3";
33
export * from "./eth";
4+
export * from "./redirect";
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { providers } from "ethers";
2+
import { Config } from "executor/lib/config";
3+
import { NetworkName } from "types/src";
4+
5+
export class RedirectAPI {
6+
private provider: providers.JsonRpcProvider;
7+
8+
constructor(private network: NetworkName, private config: Config) {
9+
this.provider = this.config.getNetworkProvider(
10+
this.network
11+
) as providers.JsonRpcProvider;
12+
}
13+
14+
async redirect(method: string, params: any[]): Promise<any> {
15+
return await this.provider
16+
.send(method, params)
17+
.then((result) => ({ result }))
18+
.catch((err: any) => {
19+
if (err.body) {
20+
try {
21+
const body = JSON.parse(err.body);
22+
return body;
23+
// eslint-disable-next-line no-empty
24+
} catch (err) {}
25+
}
26+
throw err;
27+
});
28+
}
29+
}

packages/cli/src/cmds/start/handler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { IBundlerArgs } from "./index";
1818
export async function bundlerHandler(
1919
args: IBundlerArgs & IGlobalArgs
2020
): Promise<void> {
21-
const { dataDir, networksFile, testingMode, unsafeMode } = args;
21+
const { dataDir, networksFile, testingMode, unsafeMode, redirectRpc } = args;
2222

2323
let config: Config;
2424
try {
@@ -65,6 +65,7 @@ export async function bundlerHandler(
6565
config: config,
6666
db,
6767
testingMode,
68+
redirectRpc,
6869
});
6970

7071
await server.listen();

packages/cli/src/cmds/start/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { bundlerHandler } from "./handler";
55
export interface IBundlerArgs {
66
testingMode: boolean;
77
unsafeMode: boolean;
8+
redirectRpc: boolean;
89
}
910

1011
export const bundlerOptions = {
@@ -20,6 +21,13 @@ export const bundlerOptions = {
2021
default: false,
2122
choices: [true, false],
2223
},
24+
redirectRpc: {
25+
description:
26+
"Redirect ETH-related rpc calls to the underlying execution client",
27+
type: "boolean",
28+
default: false,
29+
choices: [true, false],
30+
},
2331
};
2432

2533
export const start: ICliCommand<IBundlerArgs, IGlobalArgs> = {

packages/executor/src/modules/eth.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,18 @@ export class Eth {
8686
preVerificationGas: 0,
8787
verificationGasLimit: 10e6,
8888
};
89+
const preVerificationGas = this.calcPreVerificationGas(userOp);
90+
userOpComplemented.preVerificationGas = preVerificationGas;
91+
8992
const { returnInfo } =
90-
await this.userOpValidationService.simulateValidation(
93+
await this.userOpValidationService.validateForEstimation(
9194
userOpComplemented,
9295
entryPoint
9396
);
97+
98+
// eslint-disable-next-line prefer-const
99+
let { preOpGas, validAfter, validUntil } = returnInfo;
100+
94101
const callGasLimit = await this.provider
95102
.estimateGas({
96103
from: entryPoint,
@@ -103,17 +110,22 @@ export class Eth {
103110
err.message.match(/reason="(.*?)"/)?.at(1) ?? "execution reverted";
104111
throw new RpcError(message, RpcErrorCodes.EXECUTION_REVERTED);
105112
});
106-
const preVerificationGas = this.calcPreVerificationGas(userOp);
107-
const verificationGas = BigNumber.from(returnInfo.preOpGas).toNumber();
108-
let deadline: any = undefined;
109-
if (returnInfo.deadline) {
110-
deadline = BigNumber.from(returnInfo.deadline);
113+
// const preVerificationGas = this.calcPreVerificationGas(userOp);
114+
const verificationGas = BigNumber.from(preOpGas).toNumber();
115+
validAfter = BigNumber.from(validAfter);
116+
validUntil = BigNumber.from(validUntil);
117+
if (validUntil === BigNumber.from(0)) {
118+
validUntil = undefined;
119+
}
120+
if (validAfter === BigNumber.from(0)) {
121+
validAfter = undefined;
111122
}
112123
return {
113124
preVerificationGas,
114125
verificationGas,
126+
validAfter,
127+
validUntil,
115128
callGasLimit,
116-
deadline: deadline,
117129
};
118130
}
119131

@@ -292,14 +304,15 @@ export class Eth {
292304
} as any;
293305

294306
const packed = arrayify(packUserOp(p, false));
307+
const lengthInWord = (packed.length + 31) / 32;
295308
const callDataCost = packed
296309
.map((x) => (x === 0 ? ov.zeroByte : ov.nonZeroByte))
297310
.reduce((sum, x) => sum + x);
298311
const ret = Math.round(
299312
callDataCost +
300313
ov.fixed / ov.bundleSize +
301314
ov.perUserOp +
302-
ov.perUserOpWord * packed.length
315+
ov.perUserOpWord * lengthInWord
303316
);
304317
return ret;
305318
}

packages/executor/src/modules/web3.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ export class Web3 {
33
constructor(private config: Config) {}
44

55
clientVersion(): string {
6-
return `skandha/${this.config.unsafeMode ? "unsafe/" : ""}0.0.1`; // TODO: get version based on commit hash
6+
return `skandha/${this.config.unsafeMode ? "unsafe/" : ""}0.0.3`; // TODO: get version based on commit hash
77
}
88
}

0 commit comments

Comments
 (0)