Skip to content

Commit

Permalink
feat: multiple executors (#138)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xSulpiride authored Dec 18, 2023
1 parent a713ad1 commit a16360c
Show file tree
Hide file tree
Showing 25 changed files with 137 additions and 90 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,11 @@ Or follow the steps below:
"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)
"relayers": [
"0x0101010101010101010101010101010101010101010101010101010101010101",
"test test test test test test test test test test test junk"
], # relayers private keys, can access from here or via environment variables (SKANDHA_MUMBAI_RELAYERS | SKANDHA_DEV_RELAYERS | etc.)
"beneficiary": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", # optional, fee collector, avaiable via env var (SKANDHA_MUMBAI_BENEFICIARY | etc) - if not set, relayer will be used
"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
Expand Down
6 changes: 4 additions & 2 deletions config.json.default
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
"entryPoints": [
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
],
"relayer": "0xprivateKey",
"beneficiary": "0x690b9a9e9aa1c9db991c7721a92d351db4fac990",
"relayers": [
"test test test test test test test test test test test junk",
"0x0101010101010101010101010101010101010101010101010101010101010101"
],
"rpcEndpoint": "http://localhost:8545"
}
}
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "1.0.29-alpha",
"version": "1.0.30-alpha",
"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.0.29-alpha",
"version": "1.0.30-alpha",
"engines": {
"node": ">=18.0.0"
},
Expand Down
8 changes: 4 additions & 4 deletions packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "api",
"version": "1.0.29-alpha",
"version": "1.0.30-alpha",
"description": "The API module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://https://github.com/etherspot/skandha#readme",
Expand Down Expand Up @@ -35,13 +35,13 @@
"class-transformer": "0.5.1",
"class-validator": "0.14.0",
"ethers": "5.7.2",
"executor": "^1.0.29-alpha",
"executor": "^1.0.30-alpha",
"fastify": "4.14.1",
"monitoring": "^1.0.29-alpha",
"monitoring": "^1.0.30-alpha",
"pino": "8.11.0",
"pino-pretty": "10.0.0",
"reflect-metadata": "0.1.13",
"types": "^1.0.29-alpha"
"types": "^1.0.30-alpha"
},
"devDependencies": {
"@types/connect": "3.4.35"
Expand Down
14 changes: 7 additions & 7 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cli",
"version": "1.0.29-alpha",
"version": "1.0.30-alpha",
"description": "> TODO: description",
"author": "zincoshine <[email protected]>",
"homepage": "https://https://github.com/etherspot/skandha#readme",
Expand Down Expand Up @@ -38,15 +38,15 @@
"@libp2p/peer-id-factory": "2.0.1",
"@libp2p/prometheus-metrics": "1.1.3",
"@multiformats/multiaddr": "12.1.3",
"api": "^1.0.29-alpha",
"db": "^1.0.29-alpha",
"executor": "^1.0.29-alpha",
"api": "^1.0.30-alpha",
"db": "^1.0.30-alpha",
"executor": "^1.0.30-alpha",
"find-up": "5.0.0",
"got": "12.5.3",
"js-yaml": "4.1.0",
"monitoring": "^1.0.29-alpha",
"node": "^1.0.29-alpha",
"types": "^1.0.29-alpha",
"monitoring": "^1.0.30-alpha",
"node": "^1.0.30-alpha",
"types": "^1.0.30-alpha",
"yargs": "17.6.2"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/db/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "db",
"version": "1.0.29-alpha",
"version": "1.0.30-alpha",
"description": "The DB module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://github.com/etherspot/etherspot-bundler#readme",
Expand Down Expand Up @@ -33,7 +33,7 @@
"dependencies": {
"@chainsafe/ssz": "0.10.1",
"@farcaster/rocksdb": "5.5.0",
"types": "^1.0.29-alpha"
"types": "^1.0.30-alpha"
},
"devDependencies": {
"@types/rocksdb": "3.0.1",
Expand Down
8 changes: 4 additions & 4 deletions packages/executor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "executor",
"version": "1.0.29-alpha",
"version": "1.0.30-alpha",
"description": "The Relayer module of Etherspot bundler client",
"author": "Etherspot",
"homepage": "https://https://github.com/etherspot/skandha#readme",
Expand Down Expand Up @@ -34,8 +34,8 @@
"@flashbots/ethers-provider-bundle": "0.6.2",
"async-mutex": "0.4.0",
"ethers": "5.7.2",
"monitoring": "^1.0.29-alpha",
"params": "^1.0.29-alpha",
"types": "^1.0.29-alpha"
"monitoring": "^1.0.30-alpha",
"params": "^1.0.30-alpha",
"types": "^1.0.30-alpha"
}
}
31 changes: 21 additions & 10 deletions packages/executor/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,28 @@ export class Config {
return endpoint ? new providers.JsonRpcProvider(endpoint) : null;
}

