Skip to content

Commit

Permalink
Merge branch 'main' into cleanup-executors
Browse files Browse the repository at this point in the history
  • Loading branch information
mouseless0x committed Jan 28, 2025
2 parents 843144e + 5552d69 commit 7c9cfb4
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 23 deletions.
8 changes: 5 additions & 3 deletions src/cli/config/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ export const compatibilityArgsSchema = z.object({
"op-stack",
"arbitrum",
"hedera",
"mantle",
"skale"
"mantle"
]),
"legacy-transactions": z.boolean(),
"api-version": z
Expand Down Expand Up @@ -229,7 +228,10 @@ export const gasEstimationArgsSchema = z.object({
"simulation-paymaster-post-op-gas-limit": z
.string()
.transform((val) => BigInt(val)),
"paymaster-gas-limit-multiplier": z.string().transform((val) => BigInt(val))
"paymaster-gas-limit-multiplier": z
.string()
.transform((val) => BigInt(val)),
"eth-call-sender-address": addressSchema.optional()
})

export type IBundlerArgs = z.infer<typeof bundlerArgsSchema>
Expand Down
14 changes: 6 additions & 8 deletions src/cli/config/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ export const gasEstimationOptions: CliCommandOptions<IGasEstimationArgsInput> =
type: "string",
require: true,
default: "2000000"
},
"eth-call-sender-address": {
description:
"For permissioned chains, eth_call simulations require a whitelisted address as the sender",
type: "string"
}
}

Expand Down Expand Up @@ -315,14 +320,7 @@ export const compatibilityOptions: CliCommandOptions<ICompatibilityArgsInput> =
description:
"Indicates what type of chain the bundler is running on",
type: "string",
choices: [
"default",
"op-stack",
"arbitrum",
"hedera",
"mantle",
"skale"
],
choices: ["default", "op-stack", "arbitrum", "hedera", "mantle"],
default: "default"
},
"legacy-transactions": {
Expand Down
8 changes: 5 additions & 3 deletions src/cli/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,14 @@ export async function bundlerHandler(args_: IOptionsInput): Promise<void> {
chain
})

