From d6c658ffe07f01249875eea2ad237c98f4f59b5b Mon Sep 17 00:00:00 2001 From: debuggingfuture Date: Sat, 17 Feb 2024 04:52:44 +0800 Subject: [PATCH 1/2] add: use single event listener per #91 --- packages/webapp/src/store/subscriptions.ts | 37 ++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/webapp/src/store/subscriptions.ts b/packages/webapp/src/store/subscriptions.ts index 4abde7cc..e7353bbd 100644 --- a/packages/webapp/src/store/subscriptions.ts +++ b/packages/webapp/src/store/subscriptions.ts @@ -11,6 +11,7 @@ // ================================================================================================= +import _ from "lodash" import { Log } from "viem" import { getPublicClient } from "wagmi/actions" @@ -75,15 +76,33 @@ export function subscribeToGame(ID: bigint|null) { } if (needsSub) { currentlySubscribedID = ID - eventNames.forEach(eventName => { - unsubFunctions.push(publicClient.watchContractEvent({ - address: deployment.Game, - abi: gameABI, - eventName: eventName as any, - args: { gameID: ID }, - onLogs: logs => gameEventListener(eventName, logs) - })) - }) + + const eventsABI = gameABI.filter((abi) => abi.type === "event" && eventNames.includes(abi.name)); + + /** + * + * Related to https://github.com/0xFableOrg/0xFable/issues/91 + * + * viem limitations: watchEvent scoped to multiple events supposedly not also scoped with indexed arguments (args). + * https://viem.sh/docs/actions/public/watchEvent.html#multiple-events + * + * use `watchEvent` (instead of watchContractEvent) which will create filter against gameID + * this is not expected usage of watchEvent and might break when upgrade + * also only works if the args (gameID) is expected across all event topics + */ + unsubFunctions.push(publicClient.watchEvent({ + address: deployment.Game, + events: eventsABI, + args: { gameID: ID }, + onLogs: logs => { + _.toPairs(_.groupBy(logs, (log:any) => log.eventName)) + .forEach( + ([eventName, logs]:[string,any]) => { + gameEventListener(eventName, logs) + } + ) + } + })) } } From 3cc1bc0ccec7a667d0712c4261fb93777e746f3b Mon Sep 17 00:00:00 2001 From: debuggingfuture Date: Mon, 19 Feb 2024 17:50:03 +0800 Subject: [PATCH 2/2] fix: use single listener reference --- packages/webapp/src/store/subscriptions.ts | 30 ++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/webapp/src/store/subscriptions.ts b/packages/webapp/src/store/subscriptions.ts index e7353bbd..b019b211 100644 --- a/packages/webapp/src/store/subscriptions.ts +++ b/packages/webapp/src/store/subscriptions.ts @@ -52,8 +52,8 @@ const eventNames = [ /** ID of the game we are currently subscribed to, or null if we are not subscribed. */ let currentlySubscribedID: bigint|null = null -/** List of function to call to unsubscribe from game updates. */ -let unsubFunctions: (() => void)[] = [] +/** Function to call to unsubscribe from game updates. */ +let unsubscribeEventListener: (() => void) | null = null // ------------------------------------------------------------------------------------------------- @@ -69,8 +69,10 @@ export function subscribeToGame(ID: bigint|null) { if (needsUnsub) { // remove subscription - unsubFunctions.forEach(unsub => unsub()) - unsubFunctions = [] + if(unsubscribeEventListener){ + unsubscribeEventListener() + unsubscribeEventListener = null; + } console.log(`unsubscribed from game events for game ID ${currentlySubscribedID}`) currentlySubscribedID = null } @@ -80,29 +82,25 @@ export function subscribeToGame(ID: bigint|null) { const eventsABI = gameABI.filter((abi) => abi.type === "event" && eventNames.includes(abi.name)); /** - * - * Related to https://github.com/0xFableOrg/0xFable/issues/91 - * - * viem limitations: watchEvent scoped to multiple events supposedly not also scoped with indexed arguments (args). - * https://viem.sh/docs/actions/public/watchEvent.html#multiple-events - * - * use `watchEvent` (instead of watchContractEvent) which will create filter against gameID - * this is not expected usage of watchEvent and might break when upgrade - * also only works if the args (gameID) is expected across all event topics + * Listen to all events in eventNames for the current game ID. + * All of these events must have an indexed gameID argument. + * We must use watchEvent to be able to listen to multiple events at the same time. + * Wagmi does not officially support listening to multiple events with an argument filter, + * and this might break in future updates. */ - unsubFunctions.push(publicClient.watchEvent({ + unsubscribeEventListener = publicClient.watchEvent({ address: deployment.Game, events: eventsABI, args: { gameID: ID }, onLogs: logs => { - _.toPairs(_.groupBy(logs, (log:any) => log.eventName)) + Object.entries(_.groupBy(logs, (log:any) => log.eventName)) .forEach( ([eventName, logs]:[string,any]) => { gameEventListener(eventName, logs) } ) } - })) + }) } }