Skip to content

Commit

Permalink
move v06 gasEstimation into own folder
Browse files Browse the repository at this point in the history
  • Loading branch information
mouseless-eth committed Oct 4, 2024
1 parent 2418772 commit fde6a08
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 452 deletions.
154 changes: 6 additions & 148 deletions src/rpc/estimation/gasEstimation.ts

Large diffs are not rendered by default.

150 changes: 150 additions & 0 deletions src/rpc/estimation/gasEstimationsV06.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import {
EntryPointV06Abi,
RpcError,
ValidationErrors,
EntryPointV06SimulationsAbi,
executionResultSchema,
hexDataSchema
} from "@alto/types"
import type { StateOverrides, UserOperationV06 } from "@alto/types"
import type { Hex, RpcRequestErrorType } from "viem"
import {
type Address,
type PublicClient,
decodeErrorResult,
encodeFunctionData,
toHex
} from "viem"
import { z } from "zod"
import type { SimulateHandleOpResult } from "./gasEstimation"

export async function simulateHandleOpV06(
userOperation: UserOperationV06,
entryPoint: Address,
publicClient: PublicClient,
targetAddress: Address,
targetCallData: Hex,
blockTagSupport: boolean,
utilityWalletAddress: Address,
finalParam: StateOverrides | undefined = undefined,
fixedGasLimitForEstimation?: bigint
): Promise<SimulateHandleOpResult> {
try {
await publicClient.request({
method: "eth_call",
params: [
{
to: entryPoint,
from: utilityWalletAddress,
data: encodeFunctionData({
abi: EntryPointV06Abi,
functionName: "simulateHandleOp",
args: [userOperation, targetAddress, targetCallData]
}),
...(fixedGasLimitForEstimation !== undefined && {
gas: `0x${fixedGasLimitForEstimation.toString(16)}`
})
},
blockTagSupport
? "latest"
: toHex(await publicClient.getBlockNumber()),
// @ts-ignore
...(finalParam ? [finalParam] : [])
]
})
} catch (e) {
const err = e as RpcRequestErrorType

if (
/return data out of bounds.*|EVM error OutOfOffset.*/.test(
err.details
)
) {
// out of bound (low level evm error) occurs when paymaster reverts with less than 32bytes
return {
result: "failed",
data: "AA50 postOp revert (paymaster revert data out of bounds)"
} as const
}

const causeParseResult = z
.union([
z.object({
code: z.literal(3),
message: z.string(),
data: hexDataSchema
}),
/* Fuse RPCs return in this format. */
z.object({
code: z.number(),
message: z.string().regex(/VM execution error.*/),
data: z
.string()
.transform((data) => data.replace("Reverted ", ""))
.pipe(hexDataSchema)
}),
z.object({
code: z.number(),
message: z
.string()
.regex(/VM Exception while processing transaction:.*/),
data: hexDataSchema
})
])
.safeParse(err.cause)

if (!causeParseResult.success) {
throw new Error(JSON.stringify(err.cause))
}

const cause = causeParseResult.data

if (cause.data === "0x") {
throw new RpcError(
"AA23 reverted: UserOperation called non-existant contract, or reverted with 0x",
ValidationErrors.SimulateValidation
)
}

const decodedError = decodeErrorResult({
abi: [...EntryPointV06Abi, ...EntryPointV06SimulationsAbi],
data: cause.data
})

if (
decodedError &&
decodedError.errorName === "FailedOp" &&
decodedError.args
) {
return {
result: "failed",
data: decodedError.args[1] as string
} as const
}

if (
decodedError &&
decodedError.errorName === "Error" &&
decodedError.args
) {
return {
result: "failed",
data: decodedError.args[0]
} as const
}

if (decodedError.errorName === "ExecutionResult") {
const parsedExecutionResult = executionResultSchema.parse(
decodedError.args
)

return {
result: "execution",
data: {
executionResult: parsedExecutionResult
} as const
}
}
}
throw new Error("Unexpected error")
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,37 @@ function validateTargetCallDataResult(data: Hex):
result: "failed"
data: string
code: number
}
| {
result: "retry" // retry with new bounds if the initial simulation hit the eth_call gasLimit
optimalGas: bigint
maxGas: bigint
minGas: bigint
} {
try {
// check if the result is a SimulationOutOfGas error
const simulationOutOfGasSelector = toFunctionSelector(
"SimulationOutOfGas(uint256 optimalGas, uint256 minGas, uint256 maxGas)"
)

if (slice(data, 0, 4) === simulationOutOfGasSelector) {
const res = decodeErrorResult({
abi: EntryPointV07SimulationsAbi,
data: data
})

if (res.errorName === "SimulationOutOfGas") {
const [optimalGas, minGas, maxGas] = res.args

return {
result: "retry",
optimalGas,
minGas,
maxGas
} as const
}
}

const targetCallResult = decodeFunctionResult({
abi: EntryPointV07SimulationsAbi,
functionName: "simulateCallData",
Expand Down Expand Up @@ -205,14 +234,18 @@ export async function simulateHandleOpV07(
)

try {
const executionResult = getSimulateHandleOpResult(cause[0])
const [simulateHandleOpResult, simulateTargetCallDataResult] = cause

const executionResult = getSimulateHandleOpResult(
simulateHandleOpResult
)

if (executionResult.result === "failed") {
return executionResult
}

const targetCallValidationResult = validateTargetCallDataResult(
cause[1]
simulateTargetCallDataResult
)

if (targetCallValidationResult.result === "failed") {
Expand Down
36 changes: 20 additions & 16 deletions src/rpc/rpcHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
isVersion07,
maxBigInt,
parseUserOperationReceipt,
scaleBigIntByPercent,
toUnpackedUserOperation
} from "@alto/utils"
import {
Expand Down Expand Up @@ -398,22 +399,25 @@ export class RpcHandler implements IRpcEndpoint {
"user operation max fee per gas must be larger than 0 during gas estimation"
)
}
const preVerificationGas =
((await calcPreVerificationGas(
this.publicClient,
userOperation,
entryPoint,
this.chainId,
this.chainType,
this.gasPriceManager,
false
)) *
110n) /
100n

userOperation.preVerificationGas = 1_000_000n
userOperation.verificationGasLimit = 10_000_000n
userOperation.callGasLimit = 10_000_000n

let preVerificationGas = await calcPreVerificationGas(
this.publicClient,
userOperation,
entryPoint,
this.chainId,
this.chainType,
this.gasPriceManager,
false
)
preVerificationGas = scaleBigIntByPercent(preVerificationGas, 110)

// biome-ignore lint/style/noParameterAssign: prepare userOperaiton for simulation
userOperation = {
...userOperation,
preVerificationGas: 1_000_000n,
verificationGasLimit: 10_000_000n,
callGasLimit: 10_000_000n
}

if (this.chainId === base.id) {
userOperation.verificationGasLimit = 5_000_000n
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/validation/SafeValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
type PublicClient,
type Transport
} from "viem"
import { getSimulateValidationResult } from "../estimation/EntryPointSimulationsV07"
import { getSimulateValidationResult } from "../estimation/gasEstimationsV07"
import {
bundlerCollectorTracer,
type BundlerTracerResult,
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/validation/UnsafeValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
zeroAddress
} from "viem"
import { fromZodError } from "zod-validation-error"
import { simulateValidation } from "../estimation/EntryPointSimulationsV07"
import { simulateValidation } from "../estimation/gasEstimationsV07"
import {
type SimulateHandleOpResult,
simulateHandleOp,
Expand Down
Loading

0 comments on commit fde6a08

Please sign in to comment.