if (args.chainType === "skale") {
// SKALE only allows white listed addresses to deploy contracts.
// Some permissioned chains require a whitelisted address to make deployments.
// In order for simulations to work, we need to make our eth_call's from a whitelisted address.
if (args.ethCallSenderAddress) {
const whitelistedSender = args.ethCallSenderAddress
publicClient = publicClient
.extend((client) => ({
async call(args: CallParameters) {
args.account = "0x4337000c2828F5260d8921fD25829F606b9E8680"
args.account = whitelistedSender
return await client.call(args)
}
}))
Expand Down
1 change: 0 additions & 1 deletion src/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export class Executor {
cancelOps(_entryPoint: Address, _ops: UserOperation[]): Promise<void> {
throw new Error("Method not implemented.")
}

async sendHandleOpsTransaction({
txParam,
opts
Expand Down
16 changes: 14 additions & 2 deletions src/executor/executorManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,8 +723,20 @@ export class ExecutorManager {
await this.refreshUserOperationStatuses()

// for all still not included check if needs to be replaced (based on gas price)
const gasPriceParameters =
await this.gasPriceManager.tryGetNetworkGasPrice()
let gasPriceParameters: {
maxFeePerGas: bigint
maxPriorityFeePerGas: bigint
}

try {
gasPriceParameters =
await this.gasPriceManager.tryGetNetworkGasPrice()
} catch {
gasPriceParameters = {
maxFeePerGas: 0n,
maxPriorityFeePerGas: 0n
}
}

const transactionInfos = getTransactionsFromUserOperationEntries(
this.mempool.dumpSubmittedOps()
Expand Down
26 changes: 23 additions & 3 deletions src/executor/senderManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,18 @@ export class SenderManager {
this.metrics.utilityWalletInsufficientBalance.set(0)

if (Object.keys(balancesMissing).length > 0) {
const { maxFeePerGas, maxPriorityFeePerGas } =
await this.gasPriceManager.tryGetNetworkGasPrice()
let maxFeePerGas: bigint
let maxPriorityFeePerGas: bigint
try {
const gasPriceParameters =
await this.gasPriceManager.tryGetNetworkGasPrice()

maxFeePerGas = gasPriceParameters.maxFeePerGas
maxPriorityFeePerGas = gasPriceParameters.maxPriorityFeePerGas
} catch (e) {
this.logger.error(e, "No gas price available")
return
}

if (this.config.refillHelperContract) {
const instructions = []
Expand Down Expand Up @@ -280,7 +290,17 @@ export class SenderManager {

const wallets = Array.from(allWallets)

const gasPrice = await this.gasPriceManager.tryGetNetworkGasPrice()
let gasPrice: {
maxFeePerGas: bigint
maxPriorityFeePerGas: bigint
}

try {
gasPrice = await this.gasPriceManager.tryGetNetworkGasPrice()
} catch (e) {
this.logger.error({ error: e }, "error flushing stuck transaction")
return
}

const promises = wallets.map((wallet) => {
try {
Expand Down
7 changes: 6 additions & 1 deletion src/handlers/gasPriceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,12 @@ export class GasPriceManager {

public async getGasPrice(): Promise<GasPriceParameters> {
if (this.config.gasPriceRefreshInterval === 0) {
return await this.tryUpdateGasPrice()
try {
return await this.tryUpdateGasPrice()
} catch (e) {
this.logger.error(e, "No gas price available")
throw new RpcError("No gas price available")
}
}

const maxFeePerGas = this.maxFeePerGasQueue.getLatestValue()
Expand Down
41 changes: 41 additions & 0 deletions src/rpc/rpcHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
import { base, baseSepolia, optimism } from "viem/chains"
import type { NonceQueuer } from "./nonceQueuer"
import type { AltoConfig } from "../createConfig"
import { recoverAuthorizationAddress } from "viem/experimental"

export interface IRpcEndpoint {
handleMethod(
Expand Down Expand Up @@ -791,6 +792,8 @@ export class RpcHandler implements IRpcEndpoint {
)
}

await this.validateEip7702Auth(userOperation)

return await this.estimateGas({
apiVersion,
userOperation,
Expand All @@ -812,6 +815,7 @@ export class RpcHandler implements IRpcEndpoint {
}

this.ensureEntryPointIsSupported(entryPoint)
await this.validateEip7702Auth(userOperation)

try {
await this.addToMempoolIfValid(
Expand Down Expand Up @@ -893,6 +897,43 @@ export class RpcHandler implements IRpcEndpoint {
return userOperationReceipt
}

async validateEip7702Auth(userOperation: UserOperation) {
if (!userOperation.eip7702Auth) {
throw new RpcError(
"UserOperation is missing eip7702Auth",
ValidationErrors.InvalidFields
)
}

// Check that auth is valid.
const sender = await recoverAuthorizationAddress({
authorization: userOperation.eip7702Auth
})
if (sender !== userOperation.sender) {
throw new RpcError(
"Invalid EIP-7702 authorization: The recovered signer address does not match the userOperation sender address",
ValidationErrors.InvalidFields
)
}

if (isVersion06(userOperation) && userOperation.initCode) {
throw new RpcError(
"Invalid EIP-7702 authorization: UserOperation cannot contain initCode.",
ValidationErrors.InvalidFields
)
}

if (
isVersion07(userOperation) &&
(userOperation.factoryData || userOperation.factory)
) {
throw new RpcError(
"Invalid EIP-7702 authorization: UserOperation cannot contain factory or factoryData.",
ValidationErrors.InvalidFields
)
}
}

async getNonceValue(userOperation: UserOperation, entryPoint: Address) {
const entryPointContract = getContract({
address: entryPoint,
Expand Down
1 change: 0 additions & 1 deletion src/types/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@ export type ChainType =
| "arbitrum"
| "hedera"
| "mantle"
| "skale"
8 changes: 7 additions & 1 deletion src/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,8 +426,14 @@ export function calcDefaultPreVerificationGas(
const callDataCost = packed
.map((x) => (x === 0 ? ov.zeroByte : ov.nonZeroByte))
.reduce((sum, x) => sum + x)

const authorizationCost = userOperation.eip7702Auth
? 37500 // overhead for PER_EMPTY_ACCOUNT_COST + PER_AUTH_BASE_COST
: 0

const ret = Math.round(
callDataCost +
authorizationCost +
callDataCost +
ov.fixed / ov.bundleSize +
ov.perUserOp +
ov.perUserOpWord * lengthInWord
Expand Down

0 comments on commit 7c9cfb4

Please sign in to comment.