diff --git a/src/actions/public/watchContractEvent.test.ts b/src/actions/public/watchContractEvent.test.ts index 4ca7f945d14..9ea65f6b46f 100644 --- a/src/actions/public/watchContractEvent.test.ts +++ b/src/actions/public/watchContractEvent.test.ts @@ -11,6 +11,7 @@ import { setBalance } from '../test/setBalance.js' import { stopImpersonatingAccount } from '../test/stopImpersonatingAccount.js' import { writeContract } from '../wallet/writeContract.js' +import { InvalidInputRpcError, RpcRequestError } from '../../index.js' import * as createContractEventFilter from './createContractEventFilter.js' import * as getBlockNumber from './getBlockNumber.js' import * as getFilterChanges from './getFilterChanges.js' @@ -597,32 +598,37 @@ describe('errors', () => { { retry: 3 }, ) - test('reinitializes filter after the consecutive error reset threshold is reached', async () => { - const CONSECUTIVE_ERROR_THRESHOLD = 3 - - // Fail 3 times to trigger a filter reinitalization. - vi.spyOn(getFilterChanges, 'getFilterChanges') - .mockRejectedValueOnce(new Error('first fail')) - .mockRejectedValueOnce(new Error('second fail')) - .mockRejectedValueOnce(new Error('third fail')) - - const eventFilterCreationSpy = vi.spyOn( + test('re-initializes the filter if the active filter uninstalls', async () => { + const filterCreator = vi.spyOn( createContractEventFilter, 'createContractEventFilter', ) const unwatch = watchContractEvent(publicClient, { ...usdcContractConfig, - consecutiveErrorResetThreshold: CONSECUTIVE_ERROR_THRESHOLD, - pollingInterval: 200, onLogs: () => null, onError: () => null, + pollingInterval: 200, }) - await wait(1000) + await wait(250) + expect(filterCreator).toBeCalledTimes(1) + + vi.spyOn(getFilterChanges, 'getFilterChanges').mockRejectedValueOnce( + new InvalidInputRpcError( + new RpcRequestError({ + body: { foo: 'bar' }, + url: 'url', + error: { + code: -32000, + message: 'message', + }, + }), + ), + ) - // Check that event filter creation function was called twice - expect(eventFilterCreationSpy).toBeCalledTimes(2) + await wait(500) + expect(filterCreator).toBeCalledTimes(2) unwatch() }) }) diff --git a/src/actions/public/watchContractEvent.ts b/src/actions/public/watchContractEvent.ts index 47e13ca237e..bb8b78b33af 100644 --- a/src/actions/public/watchContractEvent.ts +++ b/src/actions/public/watchContractEvent.ts @@ -14,6 +14,7 @@ import { observe } from '../../utils/observe.js' import { poll } from '../../utils/poll.js' import { stringify } from '../../utils/stringify.js' +import { InvalidInputRpcError } from '../../index.js' import { type CreateContractEventFilterParameters, createContractEventFilter, @@ -56,8 +57,6 @@ export type WatchContractEventParameters< onLogs: WatchContractEventOnLogsFn /** Polling frequency (in ms). Defaults to Client's pollingInterval config. */ pollingInterval?: number - /** The number of consecutive filter change errors that must occur before resetting the filter. */ - consecutiveErrorResetThreshold?: number /** * Whether or not the logs must match the indexed/non-indexed arguments on `event`. * @default false @@ -113,7 +112,6 @@ export function watchContractEvent< onError, onLogs, pollingInterval = client.pollingInterval, - consecutiveErrorResetThreshold = 5, strict: strict_, }: WatchContractEventParameters, ): WatchContractEventReturnType { @@ -132,11 +130,12 @@ export function watchContractEvent< let previousBlockNumber: bigint let filter: Filter<'event', TAbi, TEventName> | undefined let initialized = false - let consecutiveErrors = 0 const unwatch = poll( async () => { + console.log('poll') if (!initialized) { + console.log('inialize') try { filter = (await createContractEventFilter(client, { abi, @@ -185,16 +184,13 @@ export function watchContractEvent< previousBlockNumber = blockNumber } - consecutiveErrors = 0 - if (logs.length === 0) return if (batch) emit.onLogs(logs as any) else logs.forEach((log) => emit.onLogs([log] as any)) } catch (err) { - consecutiveErrors++ - if (consecutiveErrors >= consecutiveErrorResetThreshold) { - initialized = false - } + // If a filter has been set and gets uninstalled, providers will throw an InvalidInput error. + // Reinitalize the filter when this occurs + if (filter && err instanceof InvalidInputRpcError) initialized = false emit.onError?.(err as Error) } }, diff --git a/src/actions/public/watchEvent.test.ts b/src/actions/public/watchEvent.test.ts index 0f6c1631352..76cec5d5c35 100644 --- a/src/actions/public/watchEvent.test.ts +++ b/src/actions/public/watchEvent.test.ts @@ -11,6 +11,7 @@ import { setBalance } from '../test/setBalance.js' import { stopImpersonatingAccount } from '../test/stopImpersonatingAccount.js' import { writeContract } from '../wallet/writeContract.js' +import { InvalidInputRpcError, RpcRequestError } from '../../index.js' import * as createEventFilter from './createEventFilter.js' import * as getBlockNumber from './getBlockNumber.js' import * as getFilterChanges from './getFilterChanges.js' @@ -385,4 +386,35 @@ describe('errors', () => { }, { retry: 3 }, ) + + test('re-initializes the filter if the active filter uninstalls', async () => { + const filterCreator = vi.spyOn(createEventFilter, 'createEventFilter') + + const unwatch = watchEvent(publicClient, { + ...usdcContractConfig, + onLogs: () => null, + onError: () => null, + pollingInterval: 200, + }) + + await wait(250) + expect(filterCreator).toBeCalledTimes(1) + + vi.spyOn(getFilterChanges, 'getFilterChanges').mockRejectedValueOnce( + new InvalidInputRpcError( + new RpcRequestError({ + body: { foo: 'bar' }, + url: 'url', + error: { + code: -32000, + message: 'message', + }, + }), + ), + ) + + await wait(500) + expect(filterCreator).toBeCalledTimes(2) + unwatch() + }) }) diff --git a/src/actions/public/watchEvent.ts b/src/actions/public/watchEvent.ts index b995ad0e463..b68fcb8ad55 100644 --- a/src/actions/public/watchEvent.ts +++ b/src/actions/public/watchEvent.ts @@ -13,6 +13,7 @@ import { observe } from '../../utils/observe.js' import { poll } from '../../utils/poll.js' import { stringify } from '../../utils/stringify.js' +import { InvalidInputRpcError } from '../../errors/rpc.js' import { type CreateEventFilterParameters, createEventFilter, @@ -187,6 +188,9 @@ export function watchEvent< if (batch) emit.onLogs(logs as any) else logs.forEach((log) => emit.onLogs([log] as any)) } catch (err) { + // If a filter has been set and gets uninstalled, providers will throw an InvalidInput error. + // Reinitalize the filter when this occurs + if (filter && err instanceof InvalidInputRpcError) initialized = false emit.onError?.(err as Error) } },