UNPKG

@owlprotocol/contracts-account-abstraction

Version:

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

126 lines (111 loc) 4.36 kB
import { BundlerRpcSchema, decodeEventLog, EIP1193Parameters, EIP1193RequestFn, encodeEventTopics, Hex, hexToNumber, numberToHex, Prettify, PublicRpcSchema, RpcTransactionReceipt, } from "viem"; import { entryPoint07Address, UserOperationReceipt } from "viem/account-abstraction"; import { sei, seiDevnet, seiTestnet } from "viem/chains"; import { UserOperationEvent } from "../../artifacts/IEntryPoint.js"; export type RpcGetUserOperationReceiptReturnType07 = Prettify< UserOperationReceipt<"0.7", Hex, Hex, "0x0" | "0x1"> > | null; export function createRequestGetUserOperationReceipt(request: EIP1193RequestFn<PublicRpcSchema>) { return async function ( args: EIP1193Parameters<BundlerRpcSchema> & { method: "eth_getUserOperationReceipt" }, ): Promise<RpcGetUserOperationReceiptReturnType07> { 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 receipt = (await request({ method: "eth_getTransactionReceipt", params: [transactionHash], })) as RpcTransactionReceipt | null; if (!receipt) { return null; } //We will filter the receipt logs const logs = receipt.logs; //This logic filters logs emitted for this UserOp let startIndex = -1; let endIndex = -1; logs.forEach((log, index) => { if (log?.topics[0] === userOperationLog.topics[0]) { // process UserOperationEvent if (log.topics[1] === userOperationLog.topics[1]) { // it's our userOpHash. save as end of logs array endIndex = index; } else if (endIndex === -1) { // it's a different hash. remember it as beginning index, but only if we didn't find our end index yet. startIndex = index; } } }); if (endIndex === -1) { // Should not happen throw new Error("fatal: no UserOperationEvent in logs"); } const filteredLogs = logs.slice(startIndex + 1, endIndex); return { entryPoint: entryPoint07Address, userOpHash: hash, sender: userOperationEvent.args.sender, nonce: numberToHex(userOperationEvent.args.nonce), actualGasUsed: numberToHex(userOperationEvent.args.actualGasUsed), actualGasCost: numberToHex(userOperationEvent.args.actualGasCost), success: userOperationEvent.args.success, receipt, logs: filteredLogs, }; }; }