UNPKG

@owlprotocol/contracts-account-abstraction

Version:

ERC4337 Account Abstraction support for deploying relevant smart contracts and interacting with UserOps.

136 lines (119 loc) 4.42 kB
import { Address, BundlerRpcSchema, decodeEventLog, EIP1193Parameters, EIP1193RequestFn, encodeEventTopics, Hash, Hex, hexToNumber, numberToHex, Prettify, PublicRpcSchema, RpcTransaction, } from "viem"; import { entryPoint07Address, UserOperation } from "viem/account-abstraction"; import { decodeFunctionData } from "viem/utils"; import { sei, seiDevnet, seiTestnet } from "viem/chains"; import { UserOperationEvent, handleOps } from "../../artifacts/IEntryPoint.js"; import { PackedUserOperation, toUserOperationEncoded } from "../../models/PackedUserOperation.js"; export type RpcGetUserOperationReturnType07 = Prettify<{ /** The block hash the User Operation was included on. */ blockHash: Hash; /** The block number the User Operation was included on. */ blockNumber: Hex; /** The EntryPoint which handled the User Operation. */ entryPoint: Address; /** The hash of the transaction which included the User Operation. */ transactionHash: Hash; /* The transaction (optional to align with official interface) */ transaction?: RpcTransaction; /** The User Operation. */ userOperation: UserOperation<"0.7", Hex>; }> | null; export function createRequestGetUserOperationByHash(request: EIP1193RequestFn<PublicRpcSchema>) { return async function ( args: EIP1193Parameters<BundlerRpcSchema> & { method: "eth_getUserOperationByHash" }, ): Promise<RpcGetUserOperationReturnType07> { const [hash] = args.params; const chainId = await request({ method: "eth_chainId" }); const chainIdNumber = hexToNumber(chainId); const blockNumberHex = await request({ method: "eth_blockNumber", }); //TODO: Parametrize rpc max range // Certain RPCs enforce a max block range let rpcMaxRange = 90_000; if (chainIdNumber === sei.id || chainIdNumber === seiDevnet.id || chainIdNumber === seiTestnet.id) { rpcMaxRange = 2_000; } const blockNumber = hexToNumber(blockNumberHex); const fromBlock = Math.max(blockNumber - rpcMaxRange, 0); const filterResult = await request({ method: "eth_getLogs", params: [ { address: entryPoint07Address, topics: encodeEventTopics({ abi: [UserOperationEvent], eventName: "UserOperationEvent", args: { userOpHash: hash, }, }), fromBlock: numberToHex(fromBlock), toBlock: "latest", }, ], }); if (filterResult.length === 0) { return null; } const userOperationLog = filterResult[0]; const userOperationEvent = decodeEventLog({ abi: [UserOperationEvent], eventName: "UserOperationEvent", data: userOperationLog.data, topics: userOperationLog.topics, strict: true, }); const transactionHash = userOperationLog.transactionHash; if (transactionHash === null) { return null; } const transaction = (await request({ method: "eth_getTransactionByHash", params: [transactionHash], })) as RpcTransaction<false> | null; if (!transaction) { return null; } let op: PackedUserOperation | undefined = undefined; try { const decoded = decodeFunctionData({ abi: [handleOps], data: transaction.input, }); const ops = decoded.args[0]; op = ops.find( (op: PackedUserOperation) => op.sender === userOperationEvent.args.sender && op.nonce === userOperationEvent.args.nonce, ); } catch { return null; } if (op === undefined) { return null; } const userOperation = toUserOperationEncoded(op); return { blockHash: transaction.blockHash, blockNumber: transaction.blockNumber, entryPoint: entryPoint07Address, transactionHash, transaction, userOperation, }; }; }