11import { storeEventsAbi } from "@latticexyz/store" ;
2- import { GetTransactionReceiptErrorType , Hex } from "viem" ;
2+ import { GetTransactionReceiptErrorType , Hex , parseEventLogs } from "viem" ;
3+ import { entryPoint07Abi } from "viem/account-abstraction" ;
34import {
45 StorageAdapter ,
56 StorageAdapterBlock ,
@@ -42,6 +43,7 @@ import { toStorageAdatperBlock } from "./indexer-client/toStorageAdapterBlock";
4243import { watchLogs } from "./watchLogs" ;
4344import { getAction } from "viem/utils" ;
4445import { getChainId , getTransactionReceipt } from "viem/actions" ;
46+ import packageJson from "../package.json" ;
4547
4648const debug = parentDebug . extend ( "createStoreSync" ) ;
4749
@@ -324,7 +326,7 @@ export async function createStoreSync({
324326 // keep 10 blocks worth processed transactions in memory
325327 const recentBlocksWindow = 10 ;
326328 // most recent block first, for ease of pulling the first one off the array
327- const recentBlocks $ = storedBlockLogs$ . pipe (
329+ const recentStoredBlocks $ = storedBlockLogs$ . pipe (
328330 scan < StorageAdapterBlock , StorageAdapterBlock [ ] > (
329331 ( recentBlocks , block ) => [ block , ...recentBlocks ] . slice ( 0 , recentBlocksWindow ) ,
330332 [ ] ,
@@ -339,29 +341,56 @@ export async function createStoreSync({
339341
340342 // This currently blocks for async call on each block processed
341343 // We could potentially speed this up a tiny bit by racing to see if 1) tx exists in processed block or 2) fetch tx receipt for latest block processed
342- const hasTransaction$ = recentBlocks $. pipe (
344+ const hasTransaction$ = recentStoredBlocks $. pipe (
343345 // We use `mergeMap` instead of `concatMap` here to send the fetch request immediately when a new block range appears,
344346 // instead of sending the next request only when the previous one completed.
345- mergeMap ( async ( blocks ) => {
346- for ( const block of blocks ) {
347- const txs = block . logs . map ( ( op ) => op . transactionHash ) ;
348- // If the transaction caused a log, it must have succeeded
349- if ( txs . includes ( tx ) ) {
350- return { blockNumber : block . blockNumber , status : "success" as const , transactionHash : tx } ;
347+ mergeMap ( async ( storedBlocks ) => {
348+ for ( const storedBlock of storedBlocks ) {
349+ // If stored block had Store event logs associated with tx, it must have succeeded.
350+ if ( storedBlock . logs . some ( ( log ) => log . transactionHash === tx ) ) {
351+ return { blockNumber : storedBlock . blockNumber , status : "success" , transactionHash : tx } as const ;
351352 }
352353 }
353354
354355 try {
355- const lastBlock = blocks [ 0 ] ;
356- debug ( "fetching tx receipt for block" , lastBlock . blockNumber ) ;
357- const { status, blockNumber, transactionHash } = await getAction (
358- publicClient ,
359- getTransactionReceipt ,
360- "getTransactionReceipt" ,
361- ) ( { hash : tx } ) ;
362- if ( lastBlock . blockNumber >= blockNumber ) {
363- return { status, blockNumber, transactionHash } ;
356+ const lastStoredBlock = storedBlocks [ 0 ] ;
357+ debug ( "fetching tx receipt for block" , lastStoredBlock . blockNumber ) ;
358+ const receipt = await getAction ( publicClient , getTransactionReceipt , "getTransactionReceipt" ) ( { hash : tx } ) ;
359+
360+ // Skip if sync hasn't caught up to this transaction's block.
361+ if ( lastStoredBlock . blockNumber < receipt . blockNumber ) return ;
362+
363+ // Check if this was a user op so we can get the internal user op status.
364+ const userOpEvents = parseEventLogs ( {
365+ logs : receipt . logs ,
366+ abi : entryPoint07Abi ,
367+ eventName : "UserOperationEvent" as const ,
368+ } ) ;
369+ if ( userOpEvents . length ) {
370+ debug ( "tx receipt appears to be a user op, using user op status instead" ) ;
371+ if ( userOpEvents . length > 1 ) {
372+ const issueLink = `https://github.com/latticexyz/mud/issues/new${ new URLSearchParams ( {
373+ title : "waitForTransaction found more than one user op" ,
374+ body : `MUD version: ${ packageJson . version } \nChain ID: ${ chainId } \nTransaction: ${ tx } \n` ,
375+ } ) } `;
376+ console . warn (
377+ // eslint-disable-next-line max-len
378+ `Receipt for transaction ${ tx } had more than one \`UserOperationEvent\`. This may have unexpected behavior.\n\nIf you encounter this, please open an issue:\n${ issueLink } ` ,
379+ ) ;
380+ }
381+
382+ return {
383+ status : userOpEvents [ 0 ] . args . success ? "success" : "reverted" ,
384+ blockNumber : receipt . blockNumber ,
385+ transactionHash : receipt . transactionHash ,
386+ } as const ;
364387 }
388+
389+ return {
390+ status : receipt . status ,
391+ blockNumber : receipt . blockNumber ,
392+ transactionHash : receipt . transactionHash ,
393+ } ;
365394 } catch ( e ) {
366395 const error = e as GetTransactionReceiptErrorType ;
367396 if ( error . name === "TransactionReceiptNotFoundError" ) {
0 commit comments