Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: EntryPoint v7 support #145

Merged
merged 14 commits into from
Feb 28, 2024
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ Or follow the steps below:
- `--unsafeMode` - enables unsafeMode
- `--redirectRpc` - enables redirecting eth rpc calls
- `--executor.bundlingMode manual|auto` - sets bundling mode to `manual` or `auto` on start. Default value is `auto`
- `--metrics.enable false|true` - enable Prometheus metrics (default - `false`)
- `--metrics.host` - metrics host (default - `127.0.0.1`)
- `--metrics.port` - metrics port (default - `8008`)

## 🔑 Relayer Configuration

Expand Down
48 changes: 36 additions & 12 deletions packages/api/src/dto/EstimateUserOperation.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ import {
IsDefined,
IsEthereumAddress,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator";
import { BigNumberish, BytesLike } from "ethers";
import { Type } from "class-transformer";
import { IsBigNumber } from "../utils/is-bignumber";
import { IsCallData } from "../utils/IsCallCode";

export class EstimateUserOperationStruct {
export class EstimateUserOperation {
/**
* Common Properties
*/
@IsEthereumAddress()
sender!: string;
@IsBigNumber()
nonce!: BigNumberish;
@IsString()
@IsCallData()
initCode!: BytesLike;
@IsString()
callData!: BytesLike;
@IsBigNumber()
callGasLimit?: BigNumberish;
@IsBigNumber()
verificationGasLimit?: BigNumberish;
@IsBigNumber()
Expand All @@ -29,20 +29,44 @@ export class EstimateUserOperationStruct {
@IsBigNumber()
maxPriorityFeePerGas?: BigNumberish;
@IsString()
@IsCallData()
paymasterAndData?: BytesLike;
callData!: BytesLike;
@IsString()
signature!: BytesLike;

/**
* EntryPoint v7 Properties
*/
@IsEthereumAddress()
@IsOptional()
factory?: string;

@IsString()
@IsOptional()
factoryData?: BytesLike;

@IsEthereumAddress()
@IsOptional()
paymaster?: string;

@IsBigNumber()
callGasLimit!: BigNumberish;
@IsOptional()
paymasterVerificationGasLimit?: BigNumberish;

@IsBigNumber()
@IsOptional()
paymasterPostOpGasLimit?: BigNumberish;

@IsString()
@IsOptional()
paymasterData?: BytesLike;
}

export class EstimateUserOperationGasArgs {
@IsDefined()
@IsObject()
@ValidateNested()
@Type(() => EstimateUserOperationStruct)
userOp!: EstimateUserOperationStruct;
@Type(() => EstimateUserOperation)
userOp!: EstimateUserOperation;

@IsEthereumAddress()
entryPoint!: string;
Expand Down
48 changes: 36 additions & 12 deletions packages/api/src/dto/SendUserOperation.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ import {
IsDefined,
IsEthereumAddress,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator";
import { BigNumberish, BytesLike } from "ethers";
import { Type } from "class-transformer";
import { IsBigNumber } from "../utils/is-bignumber";
import { IsCallData } from "../utils/IsCallCode";

export class SendUserOperationStruct {
export class SendUserOperation {
/**
* Common Properties
*/
@IsEthereumAddress()
sender!: string;
@IsBigNumber()
nonce!: BigNumberish;
@IsString()
@IsCallData()
initCode!: BytesLike;
@IsString()
callData!: BytesLike;
@IsBigNumber()
callGasLimit!: BigNumberish;
@IsBigNumber()
verificationGasLimit!: BigNumberish;
@IsBigNumber()
Expand All @@ -29,20 +29,44 @@ export class SendUserOperationStruct {
@IsBigNumber()
maxPriorityFeePerGas!: BigNumberish;
@IsString()
@IsCallData()
paymasterAndData!: BytesLike;
callData!: BytesLike;
@IsString()
signature!: BytesLike;

/**
* EntryPoint v7 Properties
*/
@IsEthereumAddress()
@IsOptional()
factory?: string;

@IsString()
@IsOptional()
factoryData?: BytesLike;

@IsEthereumAddress()
@IsOptional()
paymaster?: string;

@IsBigNumber()
callGasLimit!: BigNumberish;
@IsOptional()
paymasterVerificationGasLimit?: BigNumberish;

@IsBigNumber()
@IsOptional()
paymasterPostOpGasLimit?: BigNumberish;

@IsString()
@IsOptional()
paymasterData?: BytesLike;
}

export class SendUserOperationGasArgs {
@IsDefined()
@IsObject()
@ValidateNested()
@Type(() => SendUserOperationStruct)
userOp!: SendUserOperationStruct;
@Type(() => SendUserOperation)
userOp!: SendUserOperation;

@IsEthereumAddress()
entryPoint!: string;
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/dto/SetMempool.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import {
IsEthereumAddress,
ValidateNested,
} from "class-validator";
import { SendUserOperationStruct } from "./SendUserOperation.dto";
import { SendUserOperation } from "./SendUserOperation.dto";

export class SetMempoolArgs {
@IsDefined()
@IsArray()
@ValidateNested()
userOps!: SendUserOperationStruct[];
userOps!: SendUserOperation[];

@IsEthereumAddress()
entryPoint!: string;
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/modules/debug.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UserOperationStruct } from "types/lib/executor/contracts/EntryPoint";
import { UserOperation } from "types/lib/contracts/UserOperation";
import { Debug } from "executor/lib/modules";
import { IsEthereumAddress } from "class-validator";
import { BundlingMode } from "types/lib/api/interfaces";
Expand Down Expand Up @@ -49,7 +49,7 @@ export class DebugAPI {
* Dumps the current UserOperations mempool
* array - Array of UserOperations currently in the mempool
*/
async dumpMempool(): Promise<UserOperationStruct[]> {
async dumpMempool(): Promise<UserOperation[]> {
return await this.debugModule.dumpMempool();
}

Expand Down
6 changes: 5 additions & 1 deletion packages/api/src/utils/RpcMethodValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ export function validationFactory<T>(
arguments: args[0],
},
});
throw new RpcError("Invalid Request", RpcErrorCodes.INVALID_REQUEST);
throw new RpcError("Invalid Request", RpcErrorCodes.INVALID_REQUEST, {
data: {
errors,
},
});
}

return method.apply(this, args);
Expand Down
120 changes: 61 additions & 59 deletions packages/cli/src/cmds/node/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,76 +12,78 @@ import { getVersionData } from "../../util/version";
import { initPeerIdAndEnr } from "./initPeerIdAndEnr";

export async function nodeHandler(args: IGlobalArgs): Promise<void> {
const params = await getNodeConfigFromArgs(args);

//create the necessary directories
mkdir(params.dataDir);

logger.info(" ___ ___ ");
logger.info(" (o o) (o o) ");
logger.info("( V ) Skandha - A modular typescript bundler ( V )");
logger.info("--m-m--------------------------------------------m-m--");
logger.info("P2P is not yet available for EntryPoint v7. Stay tuned");
return;

logger.info(`Using the configFile from ${params.configFile}`);
logger.info(`Initialised the dataDir at ${params.dataDir}`);
logger.info("Boot ENR: " + params.p2p["bootEnrs"].length);
// const params = await getNodeConfigFromArgs(args);

let config: Config;
try {
const networkConfig = readFile(params.configFile) as NetworkConfig;
config = await Config.init({
config: networkConfig,
testingMode: params.testingMode,
unsafeMode: params.unsafeMode,
redirectRpc: params.redirectRpc,
});
} catch (err) {
if (err instanceof Error && err.message.indexOf("chain id") > -1) {
logger.error(err.message);
return;
}
logger.info("Config file not found. Proceeding with env vars...");
config = await Config.init({
config: null,
testingMode: params.testingMode,
unsafeMode: params.unsafeMode,
redirectRpc: params.redirectRpc,
});
}
// //create the necessary directories
// mkdir(params.dataDir);

const db = new RocksDbController(
params.dataDir,
getNamespaceByValue(Namespace.userOps)
);
await db.start();
// logger.info(`Using the configFile from ${params.configFile}`);
// logger.info(`Initialised the dataDir at ${params.dataDir}`);
// logger.info("Boot ENR: " + params.p2p["bootEnrs"].length);

const { enr, peerId } = await initPeerIdAndEnr(args, logger);
// let config: Config;
// try {
// const networkConfig = readFile(params.configFile) as NetworkConfig;
// config = await Config.init({
// config: networkConfig,
// testingMode: params.testingMode,
// unsafeMode: params.unsafeMode,
// redirectRpc: params.redirectRpc,
// });
// } catch (err) {
// if (err instanceof Error && err.message.indexOf("chain id") > -1) {
// logger.error(err.message);
// return;
// }
// logger.info("Config file not found. Proceeding with env vars...");
// config = await Config.init({
// config: null,
// testingMode: params.testingMode,
// unsafeMode: params.unsafeMode,
// redirectRpc: params.redirectRpc,
// });
// }

const options: IBundlerNodeOptions = {
...defaultOptions,
api: {
port: params.api["port"],
address: params.api["address"],
cors: params.api["cors"],
enableRequestLogging: params.api["enableRequestLogging"],
},
network: initNetworkOptions(enr, params.p2p, params.dataDir),
};
// const db = new RocksDbController(
// params.dataDir,
// getNamespaceByValue(Namespace.userOps)
// );
// await db.start();

// const { enr, peerId } = await initPeerIdAndEnr(args, logger);

// const options: IBundlerNodeOptions = {
// ...defaultOptions,
// api: {
// port: params.api["port"],
// address: params.api["address"],
// cors: params.api["cors"],
// enableRequestLogging: params.api["enableRequestLogging"],
// },
// network: initNetworkOptions(enr, params.p2p, params.dataDir),
// };

const version = getVersionData();
const node = await BundlerNode.init({
nodeOptions: options,
relayersConfig: config,
relayerDb: db,
testingMode: params.testingMode,
redirectRpc: params.redirectRpc,
bundlingMode: params.executor.bundlingMode,
peerId,
metricsOptions: params.metrics,
version,
});
// const version = getVersionData();
// const node = await BundlerNode.init({
// nodeOptions: options,
// relayersConfig: config,
// relayerDb: db,
// testingMode: params.testingMode,
// redirectRpc: params.redirectRpc,
// bundlingMode: params.executor.bundlingMode,
// peerId,
// metricsOptions: params.metrics,
// version,
// });

await node.start();
// await node.start();
}

export async function getNodeConfigFromArgs(args: IGlobalArgs): Promise<{
Expand Down
4 changes: 2 additions & 2 deletions packages/executor/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class Config {
}

isEntryPointSupported(entryPoint: string): boolean {
return !!this.config.entryPoints.some(
return this.config.entryPoints.some(
(addr) => addr.toLowerCase() === entryPoint.toLowerCase()
);
}
Expand All @@ -82,7 +82,7 @@ export class Config {
if (config == null) config = {} as NetworkConfig;
config.entryPoints = fromEnvVar(
"ENTRYPOINTS",
config.entryPoints,
config.entryPoints || [],
true
) as string[];

Expand Down
Loading
Loading