getRelayer(network: string): Wallet | providers.JsonRpcSigner | null {
getRelayers(network: string): Wallet[] | providers.JsonRpcSigner[] | null {
const config = this.getNetworkConfig(network);
if (!config) return null;

// fetch from env variables first
const privKey = config.relayer;
const provider = this.getNetworkProvider(network);
if (!provider) {
throw new Error("no provider");
}

if (this.testingMode) {
return provider.getSigner();
return [provider.getSigner()];
}

if (privKey.startsWith("0x")) {
return new Wallet(privKey, provider);
const wallets = [];
for (const privKey of config.relayers) {
if (privKey.startsWith("0x")) {
wallets.push(new Wallet(privKey, provider));
} else {
wallets.push(Wallet.fromMnemonic(privKey).connect(provider));
}
}

return Wallet.fromMnemonic(privKey).connect(provider);
return wallets;
}

getBeneficiary(network: string): string | null {
Expand Down Expand Up @@ -158,11 +160,19 @@ export class Config {
conf.entryPoints,
true
) as string[];
conf.relayer = fromEnvVar(network, "RELAYER", conf.relayer) as string;

conf.relayer = fromEnvVar(network, "RELAYER", conf.relayer) as string; // deprecated
conf.relayers = fromEnvVar(
network,
"RELAYERS",
conf.relayers ?? [conf.relayer], // fallback to `relayer` if `relayers` not found
true
) as string[];

conf.beneficiary = fromEnvVar(
network,
"BENEFICIARY",
conf.beneficiary
conf.beneficiary || bundlerDefaultConfigs.beneficiary
) as string;
conf.rpcEndpoint = fromEnvVar(network, "RPC", conf.rpcEndpoint) as string;

Expand Down Expand Up @@ -322,6 +332,7 @@ export class Config {
}

const bundlerDefaultConfigs: BundlerConfig = {
beneficiary: "",
minInclusionDenominator: 10,
throttlingSlack: 10,
banSlack: 50,
Expand Down
3 changes: 3 additions & 0 deletions packages/executor/src/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ export class Executor {
this.bundlingService.setBundlingMode("manual");
this.logger.info(`${this.networkName}: [X] MANUAL BUNDLING`);
}
if (this.config.testingMode) {
this.bundlingService.setMaxBundleSize(10);
}

if (this.networkConfig.relayingMode === "flashbots") {
if (!this.networkConfig.rpcEndpointSubmit)
Expand Down
5 changes: 3 additions & 2 deletions packages/executor/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ export type BundlingMode = "auto" | "manual";
export type Executors = Map<number, Executor>;
export interface NetworkConfig {
entryPoints: string[];
relayer: string;
relayer: string; // deprecated, but kept for backwards compatibility
relayers: string[];
beneficiary: string;
name?: NetworkName;
rpcEndpoint: string;
Expand Down Expand Up @@ -149,7 +150,7 @@ export interface NetworkConfig {

export type BundlerConfig = Omit<
NetworkConfig,
"entryPoints" | "rpcEndpoint" | "relayer" | "beneficiary"
"entryPoints" | "rpcEndpoint" | "relayer" | "relayers"
>;

export type Networks = {
Expand Down
7 changes: 7 additions & 0 deletions packages/executor/src/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,13 @@ export class Eth {
private async getUserOperationEvent(
userOpHash: string
): Promise<[IEntryPoint | null, UserOperationEventEvent | null]> {
if (!userOpHash) {
throw new RpcError(
"Missing/invalid userOpHash",
RpcErrorCodes.METHOD_NOT_FOUND
);
}

let event: UserOperationEventEvent[] = [];
for (const addr of await this.getSupportedEntryPoints()) {
const contract = IEntryPoint__factory.connect(addr, this.provider);
Expand Down
10 changes: 8 additions & 2 deletions packages/executor/src/modules/skandha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ export class Skandha {
}

async getConfig(): Promise<GetConfigResponse> {
const wallet = this.config.getRelayer(this.networkName);
const wallets = this.config.getRelayers(this.networkName);
const walletAddresses = [];
if (wallets) {
for (const wallet of wallets) {
walletAddresses.push(await wallet.getAddress());
}
}
const hasEtherscanApiKey = Boolean(this.networkConfig.etherscanApiKey);
const hasExecutionRpc = Boolean(this.networkConfig.rpcEndpointSubmit);
return {
Expand All @@ -80,7 +86,7 @@ export class Skandha {
},
entryPoints: this.networkConfig.entryPoints,
beneficiary: this.networkConfig.beneficiary,
relayer: wallet ? await wallet.getAddress() : "",
relayers: walletAddresses,
minInclusionDenominator: BigNumber.from(
this.networkConfig.minInclusionDenominator
).toNumber(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export type Relayer = Wallet | providers.JsonRpcSigner;

export interface IRelayingMode {
isLocked(): boolean;
sendBundle(bundle: Bundle, beneficiary: string): Promise<void>;
sendBundle(bundle: Bundle): Promise<void>;
}
31 changes: 27 additions & 4 deletions packages/executor/src/services/BundlingService/relayers/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ export abstract class BaseRelayer implements IRelayingMode {
protected reputationService: ReputationService,
protected metrics: PerChainMetrics | null
) {
const relayer = this.config.getRelayer(this.network);
if (!relayer) throw new Error("Relayer is not set");
this.relayers = [relayer];
const relayers = this.config.getRelayers(this.network);
if (!relayers) throw new Error("Relayers are not set");
this.relayers = [...relayers];
this.mutexes = this.relayers.map(() => new Mutex());
}

isLocked(): boolean {
return this.mutexes.every((mutex) => mutex.isLocked());
}

sendBundle(_bundle: Bundle, _beneficiary: string): Promise<void> {
sendBundle(_bundle: Bundle): Promise<void> {
throw new Error("Method not implemented.");
}

Expand Down Expand Up @@ -120,4 +120,27 @@ export abstract class BaseRelayer implements IRelayingMode {
});
}
}

/**
* determine who should receive the proceedings of the request.
* if signer's balance is too low, send it to signer. otherwise, send to configured beneficiary.
*/
protected async selectBeneficiary(relayer: Relayer): Promise<string> {
const config = this.config.getNetworkConfig(this.network);
let beneficiary = this.config.getBeneficiary(this.network);
if (!beneficiary || !utils.isAddress(beneficiary)) {
return relayer.getAddress();
}

const signerAddress = await relayer.getAddress();
const currentBalance = await this.provider.getBalance(signerAddress);

if (currentBalance.lte(config!.minSignerBalance) || !beneficiary) {
beneficiary = signerAddress;
this.logger.info(
`low balance on ${signerAddress}. using it as beneficiary`
);
}
return beneficiary;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { MempoolService } from "../../MempoolService";
import { estimateBundleGasLimit } from "../utils";
import { ReputationService } from "../../ReputationService";
import { BaseRelayer } from "./base";
import { wait } from "../../../utils";

export class ClassicRelayer extends BaseRelayer {
constructor(
Expand All @@ -38,7 +39,7 @@ export class ClassicRelayer extends BaseRelayer {
);
}

async sendBundle(bundle: Bundle, beneficiary: string): Promise<void> {
async sendBundle(bundle: Bundle): Promise<void> {
const availableIndex = this.getAvailableRelayerIndex();
if (availableIndex == null) return;
const relayer = this.relayers[availableIndex];
Expand All @@ -48,6 +49,7 @@ export class ClassicRelayer extends BaseRelayer {
if (!bundle.entries.length) return;

await mutex.runExclusive(async (): Promise<void> => {
const beneficiary = await this.selectBeneficiary(relayer);
const entryPoint = entries[0]!.entryPoint;
const entryPointContract = IEntryPoint__factory.connect(
entryPoint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class FlashbotsRelayer extends BaseRelayer {
);
}

async sendBundle(bundle: Bundle, beneficiary: string): Promise<void> {
async sendBundle(bundle: Bundle): Promise<void> {
const availableIndex = this.getAvailableRelayerIndex();
if (availableIndex == null) return;

Expand All @@ -54,6 +54,7 @@ export class FlashbotsRelayer extends BaseRelayer {
if (!bundle.entries.length) return;

await mutex.runExclusive(async (): Promise<void> => {
const beneficiary = await this.selectBeneficiary(relayer);
const entryPoint = entries[0]!.entryPoint;
const entryPointContract = IEntryPoint__factory.connect(
entryPoint,
Expand Down
Loading

0 comments on commit a16360c

Please sign in to comment.