Skip to content

Commit

Permalink
chore: smart sessions migration
Browse files Browse the repository at this point in the history
  • Loading branch information
joepegler committed Dec 31, 2024
1 parent 3fe6115 commit 0a759dc
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 132 deletions.
41 changes: 41 additions & 0 deletions src/sdk/account/utils/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,44 @@ export const getAllowance = async (

return approval as bigint
}

export function parseRequestArguments(input: string[]) {
const fieldsToOmit = [
"callGasLimit",
"preVerificationGas",
"maxFeePerGas",
"maxPriorityFeePerGas",
"paymasterAndData",
"verificationGasLimit"
]

// Skip the first element which is just "Request Arguments:"
const argsString = input.slice(1).join("")

// Split by newlines and filter out empty lines
const lines = argsString.split("\n").filter((line) => line.trim())

// Create an object from the key-value pairs
const result = lines.reduce(
(acc, line) => {
// Remove extra spaces and split by ':'
const [key, value] = line.split(":").map((s) => s.trim())

// Clean up the key (remove trailing spaces and colons)
const cleanKey = key.trim()

// Clean up the value (remove 'gwei' and other units)
const cleanValue: string | number = value.replace("gwei", "").trim()

if (fieldsToOmit.includes(cleanKey)) {
return acc
}

acc[cleanKey] = cleanValue
return acc
},
{} as Record<string, string | number>
)

return result
}
29 changes: 29 additions & 0 deletions src/sdk/account/utils/contractSimulation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { http, type Address, createPublicClient, parseEther } from "viem"
import { ENTRY_POINT_ADDRESS, EntrypointAbi } from "../../constants"
import { getChain } from "./getChain"
import { getSimulationUserOp } from "./tenderlySimulation"
import type { AnyUserOperation } from "./tenderlySimulation"

export async function contractSimulation(
partialUserOp: AnyUserOperation,
chainId: number
) {
const packed = getSimulationUserOp(partialUserOp)

return createPublicClient({
chain: getChain(chainId),
transport: http()
}).simulateContract({
account: partialUserOp.sender as Address,
address: ENTRY_POINT_ADDRESS,
abi: EntrypointAbi,
functionName: "handleOps",
args: [[packed], packed.sender],
stateOverride: [
{
address: partialUserOp.sender as Address,
balance: parseEther("1000")
}
]
})
}
66 changes: 66 additions & 0 deletions src/sdk/account/utils/tenderlySimulation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import type { RpcUserOperation } from "viem"
import {
type UserOperation,
toPackedUserOperation
} from "viem/account-abstraction"
import { getTenderlyDetails } from "."
import { ENTRY_POINT_ADDRESS } from "../../constants"
import { deepHexlify } from "./deepHexlify"

export type AnyUserOperation = Partial<UserOperation<"0.7"> | RpcUserOperation>

export const getSimulationUserOp = (partialUserOp: AnyUserOperation) => {
const simulationGasLimits = {
callGasLimit: 100_000_000_000n,
verificationGasLimit: 100_000_000_000n,
preVerificationGas: 1n,
maxFeePerGas: 100_000_000_000n,
maxPriorityFeePerGas: 1n,
paymasterVerificationGasLimit: 100_000_000_000n,
paymasterPostOpGasLimit: 100_000n
}

const mergedUserOp = deepHexlify({
...simulationGasLimits,
...partialUserOp
})

return toPackedUserOperation(mergedUserOp)
}

export function tenderlySimulation(
partialUserOp: AnyUserOperation,
chainId = 84532
) {
const tenderlyDetails = getTenderlyDetails()

if (!tenderlyDetails) {
console.log(
"Tenderly details not found in environment variables. Please set TENDERLY_API_KEY, TENDERLY_ACCOUNT_SLUG, and TENDERLY_PROJECT_SLUG."
)
return null
}

const tenderlyUrl = new URL(
`https://dashboard.tenderly.co/${tenderlyDetails.accountSlug}/${tenderlyDetails.projectSlug}/simulator/new`
)

const packedUserOp = getSimulationUserOp(partialUserOp)

const params = new URLSearchParams({
contractAddress: ENTRY_POINT_ADDRESS,
value: "0",
network: chainId.toString(),
contractFunction: "0x765e827f", // handleOps
functionInputs: JSON.stringify([packedUserOp]),
stateOverrides: JSON.stringify([
{
contractAddress: packedUserOp.sender,
balance: "100000000000000000000"
}
])
})

tenderlyUrl.search = params.toString()
return tenderlyUrl.toString()
}
199 changes: 73 additions & 126 deletions src/sdk/clients/decorators/smartAccount/debugUserOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,24 @@ import {
toPackedUserOperation
} from "viem/account-abstraction"

