Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mouseless0x committed Oct 18, 2024
1 parent e312a2d commit f8e1624
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 45 deletions.
3 changes: 2 additions & 1 deletion src/cli/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { setupServer } from "./setupServer"
import { type AltoConfig, createConfig } from "../createConfig"
import { parseArgs } from "./parseArgs"
import { deploySimulationsContract } from "./deploySimulationsContract"
import { eip7702Actions } from "viem/experimental"

const preFlightChecks = async (config: AltoConfig): Promise<void> => {
for (const entrypoint of config.entrypoints) {
Expand Down Expand Up @@ -101,7 +102,7 @@ export async function bundlerHandler(args_: IOptionsInput): Promise<void> {
)
}),
chain
})
}).extend(eip7702Actions())

// if flag is set, use utility wallet to deploy the simulations contract
if (args.deploySimulationsContract) {
Expand Down
109 changes: 80 additions & 29 deletions src/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
type UserOperationV06,
type UserOperationV07,
type UserOperationWithHash,
deriveUserOperation
deriveUserOperation,
isExperimental7702Type
} from "@alto/types"
import type { Logger, Metrics } from "@alto/utils"
import {
Expand Down Expand Up @@ -55,6 +56,7 @@ import {
} from "./utils"
import type { SendTransactionErrorType } from "viem"
import type { AltoConfig } from "../createConfig"
import { SignedAuthorizationList } from "viem/experimental"

export interface GasEstimateResult {
preverificationGas: bigint
Expand Down Expand Up @@ -340,22 +342,27 @@ export class Executor {
abi: EntryPointV06Abi,
functionName: "handleOps",
args: [
opsToBundle.map(
(opInfo) =>
opInfo.mempoolUserOperation as UserOperationV06
),
opsToBundle.map(({ mempoolUserOperation }) => {
const op =
deriveUserOperation(mempoolUserOperation)
return op as UserOperationV06
}),
transactionInfo.executor.address
]
})
: encodeFunctionData({
abi: EntryPointV07Abi,
functionName: "handleOps",
args: [
opsToBundle.map((opInfo) =>
toPackedUserOperation(
opInfo.mempoolUserOperation as UserOperationV07
opsToBundle.map((opInfo) => {
const op = deriveUserOperation(
opInfo.mempoolUserOperation
)
),

return toPackedUserOperation(
op as UserOperationV07
)
}),
transactionInfo.executor.address
]
})
Expand Down Expand Up @@ -521,7 +528,8 @@ export class Executor {
account: Account
gas: bigint
nonce: number
}
},
authorizationList?: SignedAuthorizationList
) {
const request =
await this.config.walletClient.prepareTransactionRequest({
Expand All @@ -543,8 +551,19 @@ export class Executor {
// Try sending the transaction and updating relevant fields if there is an error.
while (attempts < maxAttempts) {
try {
transactionHash =
await this.config.walletClient.sendTransaction(request)
if (authorizationList) {
transactionHash =
// @ts-ignore: It is complaining about legacy type tx types
await this.config.walletClient.sendTransaction({
...request,
authorizationList
})
} else {
transactionHash =
await this.config.walletClient.sendTransaction({
...request
})
}

break
} catch (e: unknown) {
Expand Down Expand Up @@ -592,7 +611,7 @@ export class Executor {
return {
mempoolUserOperation: op,
userOperationHash: getUserOperationHash(
op,
deriveUserOperation(op),
entryPoint,
this.config.walletClient.chain.id
)
Expand Down Expand Up @@ -655,7 +674,16 @@ export class Executor {
this.config.legacyTransactions,
this.config.fixedGasLimitForEstimation,
this.reputationManager,
childLogger
childLogger,
opsWithHashes
.map(({ mempoolUserOperation }) => {
if (isExperimental7702Type(mempoolUserOperation)) {
return mempoolUserOperation.authorization
}

return undefined
})
.filter((auth) => auth !== undefined) as SignedAuthorizationList
)

if (simulatedOps.length === 0) {
Expand Down Expand Up @@ -756,19 +784,29 @@ export class Executor {
...gasOptions
}

const userOps = opsWithHashToBundle.map((owh) =>
isUserOpVersion06
? owh.mempoolUserOperation
: toPackedUserOperation(
owh.mempoolUserOperation as UserOperationV07
)
) as PackedUserOperation[]
const userOps = opsWithHashToBundle.map((owh) => {
const op = deriveUserOperation(owh.mempoolUserOperation)
return isUserOpVersion06
? op
: toPackedUserOperation(op as UserOperationV07)
}) as PackedUserOperation[]

const authorizationList = opsWithHashToBundle
.map((owh) => {
if ("authorization" in owh.mempoolUserOperation) {
return owh.mempoolUserOperation.authorization
}

return undefined
})
.filter((auth) => auth !== undefined) as SignedAuthorizationList

transactionHash = await this.sendHandleOpsTransaction(
userOps,
isUserOpVersion06,
entryPoint,
opts
opts,
authorizationList
)

opsWithHashToBundle.map(({ userOperationHash }) => {
Expand Down Expand Up @@ -823,7 +861,7 @@ export class Executor {

sentry.captureException(err)
childLogger.error(
{ error: JSON.stringify(err) },
{ error: JSON.stringify(e) },
"error submitting bundle transaction"
)
this.markWalletProcessed(wallet)
Expand Down Expand Up @@ -865,8 +903,14 @@ export class Executor {
functionName: "handleOps",
args: [
opsWithHashToBundle.map(
(owh) =>
owh.mempoolUserOperation as UserOperationV06
({ mempoolUserOperation }) => {
const op =
deriveUserOperation(
mempoolUserOperation
)

return op as UserOperationV06
}
),
wallet.address
]
Expand All @@ -875,10 +919,17 @@ export class Executor {
abi: ep.abi,
functionName: "handleOps",
args: [
opsWithHashToBundle.map((owh) =>
toPackedUserOperation(
owh.mempoolUserOperation as UserOperationV07
)
opsWithHashToBundle.map(
({ mempoolUserOperation }) => {
const op =
deriveUserOperation(
mempoolUserOperation
)

return toPackedUserOperation(
op as UserOperationV07
)
}
),
wallet.address
]
Expand Down
31 changes: 23 additions & 8 deletions src/executor/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
hexToBytes,
numberToHex
} from "viem"
import { SignedAuthorizationList } from "viem/experimental"

export function simulatedOpsToResults(
simulatedOps: {
Expand Down Expand Up @@ -132,7 +133,8 @@ export async function filterOpsAndEstimateGas(
onlyPre1559: boolean,
fixedGasLimitForEstimation: bigint | undefined,
reputationManager: InterfaceReputationManager,
logger: Logger
logger: Logger,
authorizationList?: SignedAuthorizationList
) {
const simulatedOps: {
owh: UserOperationWithHash
Expand Down Expand Up @@ -162,19 +164,31 @@ export async function filterOpsAndEstimateGas(

const opsToSend = simulatedOps
.filter((op) => op.reason === undefined)
.map((op) => {
.map(({ owh }) => {
const op = deriveUserOperation(owh.mempoolUserOperation)
return isUserOpV06
? op.owh.mempoolUserOperation
: toPackedUserOperation(
op.owh
.mempoolUserOperation as UserOperationV07
)
? op
: toPackedUserOperation(op as UserOperationV07)
})

logger.info("calling eth_estimateGas", {
args: {
authorizationList,
account: wallet,
nonce: nonce,
blockTag: blockTag,
...(fixedEstimationGasLimit !== undefined && {
gas: fixedEstimationGasLimit
}),
...gasOptions
}
})

gasLimit = await ep.estimateGas.handleOps(
// @ts-ignore - ep is set correctly for opsToSend, but typescript doesn't know that
[opsToSend, wallet.address],
{
authorizationList,
account: wallet,
nonce: nonce,
blockTag: blockTag,
Expand Down Expand Up @@ -210,6 +224,7 @@ export async function filterOpsAndEstimateGas(

return { simulatedOps, gasLimit }
} catch (err: unknown) {
logger.error({ err }, "error estimating gas!!")
logger.error({ err, blockTag }, "error estimating gas")
const e = parseViemError(err)

Expand Down Expand Up @@ -375,7 +390,7 @@ export async function filterOpsAndEstimateGas(
} else {
sentry.captureException(err)
logger.error(
{ error: JSON.stringify(err), blockTag },
{ error: JSON.stringify(e), blockTag },
"error estimating gas"
)
return { simulatedOps: [], gasLimit: 0n }
Expand Down
36 changes: 36 additions & 0 deletions src/rpc/rpcHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
import { base, baseSepolia, optimism } from "viem/chains"
import type { NonceQueuer } from "./nonceQueuer"
import type { AltoConfig } from "../createConfig"
import { SignedAuthorization } from "viem/experimental"

export interface IRpcEndpoint {
handleMethod(
Expand Down Expand Up @@ -297,6 +298,14 @@ export class RpcHandler implements IRpcEndpoint {
...request.params
)
}
case "pimlico_sendExperimental":
return {
method,
result: await this.pimlico_sendExperimental(
apiVersion,
...request.params
)
}
}
}

Expand Down Expand Up @@ -1037,6 +1046,33 @@ export class RpcHandler implements IRpcEndpoint {
return userOperationReceipt
}

async pimlico_sendExperimental(
apiVersion: ApiVersion,
userOperation: UserOperation,
entryPoint: Address,
authorization: SignedAuthorization
) {
try {
this.logger.info("trying addToMempoolIfValid")
await this.addToMempoolIfValid(
{
userOperation,
authorization
},
entryPoint,
apiVersion
)
} catch (e) {
this.logger.error(e)
}

return getUserOperationHash(
userOperation,
entryPoint,
this.config.publicClient.chain.id
)
}

async pimlico_sendCompressedUserOperation(
apiVersion: ApiVersion,
compressedCalldata: Hex,
Expand Down
30 changes: 25 additions & 5 deletions src/types/mempool.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { Address, Chain, Hex } from "viem"
import type { Account } from "viem/accounts"
import type { CompressedUserOperation, HexData32, UserOperation } from "."
import type {
CompressedUserOperation,
Experimental7702UserOperation,
HexData32,
UserOperation
} from "."

export interface ReferencedCodeHashes {
// addresses accessed during this user operation
Expand All @@ -13,16 +18,31 @@ export interface ReferencedCodeHashes {
export const deriveUserOperation = (
op: MempoolUserOperation
): UserOperation => {
return isCompressedType(op)
? (op as CompressedUserOperation).inflatedOp
: (op as UserOperation)
if (isCompressedType(op)) {
return (op as CompressedUserOperation).inflatedOp
}
if (isExperimental7702Type(op)) {
return (op as Experimental7702UserOperation).userOperation
}

// default case
return op as UserOperation
}

export const isCompressedType = (op: MempoolUserOperation): boolean => {
return "compressedCalldata" in op
}

export type MempoolUserOperation = UserOperation | CompressedUserOperation
export const isExperimental7702Type = (
op: MempoolUserOperation
): op is Experimental7702UserOperation => {
return "authorization" in op
}

export type MempoolUserOperation =
| UserOperation
| CompressedUserOperation
| Experimental7702UserOperation

export type TransactionInfo = {
transactionType: "default" | "compressed"
Expand Down
Loading

0 comments on commit f8e1624

Please sign in to comment.