Skip to content

Commit

Permalink
feat: adding tenderly simulation for userop validation (#277)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilkumar1612 authored Feb 4, 2025
1 parent e0c3fb2 commit d9ac381
Show file tree
Hide file tree
Showing 23 changed files with 249 additions and 59 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"packages/*"
],
"npmClient": "yarn",
"version": "1.5.34",
"version": "1.5.35",
"stream": "true",
"command": {
"version": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "root",
"private": true,
"version": "1.5.34",
"version": "1.5.35",
"engines": {
"node": ">=18.0.0"
},
Expand Down
10 changes: 5 additions & 5 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.5.34",
"version": "1.5.35",
"description": "The API module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://https://github.com/etherspot/skandha#readme",
Expand Down Expand Up @@ -34,10 +34,10 @@
"dependencies": {
"@fastify/cors": "9.0.1",
"@fastify/websocket": "10.0.1",
"@skandha/executor": "^1.5.34",
"@skandha/monitoring": "^1.5.34",
"@skandha/types": "^1.5.34",
"@skandha/utils": "^1.5.34",
"@skandha/executor": "^1.5.35",
"@skandha/monitoring": "^1.5.35",
"@skandha/types": "^1.5.35",
"@skandha/utils": "^1.5.35",
"class-transformer": "0.5.1",
"class-validator": "0.14.1",
"ethers": "5.7.2",
Expand Down
14 changes: 7 additions & 7 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.5.34",
"version": "1.5.35",
"description": "> TODO: description",
"author": "zincoshine <[email protected]>",
"homepage": "https://https://github.com/etherspot/skandha#readme",
Expand Down Expand Up @@ -40,12 +40,12 @@
"@libp2p/peer-id-factory": "2.0.1",
"@libp2p/prometheus-metrics": "1.1.3",
"@multiformats/multiaddr": "12.1.3",
"@skandha/api": "^1.5.34",
"@skandha/db": "^1.5.34",
"@skandha/executor": "^1.5.34",
"@skandha/monitoring": "^1.5.34",
"@skandha/node": "^1.5.34",
"@skandha/types": "^1.5.34",
"@skandha/api": "^1.5.35",
"@skandha/db": "^1.5.35",
"@skandha/executor": "^1.5.35",
"@skandha/monitoring": "^1.5.35",
"@skandha/node": "^1.5.35",
"@skandha/types": "^1.5.35",
"find-up": "5.0.0",
"got": "12.5.3",
"js-yaml": "4.1.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.5.34",
"version": "1.5.35",
"description": "Smart contracts of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://https://github.com/etherspot/skandha#readme",
Expand Down
4 changes: 2 additions & 2 deletions packages/db/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.5.34",
"version": "1.5.35",
"description": "The DB module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://github.com/etherspot/etherspot-bundler#readme",
Expand Down Expand Up @@ -34,7 +34,7 @@
"dependencies": {
"@chainsafe/ssz": "0.10.1",
"@farcaster/rocksdb": "5.5.0",
"@skandha/types": "^1.5.34"
"@skandha/types": "^1.5.35"
},
"devDependencies": {
"@types/rocksdb": "3.0.1",
Expand Down
11 changes: 6 additions & 5 deletions packages/executor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"publishConfig": {
"access": "public"
},
"version": "1.5.34",
"version": "1.5.35",
"description": "The Relayer module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://https://github.com/etherspot/skandha#readme",
Expand Down Expand Up @@ -35,11 +35,12 @@
},
"dependencies": {
"@flashbots/ethers-provider-bundle": "0.6.2",
"@skandha/monitoring": "^1.5.34",
"@skandha/params": "^1.5.34",
"@skandha/types": "^1.5.34",
"@skandha/utils": "^1.5.34",
"@skandha/monitoring": "^1.5.35",
"@skandha/params": "^1.5.35",
"@skandha/types": "^1.5.35",
"@skandha/utils": "^1.5.35",
"async-mutex": "0.4.0",
"axios": "^1.7.9",
"ethers": "5.7.2",
"strict-event-emitter-types": "2.0.0",
"ws": "8.16.0"
Expand Down
24 changes: 24 additions & 0 deletions packages/executor/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,27 @@ export class Config {
)
);

config.tenderlyApiUrl = String(
fromEnvVar(
"TENDERLY_API_URL",
config.tenderlyApiUrl || bundlerDefaultConfigs.tenderlyApiUrl
)
);

config.tenderlyKey = String(
fromEnvVar(
"TENDERLY_KEY",
config.tenderlyKey || bundlerDefaultConfigs.tenderlyKey
)
);

config.rpcTimeout = String(
fromEnvVar(
"RPC_TIMEOUT",
config.rpcTimeout || bundlerDefaultConfigs.rpcTimeout
)
);

config.blockscoutApiKeys = fromEnvVar(
"BLOCKSCOUT_API_KEYS",
config.blockscoutApiKeys != undefined
Expand Down Expand Up @@ -433,6 +454,9 @@ const bundlerDefaultConfigs: BundlerConfig = {
fastlaneValidators: [],
blockscoutUrl: "",
blockscoutApiKeys: [],
tenderlyApiUrl: "",
tenderlyKey: "",
rpcTimeout: "10s",
};

function getEnvVar<T>(envVar: string, fallback: T): T | string {
Expand Down
3 changes: 3 additions & 0 deletions packages/executor/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ export interface NetworkConfig {
fastlaneValidators: string[];
blockscoutUrl: string;
blockscoutApiKeys: string[];
tenderlyApiUrl: string;
tenderlyKey: string;
rpcTimeout: string;
}

export type BundlerConfig = Omit<
Expand Down
2 changes: 2 additions & 0 deletions packages/executor/src/modules/skandha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ export class Skandha {
vglMarkupPercent: this.networkConfig.vglMarkupPercent,
blockscoutUrl: this.networkConfig.blockscoutUrl,
blockscoutApiKeys: this.networkConfig.blockscoutApiKeys.length,
tenderlyApiUrl: this.networkConfig.tenderlyApiUrl,
tenderlyKey: this.networkConfig.tenderlyKey,
};
}

Expand Down
8 changes: 6 additions & 2 deletions packages/executor/src/services/UserOpValidation/GethTracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { BigNumber, providers } from "ethers";
import { BundlerCollectorReturn } from "@skandha/types/lib/executor";
import { TracerPrestateResponse } from "../../interfaces";
import { NetworkConfig, TracerPrestateResponse } from "../../interfaces";

const tracer = readFileSync(
resolve(process.cwd(), "packages", "executor", "tracer.js")
Expand All @@ -29,7 +29,10 @@ const stringifiedTracer = tracer
// );

export class GethTracer {
constructor(private provider: providers.JsonRpcProvider) {}
constructor(
private provider: providers.JsonRpcProvider,
private config: NetworkConfig
) {}

async debug_traceCall(
tx: providers.TransactionRequest
Expand All @@ -45,6 +48,7 @@ export class GethTracer {
},
"latest",
{
timeout: this.config.rpcTimeout,
tracer: stringifiedTracer,
},
]);
Expand Down
31 changes: 26 additions & 5 deletions packages/executor/src/services/UserOpValidation/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class UserOpValidationService {
);
this.unsafeValidationService = new UnsafeValidationService(
this.provider,
this.chainId,
this.networkConfig,
this.logger
);
Expand Down Expand Up @@ -102,11 +103,31 @@ export class UserOpValidationService {
entryPoint
);
}
return await this.safeValidationService.validateSafely(
userOp,
entryPoint,
codehash
);
return await this.safeValidationService
.validateSafely(userOp, entryPoint, codehash)
.catch((error) => {
if (
!(error instanceof RpcError) &&
error.message === "debug_traceCall_failed"
) {
this.logger.debug(
"Error occurred during userOp validation on safe mode"
);
this.logger.debug("Validating userOp using unsafe mode...");

if (this.networkConfig.entryPointForwarder.length > 2) {
return this.unsafeValidationService.validateUnsafelyWithForwarder(
userOp,
entryPoint
);
}
return this.unsafeValidationService.validateUnsafely(
userOp,
entryPoint
);
}
throw error;
});
}

async validateGasFee(userOp: UserOperationStruct): Promise<boolean> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export class EstimationService {
private logger: Logger
) {
this.gethTracer = new GethTracer(
this.provider as providers.JsonRpcProvider
this.provider as providers.JsonRpcProvider,
this.networkConfig
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ export class SafeValidationService {
private logger: Logger
) {
this.gethTracer = new GethTracer(
this.provider as providers.JsonRpcProvider
this.provider as providers.JsonRpcProvider,
this.networkConfig
);
}

Expand Down Expand Up @@ -112,8 +113,12 @@ export class SafeValidationService {
...gasPrice,
};

const traceCall: BundlerCollectorReturn =
await this.gethTracer.debug_traceCall(tx);
const traceCall: BundlerCollectorReturn = await this.gethTracer
.debug_traceCall(tx)
.catch((error) => {
this.logger.error(error, "Debug trace call failed");
throw new Error("debug_traceCall_failed");
});
const validationResult = await this.validateOpcodesAndStake(
traceCall,
entryPointContract,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { UserOperationStruct } from "@skandha/types/lib/executor/contracts/EntryPoint";
import { IEntryPoint } from "@skandha/types/src/executor/contracts";
import axios from "axios";
import { BigNumber, constants } from "ethers";
import { Logger } from "@skandha/types/lib";

export class TenderlyValidationService {
constructor(
private tenderlyApiUrl: string,
private tenderlyAccessKey: string,
private chainId: number,
private logger: Logger
) {}

async validate(
userOp: UserOperationStruct,
entryPoint: IEntryPoint,
gasLimit?: BigNumber
): Promise<any> {
const config = {
method: "post",
url: `${this.tenderlyApiUrl}/simulate`,
headers: {
"Content-Type": "application/json",
"X-Access-Key": this.tenderlyAccessKey,
},
data: JSON.stringify({
network_id: `${this.chainId}`,
from: constants.AddressZero,
to: entryPoint.address,
input: entryPoint.interface.encodeFunctionData("simulateValidation", [
userOp,
]),
gas: gasLimit?.toNumber(),
}),
};
return await axios
.request(config)
.then((response) => {
const parsed = entryPoint.interface.parseError(
response.data.transaction.call_trace[0].output
);
return {
...parsed,
errorName: parsed.name,
errorArgs: parsed.args,
};
})
.catch((err) => {
this.logger.error(`Tenderly validation failed: ${err}`);
});
}
}
Loading

0 comments on commit d9ac381

Please sign in to comment.