import {
http,
type Assign,
type BaseError,
type Chain,
type Client,
type Hex,
type MaybeRequired,
type Narrow,
type OneOf,
type Transport,
createPublicClient,
parseEther
import type {
Assign,
BaseError,
Chain,
Client,
Hex,
MaybeRequired,
Narrow,
OneOf,
Transport
} from "viem"
import { type Address, parseAccount } from "viem/accounts"
import { type RequestErrorType, getAction } from "viem/utils"
import { AccountNotFoundError } from "../../../account/utils/AccountNotFound"
import { getTenderlyDetails } from "../../../account/utils/Utils"
import { parseRequestArguments } from "../../../account/utils/Utils"
import { deepHexlify } from "../../../account/utils/deepHexlify"
import { getAAError } from "../../../account/utils/getAAError"
import { getChain } from "../../../account/utils/getChain"
import { ENTRY_POINT_ADDRESS, EntrypointAbi } from "../../../constants"
import { tenderlySimulation } from "../../../account/utils/tenderlySimulation"
export type DebugUserOperationParameters<
account extends SmartAccount | undefined = SmartAccount | undefined,
accountOverride extends SmartAccount | undefined = SmartAccount | undefined,
Expand Down Expand Up @@ -125,128 +121,79 @@ export async function debugUserOperation<
client: Client<Transport, Chain | undefined, account>,
parameters: DebugUserOperationParameters<account, accountOverride, calls>
) {
const tenderlyDetails = getTenderlyDetails()

const { account: account_ = client.account, entryPointAddress } = parameters

if (!account_ && !parameters.sender) throw new AccountNotFoundError()
const account = account_ ? parseAccount(account_) : undefined
const chainId = Number(client.account?.client?.chain?.id?.toString() ?? 84532)

const request = account
? await getAction(
client,
prepareUserOperation,
"prepareUserOperation"
)(parameters as unknown as PrepareUserOperationParameters)
: parameters

// biome-ignore lint/style/noNonNullAssertion: <explanation>
const signature = (parameters.signature ||
(await account?.signUserOperation(request as UserOperation)))!

const userOpWithSignature = {
...request,
signature
} as UserOperation
try {
const { account: account_ = client.account, entryPointAddress } = parameters

const packed = toPackedUserOperation(userOpWithSignature)
console.log(
"Packed userOp:\n",
JSON.stringify([deepHexlify(packed)], null, 2)
)
const rpcParameters = formatUserOperationRequest(userOpWithSignature)
console.log("Bundler userOp:", rpcParameters)
if (!account_ && !parameters.sender) throw new AccountNotFoundError()
const account = account_ ? parseAccount(account_) : undefined

const chainId = client.account?.client?.chain?.id?.toString()
const request = account
? await getAction(
client,
prepareUserOperation,
"prepareUserOperation"
)(parameters as unknown as PrepareUserOperationParameters)
: parameters

if (tenderlyDetails) {
const tenderlyUrl = new URL(
`https://dashboard.tenderly.co/${tenderlyDetails.accountSlug}/${tenderlyDetails.projectSlug}/simulator/new`
)
// biome-ignore lint/style/noNonNullAssertion: <explanation>
const signature = (parameters.signature ||
(await account?.signUserOperation(request as UserOperation)))!

const formattedRpcParams = {
sender: rpcParameters.sender,
nonce: rpcParameters.nonce,
initCode: rpcParameters.initCode,
callData: rpcParameters.callData,
accountGasLimits: rpcParameters.callGasLimit,
preVerificationGas: rpcParameters.preVerificationGas,
gasFees: rpcParameters.maxFeePerGas,
maxPriorityFeePerGas: rpcParameters.maxPriorityFeePerGas,
paymasterAndData: rpcParameters.paymasterAndData,
signature: rpcParameters.signature
}
const userOpWithSignature = {
...request,
signature
} as UserOperation

const params = new URLSearchParams({
contractAddress: ENTRY_POINT_ADDRESS,
value: "0",
network: chainId ?? "84532",
contractFunction: "0x765e827f",
rawFunctionInput: packed.callData,
functionInputs: JSON.stringify([formattedRpcParams]),
stateOverrides: JSON.stringify([
{
contractAddress: rpcParameters.sender,
balance: "100000000000000000000"
}
])
})
tenderlyUrl.search = params.toString()
} else {
const packed = toPackedUserOperation(userOpWithSignature)
console.log(
"Tenderly details not found in environment variables. Please set TENDERLY_API_KEY, TENDERLY_ACCOUNT_SLUG, and TENDERLY_PROJECT_SLUG."
"Packed userOp:\n",
JSON.stringify([deepHexlify(packed)], null, 2)
)
}
const rpcParameters = formatUserOperationRequest(userOpWithSignature)
console.log("Bundler userOp:", rpcParameters)

try {
const simulation = await createPublicClient({
chain: client.account?.client?.chain ?? getChain(Number(chainId)),
transport: http()
}).simulateContract({
account: rpcParameters.sender,
address: ENTRY_POINT_ADDRESS,
abi: EntrypointAbi,
functionName: "handleOps",
args: [[packed], rpcParameters.sender],
stateOverride: [
{
address: rpcParameters.sender,
balance: parseEther("1000")
}
]
})
const tenderlyUrl = tenderlySimulation(rpcParameters, chainId)
console.log({ tenderlyUrl })

console.log("Simulation:", { simulation })
} catch (error) {
console.error("Simulation failed")
}

try {
const hash = await client.request(
{
method: "eth_sendUserOperation",
params: [
rpcParameters,
// biome-ignore lint/style/noNonNullAssertion: <explanation>
(entryPointAddress ?? account?.entryPoint.address)!
]
},
{ retryCount: 0 }
)
console.log("User Operation Hash:", hash)
return hash
try {
const hash = await client.request(
{
method: "eth_sendUserOperation",
params: [
rpcParameters,
// biome-ignore lint/style/noNonNullAssertion: <explanation>
(entryPointAddress ?? account?.entryPoint.address)!
]
},
{ retryCount: 0 }
)
console.log("User Operation Hash:", hash)
return hash
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
} catch (error: any) {
if (error?.details) {
const aaError = await getAAError(error?.details)
console.log({ aaError })
}

const calls = (parameters as any).calls
throw getUserOperationError(error as BaseError, {
...(request as UserOperation),
...(calls ? { calls } : {}),
signature
})
}
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
} catch (error: any) {
if (error?.details) {
const aaError = await getAAError(error?.details)
console.log({ aaError })
if (error.metaMessages) {
try {
const messageJson = parseRequestArguments(error.metaMessages)
const tenderlyUrl = tenderlySimulation(messageJson)
console.log({ tenderlyUrl })
} catch (error) {}
}

const calls = (parameters as any).calls
throw getUserOperationError(error as BaseError, {
...(request as UserOperation),
...(calls ? { calls } : {}),
signature
})
throw error
}
}
2 changes: 1 addition & 1 deletion src/sdk/modules/smartSessionsValidator/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export type Rule = {
*/
export type RawParamRule = {
condition: ParamCondition
offset: bigint
offset: number
isLimited: boolean
ref: Hex
usage: LimitUsage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,6 @@ describe("modules.smartSessions.dx", async () => {
const byteCode = await testClient.getCode({
address: testAddresses.Counter
})
console.log(
"testAddresses.Counter",
testAddresses.Counter,
byteCode?.length
)
const userOpHash = await useSmartSessionNexusClient.usePermission({
calls: [
{
Expand Down

0 comments on commit 0a759dc

Please sign in to comment.