diff --git a/.changeset/blue-trees-invent.md b/.changeset/blue-trees-invent.md new file mode 100644 index 0000000000..eb05f585de --- /dev/null +++ b/.changeset/blue-trees-invent.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/explorer": patch +--- + +The transactions list in the explorer is now updated every 100ms instead of on every incoming transaction, to improve performance when there are many incoming transactions. diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx index 1af163b118..8f5db0f8b9 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx @@ -11,6 +11,7 @@ import { parseEventLogs, } from "viem"; import { UserOperation, entryPoint07Abi, entryPoint07Address } from "viem/account-abstraction"; +import { anvil } from "viem/chains"; import { useConfig, useWatchBlocks } from "wagmi"; import { getTransaction, simulateContract, waitForTransactionReceipt } from "wagmi/actions"; import { useStore } from "zustand"; @@ -225,7 +226,7 @@ export function TransactionsWatcher() { } }, chainId, - pollingInterval: 500, + pollingInterval: chainId === anvil.id ? 10 : 500, }); return null; diff --git a/packages/explorer/src/observer/store.ts b/packages/explorer/src/observer/store.ts index 8fc547b194..0f1145b52f 100644 --- a/packages/explorer/src/observer/store.ts +++ b/packages/explorer/src/observer/store.ts @@ -31,37 +31,58 @@ export const store = createStore(() => ({ })); debug("listening for relayed messages", relayChannelName); +const messageBuffer: MessageEvent[] = []; const channel = new BroadcastChannel(relayChannelName); -channel.addEventListener("message", ({ data }: MessageEvent) => { - if (data.type === "ping") return; + +channel.addEventListener("message", (message: MessageEvent) => { + if (message.data.type === "ping") return; + messageBuffer.push(message); +}); + +function flushMessageBuffer(): void { + if (messageBuffer.length === 0) return; + store.setState((state) => { - const write = data.type === "write" ? ({ ...data, events: [] } satisfies Write) : state.writes[data.writeId]; - if (!write) return state; + let updated = state; - let hash = write.hash; - if (data.type === "waitForTransactionReceipt") { - hash = data.hash; - } else if ( - data.type === "waitForUserOperationReceipt:result" && - isPromiseFulfilled< - Messages["waitForUserOperationReceipt:result"] extends PromiseSettledResult ? T : never - >(data) - ) { - hash = data.value.receipt.transactionHash; - } + for (const { data } of messageBuffer) { + if (data.type === "ping") continue; + + const write = data.type === "write" ? ({ ...data, events: [] } satisfies Write) : updated.writes[data.writeId]; + if (!write) continue; - return { - ...state, - writes: { - ...state.writes, - [data.writeId]: { - ...write, - type: data.type, - hash, - userOpHash: data.type === "waitForUserOperationReceipt" ? data.userOpHash : write.userOpHash, - events: [...write.events, data], + let hash = write.hash; + if (data.type === "waitForTransactionReceipt") { + hash = data.hash; + } else if ( + data.type === "waitForUserOperationReceipt:result" && + isPromiseFulfilled< + Messages["waitForUserOperationReceipt:result"] extends PromiseSettledResult ? T : never + >(data) + ) { + hash = data.value.receipt.transactionHash; + } + + updated = { + ...updated, + writes: { + ...updated.writes, + [data.writeId]: { + ...write, + type: data.type, + hash, + userOpHash: data.type === "waitForUserOperationReceipt" ? data.userOpHash : write.userOpHash, + events: [...write.events, data], + }, }, - }, - }; + }; + } + + // Clear messages after processing + messageBuffer.length = 0; + return updated; }); -}); +} + +const bufferInterval = 100; +setInterval(flushMessageBuffer, bufferInterval);