From 231d20f6e46f1e81e224eaa149b74382ce6239ee Mon Sep 17 00:00:00 2001 From: Red Cyrax Date: Thu, 25 Aug 2022 10:38:00 +0400 Subject: [PATCH] Improved typing and filtering --- tools/event-filters.ts | 22 +++---- tools/event-wrapper.ts | 128 +++++++++++++++-------------------------- 2 files changed, 56 insertions(+), 94 deletions(-) diff --git a/tools/event-filters.ts b/tools/event-filters.ts index 52c3dc5..7497ab8 100644 --- a/tools/event-filters.ts +++ b/tools/event-filters.ts @@ -303,26 +303,26 @@ const _buildFilterArgs = ( const nonIndexed: unknown[] = [] let hasNonIndexed = false - const isArray = Array.isArray(properties) - const arrayCount = isArray ? (properties as unknown[]).length : 0 - if (isArray) { - expect(arrayCount, 'Inconsistend set of indexed properties').lte( - fragment.inputs.length - ) - } - + let maxIndexed = -1 let namedCount = 0 // eslint-disable-next-line @typescript-eslint/no-for-in-array for (const key in properties) { - if (!isArray || isNaN(parseInt(key, 10))) { + const v = parseInt(key, 10) + if (isNaN(v)) { namedCount++ + } else if (v > maxIndexed) { + maxIndexed = v } } - if (namedCount > 0 || arrayCount > 0) { + expect(maxIndexed, 'Inconsistend set of indexed properties').lt( + fragment.inputs.length + ) + + if (namedCount > 0 || maxIndexed >= 0) { fragment.inputs.forEach((param, index) => { let namedValue = (properties as Record)[param.name] - let value = isArray + let value = index <= maxIndexed ? (properties as unknown[])[index] ?? null : null diff --git a/tools/event-wrapper.ts b/tools/event-wrapper.ts index 9c905ab..909c603 100644 --- a/tools/event-wrapper.ts +++ b/tools/event-wrapper.ts @@ -1,6 +1,5 @@ import {expect} from 'chai' -import {BaseContract, Contract, ContractReceipt, utils} from 'ethers' -import {EventFilters, TypedEvent, TypedEventFilter} from './event-types' +import {Contract, ContractReceipt, utils} from 'ethers' import { expectEmittersAndEvents, ExtendedEventFilter, @@ -45,23 +44,19 @@ type PartialTuple = T extends [infer Head, ...infer Tail] ? [(Head | null)?, ...PartialTuple] : [] -type EventIn = T extends TypedEvent - ? (PartialTuple & O) | A | O - : never +type EventIn = (PartialTuple & O) | A | O -type PartialEventIn = T extends TypedEvent< - infer A, - infer O -> - ? (PartialTuple & Partial) | PartialTuple | Partial - : never +type PartialEventIn = (PartialTuple & Partial) | PartialTuple | Partial -type EventOut = T extends TypedEvent - ? A & O - : never +export interface EventFactoryWithTuple extends EventFactoryOmni { +} + +export interface EventFactory extends EventFactoryOmni { + readonly withTuple: EventFactoryWithTuple +} -export interface EventFactory { - expectOne(receipt: ContractReceipt, expected?: EventIn): EventOut +export interface EventFactoryOmni { + expectOne(receipt: ContractReceipt, expected?: EventIn): R /** * Parses logs of the receipt by the given filters. @@ -88,54 +83,55 @@ export interface EventFactory { */ expectOrdered( receipt: ContractReceipt, - expecteds: PartialEventIn[], + expecteds: PartialEventIn[], forwardOnly?: boolean - ): EventOut[] + ): R[] - all[]>( + all( receipt: ContractReceipt, - fn?: (args: EventOut[]) => Result + fn?: (args: R[]) => Result ): Result waitAll( source: ContractReceiptSource, - fn?: (args: EventOut[]) => void + fn: (args: R[]) => void ): Promise toString(): string name(): string - newListener(): EventListener> + newListener(): EventListener newFilter( - args?: PartialEventIn, + args?: PartialEventIn, emitterAddress?: string | '*' - ): ExtendedEventFilter> + ): ExtendedEventFilter } -const _wrap = ( - template: T, // only for type +export const wrapEventType = ( customName: string, emitter: Contract -): EventFactory => - new (class implements EventFactory { - expectOne>( +): EventFactory => + new (class implements EventFactory { + withTuple = this as unknown as EventFactoryWithTuple + + expectOne( receipt: ContractReceipt, - expected?: EventIn - ): R { + expected?: EventIn + ): O { const args = findEventArgs(this.toString(), receipt, emitter) expect( args.length, `Expecting a single event ${this.toString()}` ).equals(1) - return this.verifyArgs(args[0], expected as PartialEventIn) + return this.verifyArgs(args[0], expected as PartialEventIn) } expectOrdered( receipt: ContractReceipt, - expecteds: PartialEventIn[], + expecteds: PartialEventIn[], forwardOnly?: boolean - ): EventOut[] { + ): O[] { const filters = expecteds.map((expected) => this.newFilter(expected) ) @@ -147,9 +143,9 @@ const _wrap = ( return events } - all[]>( + all( receipt: ContractReceipt, - fn?: (args: EventOut[]) => Result + fn?: (args: O[]) => Result ): Result { const args = findEventArgs(this.toString(), receipt, emitter) @@ -161,12 +157,12 @@ const _wrap = ( return args as unknown as Result } - return fn(args as unknown as EventOut[]) + return fn(args as unknown as O[]) } async waitAll( source: ContractReceiptSource, - fn?: (args: EventOut[]) => void + fn?: (args: O[]) => void ): Promise { const receipt = await successfulTransaction(source) this.all(receipt, fn) @@ -181,39 +177,39 @@ const _wrap = ( return customName } - private verifyArgs>( + private verifyArgs( args: utils.Result, - expected?: PartialEventIn - ): R { + expected?: PartialEventIn + ): O { const n = this.toString() if ((expected ?? null) !== null) { _verifyByProperties(expected, n, args) } _verifyByFragment(emitter.interface.getEvent(n), n, args) - return args as unknown as R + return args as unknown as O } - newListener>(): EventListener { + newListener(): EventListener { const n = this.toString() const fragment = emitter.interface.getEvent(n) - return new EventListener(emitter, n, (event) => { + return new EventListener(emitter, n, (event) => { const args = event.args ?? ({} as utils.Result) _verifyByFragment(fragment, n, args) - return args as unknown as R + return args as unknown as O }) } newFilter( - filter?: PartialEventIn, + filter?: PartialEventIn, emitterAddress?: string - ): ExtendedEventFilter> { + ): ExtendedEventFilter { const n = this.toString() - return newExtendedEventFilter>( + return newExtendedEventFilter( n, emitterAddress ?? emitter.address, emitter.interface, - filter as unknown as EventOut + filter as unknown as O ) } })() @@ -261,37 +257,3 @@ const _verifyByProperties = ( }) } -class ContractEventFilters extends BaseContract { - readonly filters!: F -} - -type ExtractEventFilters = - T extends ContractEventFilters ? F : never - -type EventFilterType> = - T extends TypedEventFilter ? R : never - -export const eventOf = < - C extends BaseContract, - N extends keyof ExtractEventFilters & string ->( - emitter: C, - name: N -): EventFactory[N]>>> => - _wrap( - null as unknown as EventFilterType< - ReturnType[N]> - >, - name, - emitter - ) - -export const newEventListener = < - C extends BaseContract, - N extends keyof ExtractEventFilters & string ->( - emitter: C, - name: N -): EventListener< - EventOut[N]>>> -> => eventOf(emitter, name).newListener()