import { getEntryPointVersion } from "permissionless" import type { EntryPoint } from "permissionless/types" import { decodeAbiParameters, decodeFunctionData } from "viem" import type { Address, Hex } from "viem" import { KernelAccountAbi as KernelV2AccountAbi } from "../abis/KernelV2AccountAbi" import { KernelV3AccountAbi } from "../abis/KernelV3AccountAbi" import type { Call } from "../types" export const decodeCalldata = ({ accountAddress, callData, entryPoint }: { accountAddress: Address callData: Hex entryPoint: EntryPoint }): Call[] => { if (getEntryPointVersion(entryPoint) === "v0.6") { return decodeKernelV2Calldata(accountAddress, callData) } return decodeKernelV3Calldata(accountAddress, callData) } // Decode function for kernel v2 const decodeKernelV2Calldata = ( accountAddress: Address, callData: Hex ): Call[] => { const res = decodeFunctionData({ data: callData, abi: KernelV2AccountAbi }) switch (res.functionName) { case "execute": return [ { to: res.args[0], data: res.args[2], value: res.args[1] } ] case "executeBatch": return [...res.args[0]] case "executeDelegateCall": throw new Error("delegate call cannot be batched") default: return [ { to: accountAddress, data: callData, value: 0n } ] } } // Decode function for kernel v3 const decodeKernelV3Calldata = ( accountAddress: Address, callData: Hex ): Call[] => { // biome-ignore lint/suspicious/noExplicitAny: abi decoding let res: { functionName: string; args: readonly any[] } try { res = decodeFunctionData({ data: callData, abi: KernelV3AccountAbi }) } catch (e) { // calldata of kernel account with hook res = decodeFunctionData({ data: `0x${callData.slice(10)}`, abi: KernelV3AccountAbi }) } // to address is the account address if (res.functionName !== "execute") { return [ { to: accountAddress, data: callData, value: 0n } ] } return decodeKernelV3ExecuteCall(res.args[0], res.args[1]) } const decodeKernelV3ExecuteCall = (execMode: Hex, args: Hex) => { const callType = execMode.slice(0, 4) switch (callType) { case "0x00": return [ { to: args.slice(0, 42) as Address, value: BigInt(`0x${args.slice(42, 106)}`), data: `0x${args.slice(106)}` as Hex } ] case "0x01": return decodeBatchExecution(args) case "0xff": throw new Error("delegate call cannot be batched") default: throw new Error("unknown call data") } } const decodeBatchExecution = (args: Hex) => { const decodedBatch = decodeAbiParameters( [ { name: "executionBatch", type: "tuple[]", components: [ { name: "target", type: "address" }, { name: "value", type: "uint256" }, { name: "callData", type: "bytes" } ] } ], args ) return decodedBatch[0].map((batch) => ({ to: batch.target, value: batch.value, data: batch.callData })) }