// ts-essentials import { DeepRequired, Merge } from 'ts-essentials' // Contract types import { RelayerRegistry } from './deth/RelayerRegistry.js' // Local imports import { AsyncUtils, ErrorUtils } from '@tornado/sdk-utils' import { Docs, Cache, Onchain, Options as DataOptions } from '@tornado/sdk-data' import { Chain, Synchronizer, Options as ChainOptions, syncErrorHandler } from '@tornado/sdk-chain' // @ts-ignore import { toIndexableString } from 'pouchdb-collate' import { match } from 'assert' // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECLARATIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ export namespace Options { export type Cache = DataOptions.Cache export type Sync = ChainOptions.Sync } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPLEMENTATIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ export class Registrations extends Docs.Base {} export class Stakes extends Docs.Base {} export class Burns extends Docs.Base {} export class RegistrationsCache extends Cache.Syncable { // TODO: IMPLEMENT buildDoc(response: any): Docs.Withdrawal { return new Docs.Withdrawal(response) } getErrorHandlers(): Array { return [syncErrorHandler] } getCallbacks(registry: RelayerRegistry): Array { return [ (fromBlock: number, toBlock: number) => { return registry.queryFilter( registry.filters['RelayerRegistered(bytes32,string,address,uint256)'](null, null, null, null), fromBlock, toBlock ) } ] } } export class StakesCache extends Cache.Syncable { // TODO: IMPLEMENT buildDoc(response: any): Docs.Withdrawal { return new Docs.Withdrawal(response) } getErrorHandlers(): Array { return [syncErrorHandler] } getCallbacks(registry: RelayerRegistry): Array { return [ (fromBlock: number, toBlock: number) => { return registry.queryFilter( registry.filters['StakeAddedToRelayer(address,uint256)'](null, null), fromBlock, toBlock ) } ] } } export class BurnsCache extends Cache.Syncable { // TODO: IMPLEMENT buildDoc(response: any): Docs.Withdrawal { return new Docs.Withdrawal(response) } getErrorHandlers(): Array { return [syncErrorHandler] } getCallbacks(registry: RelayerRegistry): Array { return [ (fromBlock: number, toBlock: number) => { return registry.queryFilter( registry.filters['StakeBurned(address,uint256)'](null, null), fromBlock, toBlock ) } ] } } export class Registry extends Synchronizer { private _chain?: Chain get chain(): Chain { this._checkProvider('chain') return this._chain! } constructor() { super() this.caches = new Map>() } loadRegistrations(name: string, options?: Options.Cache): RegistrationsCache { return this.loadCache(name, options) } loadStakes(name: string, options?: Options.Cache): StakesCache { return this.loadCache(name, options) } loadBurns(name: string, options?: Options.Cache): BurnsCache { return this.loadCache(name, options) } loadCache = Cache.Syncable>( name: string, options?: Options.Cache ): C { let constructor, cache = super.loadCache(name) if (!cache) { const regexp = /([A-Za-z]+)([0-9]+)/ const matches = name.match(regexp) if (!matches) throw ErrorUtils.getError(`Registry.loadCache: name ${name} has wrong format for cache`) // ETH net id if (matches.length === 2) name += "1" if (matches[1] === 'Registrations') { constructor = (name: string, options?: Options.Cache) => new RegistrationsCache(name, options) } else if (matches[1] === 'Burns') { constructor = (name: string, options?: Options.Cache) => new BurnsCache(name, options) } else if (matches[1] === 'Stakes') { constructor = (name: string, options?: Options.Cache) => new StakesCache(name, options) } else throw ErrorUtils.getError(`Registry.loadCache: there exists no such event ${matches[1]}`) cache = constructor(name, options) this.caches.set(name, cache) } return cache as C } // TODO: FINISH async _sync( event: string, networkId?: string, options?: Merge ): Promise { networkId = networkId ?? '1' if (this.chain.id !== +networkId) throw ErrorUtils.getError('Registry.syncRegistrations: provider bound to wrong chain!') const registryAddress = await Onchain.getRegistryAddress(networkId) options = options ?? {} options.startBlock = options.startBlock ?? (await Onchain.getRegistryDeployBlockNum(networkId)) } private _checkProvider(parentCallName: string): void { if (!this._chain) throw ErrorUtils.getError('Core.' + parentCallName + ': you must first connect a provider!') } protected async _populateSyncOptions(options: Options.Sync): Promise> { options.targetBlock = options.targetBlock ?? (await this.chain.latestBlockNum()) return super._populateSyncOptions(options) } }