From 1773291a9f994b5eb0a5e6838ad50c5db982b215 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Sun, 16 Jan 2022 19:08:13 +0100 Subject: [PATCH 01/63] feat: created indexer --- typescript/nomad-monitor/package-lock.json | 16 +- typescript/nomad-monitor/package.json | 4 +- .../nomad-monitor/src/indexer/consumer.ts | 11 + typescript/nomad-monitor/src/indexer/event.ts | 32 ++ .../nomad-monitor/src/indexer/indexer.ts | 450 ++++++++++++++++++ typescript/nomad-monitor/src/indexer/main.ts | 38 ++ .../nomad-monitor/src/indexer/orchestrator.ts | 42 ++ 7 files changed, 583 insertions(+), 10 deletions(-) create mode 100644 typescript/nomad-monitor/src/indexer/consumer.ts create mode 100644 typescript/nomad-monitor/src/indexer/event.ts create mode 100644 typescript/nomad-monitor/src/indexer/indexer.ts create mode 100644 typescript/nomad-monitor/src/indexer/main.ts create mode 100644 typescript/nomad-monitor/src/indexer/orchestrator.ts diff --git a/typescript/nomad-monitor/package-lock.json b/typescript/nomad-monitor/package-lock.json index 44dd2b5b5..e1508fe0f 100644 --- a/typescript/nomad-monitor/package-lock.json +++ b/typescript/nomad-monitor/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache 2.0", "dependencies": { "@nomad-xyz/contract-interfaces": "1.1.1", - "@nomad-xyz/sdk": "1.2.1", + "@nomad-xyz/sdk": "^1.2.2", "@types/bunyan": "^1.8.7", "@types/express": "^4.17.13", "@types/google-spreadsheet": "^3.1.5", @@ -30,7 +30,7 @@ "plotly.js": "^2.5.1", "prom-client": "^14.0.0", "request": "^2.88.2", - "ts-node": "^10.2.1", + "ts-node": "^10.4.0", "typescript": "^4.4.3" }, "devDependencies": { @@ -847,9 +847,9 @@ "integrity": "sha512-BPAcfDPoHlRQNKktbsbnpACGdypPFBuX4xQlsWDE7B8XXcfII+SpOLay3/qZmCLb39kV5S1RTYwXdkx2lwLYng==" }, "node_modules/@nomad-xyz/sdk": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@nomad-xyz/sdk/-/sdk-1.2.1.tgz", - "integrity": "sha512-0wK5viJF7/3yjiPLRm0eB+TsNJ8Sd8wu88Z9/VJhKV5+qasVBqMaw9Y4zQhDVa1pbfFcK2+U3TpYy5ripjNcJQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@nomad-xyz/sdk/-/sdk-1.2.2.tgz", + "integrity": "sha512-ePUEh6qUiKb5Hts+f46K8nLUtxxOReyukEMM/JESjUX5dNQ2X8dd+bpxJ/PaVGm7TzhxHwM1gEDNxQeYc6YYmQ==", "dependencies": { "@nomad-xyz/contract-interfaces": "1.1.1", "ethers": "^5.4.6" @@ -5603,9 +5603,9 @@ } }, "@nomad-xyz/sdk": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@nomad-xyz/sdk/-/sdk-1.2.1.tgz", - "integrity": "sha512-0wK5viJF7/3yjiPLRm0eB+TsNJ8Sd8wu88Z9/VJhKV5+qasVBqMaw9Y4zQhDVa1pbfFcK2+U3TpYy5ripjNcJQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@nomad-xyz/sdk/-/sdk-1.2.2.tgz", + "integrity": "sha512-ePUEh6qUiKb5Hts+f46K8nLUtxxOReyukEMM/JESjUX5dNQ2X8dd+bpxJ/PaVGm7TzhxHwM1gEDNxQeYc6YYmQ==", "requires": { "@nomad-xyz/contract-interfaces": "1.1.1", "ethers": "^5.4.6" diff --git a/typescript/nomad-monitor/package.json b/typescript/nomad-monitor/package.json index 94caea8f7..2435f042d 100644 --- a/typescript/nomad-monitor/package.json +++ b/typescript/nomad-monitor/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "@nomad-xyz/contract-interfaces": "1.1.1", - "@nomad-xyz/sdk": "1.2.1", + "@nomad-xyz/sdk": "^1.2.2", "@types/bunyan": "^1.8.7", "@types/express": "^4.17.13", "@types/google-spreadsheet": "^3.1.5", @@ -47,7 +47,7 @@ "plotly.js": "^2.5.1", "prom-client": "^14.0.0", "request": "^2.88.2", - "ts-node": "^10.2.1", + "ts-node": "^10.4.0", "typescript": "^4.4.3" }, "devDependencies": { diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts new file mode 100644 index 000000000..90c7dd250 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -0,0 +1,11 @@ +import { NomadEvent } from "./event"; + +export abstract class Consumer { + abstract consume(event: NomadEvent): void; +} + +export class Logger extends Consumer { + consume(event: NomadEvent): void { + console.log(event); + } +} \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts new file mode 100644 index 000000000..1f1a47c15 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -0,0 +1,32 @@ +export enum ContractType { + Home = 'home', + Replica = 'replica', +} + +export enum EventType { + HomeDispatch = 'homeDispatch', + HomeUpdate = 'homeUpdate', + ReplicaUpdate = 'replicaUpdate', + ReplicaProcess = 'replicaProcess', +} + +export class NomadEvent { + domain: number; + eventType: EventType; + contractType: ContractType; + replicaOrigin: number; + ts: number; + eventData: Object; + block: number; + + constructor(domain: number, eventType: EventType, contractType: ContractType, replicaOrigin: number, ts: number, eventData: Object, block: number) { + this.domain = domain; + this.eventType = eventType; + this.contractType = contractType; + this.replicaOrigin = replicaOrigin; + this.ts = ts; + this.eventData = eventData; + this.block = block; + console.log(`New event at block`, block, new Date(ts)); + } +} \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts new file mode 100644 index 000000000..881126678 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -0,0 +1,450 @@ +import { Orchestrator } from "./orchestrator"; +import { NomadContext } from '@nomad-xyz/sdk'; +import fs from 'fs'; +import { ContractType, EventType, NomadEvent } from "./event"; +import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; +import { ethers } from "ethers"; + +export class Indexer { + domain: number; + sdk: NomadContext; + orchestrator: Orchestrator; + persistance: Persistance; + block2timeCache: Map; + provider: ethers.providers.Provider; + + constructor(domain: number, sdk: NomadContext, orchestrator: Orchestrator) { + this.domain = domain; + this.sdk = sdk; + this.orchestrator = orchestrator; + const loadPersistance = false; + this.persistance = loadPersistance ? this.loadPersistance() : new RamPersistance(`/tmp/persistance_${this.domain}.json`); + console.log(`Persistance:`, this.persistance); + const it = (this.persistance as RamPersistance).iter(); + let i = 0; + for (const event of it) { + console.log(`---> ${event.ts} - ${event.block} - ${event.domain} - ${event.eventType} - ${event.replicaOrigin}`, new Date(event.ts*1000)); + i += 1; + } + console.log(`events found:`, i) + this.block2timeCache = new Map(); + this.provider = this.sdk.getProvider(domain)!; + } + + async getTimeForBlock(block: number) { + const possibleTime = this.block2timeCache.get(block); + if (possibleTime) return possibleTime; + const time = (await this.provider.getBlock(block)).timestamp * 1000; + this.block2timeCache.set(block, time); + return time; + } + + stop() { + // TODO: this should close all listeners + } + + async init() { + await this.persistance.init(); + } + + get height(): number { + return this.persistance.height; + } + + get from(): number { + return this.sdk.getDomain(this.domain)?.paginate?.from || 0; //.getFrom(this.domain); + } + + subscribeHome() { + const home = this.home(); + home.on(home.filters.Dispatch(), async ( + messageHash, + leafIndex, + destinationAndNonce, + committedRoot, + message, + ev) => { + + const eventPrepared = new NomadEvent( + this.domain, + EventType.HomeDispatch, + ContractType.Home, + 0, new Date().valueOf(), { + messageHash, + leafIndex, + destinationAndNonce, + committedRoot, + message, + }, ev.blockNumber + ) + this.persistance.store(eventPrepared) + }); + + home.on(home.filters.Update(), async ( + homeDomain, + oldRoot, + newRoot, + signature, + ev) => { + + const eventPrepared = new NomadEvent( + this.domain, + EventType.HomeUpdate, + ContractType.Home, + 0, new Date().valueOf(), { + homeDomain, + oldRoot, + newRoot, + signature, + }, ev.blockNumber + ) + this.persistance.store(eventPrepared) + }) + } + + subscribeReplica(domain: number) { + const replica = this.replicaForDomain(domain); + replica.on(replica.filters.Update(), async ( + homeDomain, + oldRoot, + newRoot, + signature, + ev) => { + + const eventPrepared = new NomadEvent( + this.domain, + EventType.ReplicaUpdate, + ContractType.Replica, + domain, new Date().valueOf(), { + homeDomain, + oldRoot, + newRoot, + signature, + }, ev.blockNumber + ) + this.persistance.store(eventPrepared) + }); + + replica.on(replica.filters.Process(), async ( + messageHash, success, returnData,ev + ) => { + + const eventPrepared = new NomadEvent( + this.domain, + EventType.ReplicaProcess, + ContractType.Replica, + domain, new Date().valueOf(), { + messageHash, success, returnData, + }, ev.blockNumber + ) + this.persistance.store(eventPrepared) + }) + } + + async startAll(replicas: number[]) { + let from = this.persistance.height + 1; + const to = await this.provider.getBlockNumber(); + from = to - 2000; + console.log(`Wat to fetch from`, from, `to`, to); + // console.log(this.domain, `starting from height`, from, `but actually from to`, to, `- 1000`, to - 1000); + this.subscribeAll(replicas); + await this.fetchAll(from, to, replicas); + + } + + subscribeAll(replicas: number[]) { + this.subscribeHome(); + replicas.forEach(r => this.subscribeReplica(r)) + } + + home(): Home { + return this.sdk.getCore(this.domain)!.home; //getHomeAtDomain(this.domain); + } + + replicaForDomain(domain: number): Replica { + return this.sdk.getReplicaFor(domain, this.domain)!; + } + + async fetchAll(from: number, to: number, replicas: number[]) { + await this.fetchHome(from, to); + await Promise.all(replicas.map(r => this.fetchReplica(r, from, to))); + this.persistance.sortSorage(); + this.savePersistance(); + } + + savePersistance() { + (this.persistance as RamPersistance).saveToFile(); + } + + loadPersistance(): Persistance { + return RamPersistance.loadFromFile(`/tmp/persistance_${this.domain}.json`) + } + + async fetchHome(from: number, to: number) { + const home = this.home(); + { + const events = await home.queryFilter(home.filters.Dispatch(), from, to); + const parsedEvents = await Promise.all(events.map(async (event) => + new NomadEvent( + this.domain, + EventType.HomeDispatch, + ContractType.Home, + 0, await this.getTimeForBlock(event.blockNumber), { + messageHash: event.args[0], + leafIndex: event.args[1], + destinationAndNonce: event.args[2], + committedRoot: event.args[3], + message: event.args[4], + }, event.blockNumber + ) + )) + this.persistance.store(...parsedEvents) + } + + { + const events = await home.queryFilter(home.filters.Update(), from, to); + const parsedEvents = await Promise.all(events.map(async (event) => + new NomadEvent( + this.domain, + EventType.HomeUpdate, + ContractType.Home, + 0, await this.getTimeForBlock(event.blockNumber), { + homeDomain: event.args[0], + oldRoot: event.args[1], + newRoot: event.args[2], + signature: event.args[3], + }, event.blockNumber + ) + )); + this.persistance.store(...parsedEvents) + } + } + + async fetchReplica(domain: number, from: number, to: number) { + const replica = this.replicaForDomain(domain); + { + const events = await replica.queryFilter(replica.filters.Update(), from, to); + const parsedEvents = await Promise.all(events.map(async (event) => + new NomadEvent( + this.domain, + EventType.ReplicaUpdate, + ContractType.Replica, + domain, await this.getTimeForBlock(event.blockNumber), { + homeDomain: event.args[0], + oldRoot: event.args[1], + newRoot: event.args[2], + signature: event.args[3], + }, event.blockNumber + ) + )); + this.persistance.store(...parsedEvents) + } + + { + const events = await replica.queryFilter(replica.filters.Process(), from, to); + const parsedEvents = await Promise.all(events.map(async (event) => + new NomadEvent( + this.domain, + EventType.ReplicaProcess, + ContractType.Replica, + domain, await this.getTimeForBlock(event.blockNumber), { + messageHash: event.args[0], + success: event.args[1], + returnData: event.args[2], + }, event.blockNumber + ) + )); + this.persistance.store(...parsedEvents) + } + } + +} + +export abstract class Persistance { + initiated: boolean; + from: number; + height: number; + constructor() { + this.initiated = false; + this.from = -1; + this.height = -1; + } + + abstract store(...events: NomadEvent[]): void; + abstract init(): Promise; + abstract sortSorage(): void; +} + +export class FilePersistance extends Persistance { + path: string; + constructor( path: string) { + super(); + this.path = path; + } + + store(...events: NomadEvent[]): void {} + async init(): Promise { + if (fs.existsSync(this.path)) { + // load from the storage + this.from = 13; + this.height = 14; + return true; + } else { + return false; + } + } + sortSorage(){} +} + +export class RamPersistance extends Persistance { + block2events: Map; + blocks: number[]; + storePath: string; + + constructor(storePath: string) { + super(); + this.block2events = new Map(); + this.blocks = []; + this.storePath = storePath; + } + + updateFromTo(block: number) { + if (block < this.from || this.from === -1) this.from = block; + if (block > this.height || this.height === -1) this.height = block; + } + + store(...events: NomadEvent[]): void { + events.forEach(event => { + this.updateFromTo(event.block); + const block = this.block2events.get(event.block); + if (block) { + block.push(event) + } else { + this.block2events.set(event.block, [event]); + } + // if (this.height < event.block) { + // this.height = event.block; + // this.blocks.push(event.block); + // this.blocks = this.blocks.sort(); + // } + if (this.blocks.indexOf(event.block) < 0) { + this.blocks.push(event.block); + this.blocks = this.blocks.sort(); + } + }); + this.saveToFile(); + } + async init(): Promise { + return true; + } + sortSorage(){ + this.blocks = this.blocks.sort(); + } + + iter(): EventsRange { + return new EventsRange(this); + } + + saveToFile() { + fs.writeFileSync(this.storePath, JSON.stringify({ + block2events: this.block2events, + blocks: this.blocks, + initiated: this.initiated, + from: this.from, + height: this.height, + storePath: this.storePath, + }, replacer)) + } + + static loadFromFile(storePath: string): RamPersistance { + const object = JSON.parse(fs.readFileSync(storePath, 'utf8'), reviver) as {block2events: Map, blocks: number[], initiated: boolean, + from: number, + height: number}; + const p = new RamPersistance(storePath); + p.block2events = object.block2events; + p.blocks = object.blocks; + p.initiated = object.initiated; + p.from = object.from; + p.height = object.height; + return p; + } +} + +function replacer(key: any, value: any): any { + if(value instanceof Map) { + return { + dataType: 'Map', + value: Array.from(value.entries()), // or with spread: value: [...value] + }; + } else { + return value; + } +} + +function reviver(key: any, value: any): any { + if(typeof value === 'object' && value !== null) { + if (value.dataType === 'Map') { + return new Map(value.value); + } + } + return value; + } + + +export class EventsRange implements Iterable { + private _p: RamPersistance; + private _cacheBlockIndex: number; + private _position: number; + private nextDone: boolean; + + constructor(p: RamPersistance) { + this._p = p; + this._cacheBlockIndex = 0; + this._position = 0; + this.nextDone = false; + } + cachedBlockIndex(index: number): number | undefined { + return this._p.blocks.at(index) + } + + next(value?: any): IteratorResult { + if (this.nextDone) return {done: true, value: undefined}; + let done = false; + const blockNumber = this.cachedBlockIndex(this._cacheBlockIndex); + if (!blockNumber) { + return {done: true, value: undefined}; + } + const block = this._p.block2events.get(blockNumber); + if (!block) { + return {done: true, value: undefined}; + } + let _value = block.at(this._position)!; + + // calculating next positions + if (this._position + 1 < block.length) { + this._position += 1; + } else { + const nextIndex = this._cacheBlockIndex + 1; + const nextBlockNumber = this.cachedBlockIndex(nextIndex); + if (!nextBlockNumber) { + this.nextDone = true; + } else { + if (this._p.block2events.get(nextBlockNumber)) { + this._cacheBlockIndex = nextIndex; + this._position = 0; + } else { + this.nextDone = true; + } + } + } + + return { + done, + value: _value + }; + } + + [Symbol.iterator]() { + return this; + } +} diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts new file mode 100644 index 000000000..1c06ea4c0 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -0,0 +1,38 @@ +import { mainnet } from "@nomad-xyz/sdk"; +import { Logger } from "./consumer"; +import { Orchestrator } from "./orchestrator"; +import * as dotenv from 'dotenv'; +import { ethers } from "ethers"; +dotenv.config({ + path: '/Users/daniilnaumetc/code/tmp/monitor/typescript/nomad-monitor/src/xxx/.env' +}); + +const signer = + process.env.SIGNER!; +const alchemyKey = + process.env.ALCHEMY_KEY!; +const infuraKey = + process.env.INFURA_KEY!; + +console.log(signer, alchemyKey) + +const moonbeamRPC = "https://moonbeam.api.onfinality.io/public";//"https://rpc.api.moonbeam.network"; +// const ethereumRPC = `https://eth-mainnet.alchemyapi.io/v2/${alchemyKey}`; + +(async () => { + const ctx = mainnet;//NomadContext.fromDomains([ethereum, moonbeam]); + const ethereumId = ctx.mustGetDomain('ethereum').id; + const moonbeamId = ctx.mustGetDomain('moonbeam').id; + + ctx.registerProvider( + ethereumId, + new ethers.providers.InfuraProvider('homestead', infuraKey) + ); + ctx.registerWalletSigner(ethereumId, signer); + + ctx.registerRpcProvider(moonbeamId, moonbeamRPC); + ctx.registerWalletSigner(moonbeamId, signer); + const c = new Logger(); + const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0]); + o.indexAll(); +})(); \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts new file mode 100644 index 000000000..e00da23d3 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -0,0 +1,42 @@ +import { NomadContext } from '@nomad-xyz/sdk'; +import { Consumer } from './consumer'; +import { Indexer } from './indexer'; +// import {EventEmmiter} from 'events'; + +export class Orchestrator { + sdk: NomadContext; + consumer: Consumer; + indexers: Map; + gov: number; + // emmiter: EventEmmiter; + + constructor(sdk: NomadContext, c: Consumer, gov: number) { + this.sdk = sdk; + this.consumer = c; + this.indexers = new Map(); + this.gov = gov; + } + + indexAll() { + this.sdk.domainNumbers.map((domain: number) => { + this.index(domain) + }) + } + + index(domain: number) { + const existingIndexer = this.indexers.get(domain); + if (existingIndexer) { + existingIndexer.stop(); + } + + const indexer = new Indexer(domain, this.sdk, this); + + if (domain === this.gov) { + + indexer.startAll(this.sdk.domainNumbers.filter(d => d!=this.gov)) + } else { + indexer.startAll([this.gov]) + } + this.indexers.set(domain, indexer); + } +} \ No newline at end of file From 159f18f7f56858cb7d8d16d32fc2ead29d5cb1f3 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Sun, 16 Jan 2022 20:32:51 +0100 Subject: [PATCH 02/63] feat: added consumer --- .../nomad-monitor/src/indexer/consumer.ts | 9 ++++- .../nomad-monitor/src/indexer/indexer.ts | 36 +++++++++++++---- typescript/nomad-monitor/src/indexer/main.ts | 6 ++- .../nomad-monitor/src/indexer/orchestrator.ts | 39 ++++++++++++++----- 4 files changed, 71 insertions(+), 19 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 90c7dd250..9f4924ae0 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -5,7 +5,14 @@ export abstract class Consumer { } export class Logger extends Consumer { + i: number; + + constructor() { + super(); + this.i = 0; + } + consume(event: NomadEvent): void { - console.log(event); + console.log(this.i ++, '---->', event); } } \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index 881126678..dfe952578 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -12,12 +12,14 @@ export class Indexer { persistance: Persistance; block2timeCache: Map; provider: ethers.providers.Provider; + upToDate: boolean; + eventCallback: undefined | ((event: NomadEvent) => void); constructor(domain: number, sdk: NomadContext, orchestrator: Orchestrator) { this.domain = domain; this.sdk = sdk; this.orchestrator = orchestrator; - const loadPersistance = false; + const loadPersistance = fs.existsSync(`/tmp/persistance_${this.domain}.json`); // true; this.persistance = loadPersistance ? this.loadPersistance() : new RamPersistance(`/tmp/persistance_${this.domain}.json`); console.log(`Persistance:`, this.persistance); const it = (this.persistance as RamPersistance).iter(); @@ -29,6 +31,7 @@ export class Indexer { console.log(`events found:`, i) this.block2timeCache = new Map(); this.provider = this.sdk.getProvider(domain)!; + this.upToDate = false; } async getTimeForBlock(block: number) { @@ -55,6 +58,11 @@ export class Indexer { return this.sdk.getDomain(this.domain)?.paginate?.from || 0; //.getFrom(this.domain); } + processEvent(event: NomadEvent) { + if (this.eventCallback != undefined) this.eventCallback(event); + this.persistance.store(event) + } + subscribeHome() { const home = this.home(); home.on(home.filters.Dispatch(), async ( @@ -77,7 +85,8 @@ export class Indexer { message, }, ev.blockNumber ) - this.persistance.store(eventPrepared) + + this.processEvent(eventPrepared) }); home.on(home.filters.Update(), async ( @@ -98,7 +107,7 @@ export class Indexer { signature, }, ev.blockNumber ) - this.persistance.store(eventPrepared) + this.processEvent(eventPrepared) }) } @@ -122,7 +131,7 @@ export class Indexer { signature, }, ev.blockNumber ) - this.persistance.store(eventPrepared) + this.processEvent(eventPrepared) }); replica.on(replica.filters.Process(), async ( @@ -137,14 +146,14 @@ export class Indexer { messageHash, success, returnData, }, ev.blockNumber ) - this.persistance.store(eventPrepared) + this.processEvent(eventPrepared) }) } - async startAll(replicas: number[]) { + async startAll(replicas: number[]) { // , past: number let from = this.persistance.height + 1; const to = await this.provider.getBlockNumber(); - from = to - 2000; + // from = to - past; console.log(`Wat to fetch from`, from, `to`, to); // console.log(this.domain, `starting from height`, from, `but actually from to`, to, `- 1000`, to - 1000); this.subscribeAll(replicas); @@ -170,6 +179,7 @@ export class Indexer { await Promise.all(replicas.map(r => this.fetchReplica(r, from, to))); this.persistance.sortSorage(); this.savePersistance(); + this.upToDate = true; } savePersistance() { @@ -220,6 +230,18 @@ export class Indexer { } } + throwPastEvents() { + for (const event of (this.persistance as RamPersistance).iter()) { + this.orchestrator.emit('new_event', event); + } + } + + startThrowingEvents(past: boolean) { + this.eventCallback = (event: NomadEvent) => {this.orchestrator.emit('new_event', event)}; + if (past) this.throwPastEvents(); + + } + async fetchReplica(domain: number, from: number, to: number) { const replica = this.replicaForDomain(domain); { diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index 1c06ea4c0..5a15e3029 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -4,7 +4,7 @@ import { Orchestrator } from "./orchestrator"; import * as dotenv from 'dotenv'; import { ethers } from "ethers"; dotenv.config({ - path: '/Users/daniilnaumetc/code/tmp/monitor/typescript/nomad-monitor/src/xxx/.env' + // path: '/Users/daniilnaumetc/code/tmp/monitor/typescript/nomad-monitor/src/indexer/.env' }); const signer = @@ -34,5 +34,7 @@ const moonbeamRPC = "https://moonbeam.api.onfinality.io/public";//"https://rpc.a ctx.registerWalletSigner(moonbeamId, signer); const c = new Logger(); const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0]); - o.indexAll(); + await o.indexAll(); + + o.startConsuming(); })(); \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index e00da23d3..a7f6e364b 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -2,28 +2,37 @@ import { NomadContext } from '@nomad-xyz/sdk'; import { Consumer } from './consumer'; import { Indexer } from './indexer'; // import {EventEmmiter} from 'events'; +import {EventEmitter} from 'events'; +import { NomadEvent } from './event'; -export class Orchestrator { +// class Database extends EventEmitter { +// constructor() { +// super(); +// this.emit('ready'); +// } +// } + + +export class Orchestrator extends EventEmitter { sdk: NomadContext; consumer: Consumer; indexers: Map; gov: number; - // emmiter: EventEmmiter; constructor(sdk: NomadContext, c: Consumer, gov: number) { + super(); this.sdk = sdk; this.consumer = c; this.indexers = new Map(); this.gov = gov; + } - indexAll() { - this.sdk.domainNumbers.map((domain: number) => { - this.index(domain) - }) + async indexAll() { + await Promise.all(this.sdk.domainNumbers.map((domain: number) => this.index(domain))) } - index(domain: number) { + async index(domain: number) { const existingIndexer = this.indexers.get(domain); if (existingIndexer) { existingIndexer.stop(); @@ -33,10 +42,22 @@ export class Orchestrator { if (domain === this.gov) { - indexer.startAll(this.sdk.domainNumbers.filter(d => d!=this.gov)) + await indexer.startAll(this.sdk.domainNumbers.filter(d => d!=this.gov)) } else { - indexer.startAll([this.gov]) + await indexer.startAll([this.gov]) } this.indexers.set(domain, indexer); } + + startConsuming() { + this.on('new_event', (event: NomadEvent) => { + this.consumer.consume(event); + }) + Array.from(this.indexers.values()).forEach( + indexer => { + indexer.startThrowingEvents(true) + } + ) + + } } \ No newline at end of file From 9416ca73c96550bf9f52a8b78d8f253aa0e0c3eb Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 17 Jan 2022 17:08:12 +0100 Subject: [PATCH 03/63] works, but bunch of debug junk --- .../nomad-monitor/src/indexer/consumer.ts | 276 +++++++++++++++++- .../src/indexer/customProvider.ts | 5 + typescript/nomad-monitor/src/indexer/event.ts | 89 +++++- .../nomad-monitor/src/indexer/indexer.ts | 223 ++++++++++---- typescript/nomad-monitor/src/indexer/main.ts | 12 +- .../nomad-monitor/src/indexer/orchestrator.ts | 13 +- typescript/nomad-monitor/src/indexer/utils.ts | 57 ++++ 7 files changed, 612 insertions(+), 63 deletions(-) create mode 100644 typescript/nomad-monitor/src/indexer/customProvider.ts create mode 100644 typescript/nomad-monitor/src/indexer/utils.ts diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 9f4924ae0..8fb15878f 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -1,9 +1,279 @@ -import { NomadEvent } from "./event"; +import { ethers } from "ethers"; +import { EventType, NomadEvent } from "./event"; +import fs from 'fs'; +import { replacer } from "./utils"; export abstract class Consumer { abstract consume(event: NomadEvent): void; + abstract stats(): void; } +function xxx(s: string) { + fs.appendFileSync('/tmp/lol.txt', s+'\n'); +} + +// function toBigNumber(c: TypedContainer | ethers.BigNumber): ethers.BigNumber | undefined { +// if (c instanceof ethers.BigNumber) { +// return c +// } else { +// if (c.type === 'BigNumber' && c.hex) { +// return ethers.BigNumber.from(c.hex) +// } +// return undefined +// } +// } + +// type TypedContainer = { +// type: string; +// hex?: string; +// } + + +// type HomeDispatchEvent = { +// committedRoot: string; +// messageHash: string; +// leafIndex: ethers.BigNumber; +// destinationAndNonce: ethers.BigNumber; +// message: string; +// }; + +// type HomeUpdateEvent = { +// homeDomain: number; +// oldRoot: string; +// newRoot: string; +// signature: string; +// }; + +// type ReplicaUpdateEvent = { +// homeDomain: number; +// oldRoot: string; +// newRoot: string; +// signature: string; +// }; + +// type ReplicaProcessEvent = { +// messageHash: string; +// success: boolean; +// returnData: string; +// }; + + enum MsgState { + Dispatched, + Updated, + Relayed, + Processed, + } + +class NomadMessage { + origin: number; + destination: number; + root: string; + hash: string; + leafIndex: ethers.BigNumber; + destinationAndNonce: ethers.BigNumber; + message: string; + + state: MsgState; + + constructor( + origin: number, + destination: number, + root: string, + hash: string, + leafIndex: ethers.BigNumber, + destinationAndNonce: ethers.BigNumber, + message: string + ) { + this.origin = origin; + this.destination = destination; + this.root = root; + this.hash = hash; + this.leafIndex = leafIndex; + this.destinationAndNonce = destinationAndNonce; + this.message = message; + + this.state = MsgState.Dispatched; + } + + get originAndRoot(): string { + return `${this.origin}${this.root}`; + } + } + +export class Processor extends Consumer { + messages: NomadMessage[]; + msgToIndex: Map; + msgByOriginAndRoot: Map; + consumed: number; // for debug + + constructor() { + super(); + this.messages = []; + this.msgToIndex = new Map(); + this.msgByOriginAndRoot = new Map(); + this.consumed = 0; + } + + consume(event: NomadEvent): void { + + this.kek(event); + + if (event.eventType === EventType.HomeDispatch) { + this.dispatched(event); + } else if (event.eventType === EventType.HomeUpdate) { + this.homeUpdate(event); + } else if (event.eventType === EventType.ReplicaUpdate) { + this.replicaUpdate(event); + } else if (event.eventType === EventType.ReplicaProcess) { + this.process(event); + } + + this.consumed += 1; + } + + kek(event: NomadEvent) { + if (event.eventData.messageHash === '0x12079a921d759348aa6c300c02376d66eadfac53015ba9a7db0c243826ba402c') { + xxx(`${this.consumed} matched hash: ${JSON.stringify(event, replacer)}`) + } else if (event.eventData.oldRoot === '0xe0115dc26c3c8632490cd9c6c2ce8426ab0ba0cc29fa060be691267f68571ce3') { + xxx(`${this.consumed} matched root: ${JSON.stringify(event, replacer)}`) + } + } + + dispatched(e: NomadEvent) { + // const b = e.eventData as HomeDispatchEvent; + const m = new NomadMessage( + e.domain, + e.destination(), + e.eventData.committedRoot!, + e.eventData.messageHash!, + e.eventData.leafIndex!, + e.eventData.destinationAndNonce!, + e.eventData.message! + ); + this.add(m); + } + + homeUpdate(e: NomadEvent) { + // const b = e.eventData as HomeUpdateEvent; + const m = this.getMsgByOriginAndRoot(e.domain, e.eventData.oldRoot!); + if (m) m.state = MsgState.Updated; + } + + replicaUpdate(e: NomadEvent) { + // const b = e.eventData as ReplicaUpdateEvent; + const m = this.getMsgByOriginAndRoot(e.replicaOrigin, e.eventData.oldRoot!); + if (m) m.state = MsgState.Relayed; + } + + process(e: NomadEvent) { + // const b = e.eventData as ReplicaProcessEvent; + const m = this.getMsg(e.eventData.messageHash!); + if (m) m.state = MsgState.Processed; + } + + add(m: NomadMessage) { + const index = this.messages.length; + this.msgToIndex.set(m.hash, index); + this.msgByOriginAndRoot.set(m.originAndRoot, index); + this.messages.push(m); + } + + getMsg(id: string | number): NomadMessage | undefined { + if (typeof id === "string") { + const msgIndex = this.msgToIndex.get(id); + if (msgIndex) return this.messages[msgIndex]; + } else { + return this.messages[id]; + } + return undefined; + } + + mustGetMsg(id: string | number): NomadMessage { + const m = this.getMsg(id); + if (!m) throw new Error(`Message not found`); + return m; + } + + getMsgByOriginAndRoot( + origin: number, + root: string + ): NomadMessage | undefined { + const originAndRoot = `${origin}${root}`; + const msgIndex = this.msgByOriginAndRoot.get(originAndRoot); + if (msgIndex) return this.messages[msgIndex]; + return undefined; + } + + mustGetMsgByOriginAndRoot(origin: number, root: string): NomadMessage { + const m = this.getMsgByOriginAndRoot(origin, root); + if (!m) throw new Error(`Msg not found by origin and root`); + return m; + } + + // run() { + // while (true) { + // const event = this.r.next(); + // if (!event) { + // break; + // } else if (event.eventType === EventType.HomeDispatched) { + // this.dispatched(event); + // } else if (event.eventType === EventType.HomeUpdated) { + // this.homeUpdate(event); + // } else if (event.eventType === EventType.ReplicaUpdated) { + // this.replicaUpdate(event); + // } else if (event.eventType === EventType.ReplicaProcessed) { + // this.process(event); + // } + // } + // } + + stats(): void { + let dispatched = 0; + let updated = 0; + let relayed = 0; + let processed = 0; + + this.messages.forEach((m) => { + switch (m.state) { + case MsgState.Dispatched: + dispatched += 1; + break; + case MsgState.Updated: + updated += 1; + break; + case MsgState.Relayed: + relayed += 1; + break; + case MsgState.Processed: + processed += 1; + break; + default: + break; + } + }); + console.log( + `D:`, + dispatched, + `U:`, + updated, + `R:`, + relayed, + `P:`, + processed + ); + + console.log(this.getMsg('0x12079a921d759348aa6c300c02376d66eadfac53015ba9a7db0c243826ba402c')) + + console.log(this.messages.filter(m => m.state == MsgState.Updated).slice(0,5).map(m=> m.hash)) + + // console.log(`x->\n`, this.messages.filter(m => m.state === MsgState.Dispatched)[0]); + // console.log(`1650811245->\n`, this.messages.filter(m => m.origin === 1650811245)); + // console.log(`6648936->\n`, this.messages.filter(m => m.origin === 6648936).length); + } +} + + + export class Logger extends Consumer { i: number; @@ -13,6 +283,8 @@ export class Logger extends Consumer { } consume(event: NomadEvent): void { - console.log(this.i ++, '---->', event); + // console.log(this.i ++, '---->', event); } + + stats(): void {console.log(`this.i:`, this.i)} } \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/customProvider.ts b/typescript/nomad-monitor/src/indexer/customProvider.ts new file mode 100644 index 000000000..f52bb63be --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/customProvider.ts @@ -0,0 +1,5 @@ +import { ethers } from "ethers"; + +// class CustomProvider extends ethers.providers.JsonRpcProvider { + +// } \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index 1f1a47c15..7c1205d7a 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -1,3 +1,5 @@ +import { ethers } from "ethers"; + export enum ContractType { Home = 'home', Replica = 'replica', @@ -10,16 +12,37 @@ export enum EventType { ReplicaProcess = 'replicaProcess', } +export enum EventSource { + Past = 'past', + New = 'new', + Storage = 'storage', +} + +export type EventData = { + messageHash?: string; + leafIndex?: ethers.BigNumber; + destinationAndNonce?: ethers.BigNumber; + committedRoot?: string; + oldRoot?: string; + newRoot?: string; + success?: boolean; + returnData?: ethers.utils.BytesLike; + message?: string; + signature?: string; + homeDomain?: number; +} + export class NomadEvent { domain: number; eventType: EventType; contractType: ContractType; replicaOrigin: number; ts: number; - eventData: Object; + eventData: EventData; block: number; + source: EventSource - constructor(domain: number, eventType: EventType, contractType: ContractType, replicaOrigin: number, ts: number, eventData: Object, block: number) { + constructor(domain: number, eventType: EventType, contractType: ContractType, replicaOrigin: number, ts: number, eventData: EventData, block: number, source: EventSource) { this.domain = domain; this.eventType = eventType; this.contractType = contractType; @@ -27,6 +50,66 @@ export class NomadEvent { this.ts = ts; this.eventData = eventData; this.block = block; - console.log(`New event at block`, block, new Date(ts)); + this.source = source; + // console.log(`New event '${eventType}' at block`, block, new Date(ts)); + } + + destination(): number { + if (this.eventType !== EventType.HomeDispatch) { + throw new Error(`Destination method is not availiable for non home-dispatch`) + } + // const eventData = this.eventData as {destinationAndNonce: ethers.BigNumber}; + const [destination] = parseDestinationAndNonce(this.eventData.destinationAndNonce!); + return destination + } + + toObject() { + return { + domain: this.domain, + eventType: this.eventType, + contractType: this.contractType, + replicaOrigin: this.replicaOrigin, + ts: this.ts, + eventData: this.eventData, + block: this.block, + source: EventSource.Storage, + } + } + + static fromObject(v: any): NomadEvent { + const e = v as { + domain: number, + eventType: EventType, + contractType: ContractType, + replicaOrigin: number, + ts: number, + eventData: EventData, + block: number, + }; + return new NomadEvent( + e.domain, + e.eventType, + e.contractType, + e.replicaOrigin, + e.ts, + e.eventData, + e.block, + EventSource.Storage + ) + } +} + +function parseDestinationAndNonce(h: ethers.BigNumber | { hex: string }): [number, number] { + let hexString = ''; + if (h instanceof ethers.BigNumber) { + hexString = h.toHexString(); + } else { + hexString = h.hex; } + + const without0x = hexString.slice(2); + const destinationLength = without0x.length - 8; + const destinationHex = ethers.BigNumber.from('0x' + without0x.slice(0, destinationLength)); + const nonceHex = ethers.BigNumber.from('0x' + without0x.slice(destinationLength)); + return [destinationHex.toNumber(), nonceHex.toNumber()] } \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index dfe952578..bad213ab1 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -1,16 +1,54 @@ import { Orchestrator } from "./orchestrator"; import { NomadContext } from '@nomad-xyz/sdk'; import fs from 'fs'; -import { ContractType, EventType, NomadEvent } from "./event"; +import { ContractType, EventType, NomadEvent, EventSource } from "./event"; import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; import { ethers } from "ethers"; +import { replacer, retry, reviver, sleep } from "./utils"; + + +function xxx(s: string) { + fs.appendFileSync('/tmp/kek.txt', s+'\n'); +} + +class KVCache { + m: Map; + path: string; + + constructor(path: string) { + this.m = new Map(); + this.path = path; + this.tryLoad(); + } + + save() { + fs.writeFileSync(this.path, JSON.stringify(this.m, replacer)); + } + + tryLoad() { + try { + this.m = JSON.parse(fs.readFileSync(this.path, 'utf8'), reviver); + } catch(_) { + + } + } + + set(k: K, v: V) { + this.m.set(k, v); + this.save() + } + + get(k: K): V | undefined { + return this.m.get(k); + } +} export class Indexer { domain: number; sdk: NomadContext; orchestrator: Orchestrator; persistance: Persistance; - block2timeCache: Map; + block2timeCache: KVCache; provider: ethers.providers.Provider; upToDate: boolean; eventCallback: undefined | ((event: NomadEvent) => void); @@ -21,24 +59,29 @@ export class Indexer { this.orchestrator = orchestrator; const loadPersistance = fs.existsSync(`/tmp/persistance_${this.domain}.json`); // true; this.persistance = loadPersistance ? this.loadPersistance() : new RamPersistance(`/tmp/persistance_${this.domain}.json`); - console.log(`Persistance:`, this.persistance); + // console.log(`Persistance:`, this.persistance); const it = (this.persistance as RamPersistance).iter(); let i = 0; - for (const event of it) { - console.log(`---> ${event.ts} - ${event.block} - ${event.domain} - ${event.eventType} - ${event.replicaOrigin}`, new Date(event.ts*1000)); + for (const _ of it) { + // console.log(`---> ${event.ts} - ${event.block} - ${event.domain} - ${event.eventType} - ${event.replicaOrigin}`, new Date(event.ts*1000)); i += 1; } console.log(`events found:`, i) - this.block2timeCache = new Map(); + this.block2timeCache = new KVCache(`/tmp/blocktime_cache_${this.domain}`); this.provider = this.sdk.getProvider(domain)!; this.upToDate = false; } - async getTimeForBlock(block: number) { - const possibleTime = this.block2timeCache.get(block); + async getTimeForBlock(blockNumber: number) { + const possibleTime = this.block2timeCache.get(blockNumber); if (possibleTime) return possibleTime; - const time = (await this.provider.getBlock(block)).timestamp * 1000; - this.block2timeCache.set(block, time); + const [block, error] = await retry(async () => await this.provider.getBlock(blockNumber), 5); + if (!block) { + throw error || new Error(`Some error happened at retrying getting the block ${blockNumber} for ${this.domain}`); + } + // const block = await this.provider.getBlock(blockNumber) + const time = (block).timestamp * 1000; + this.block2timeCache.set(blockNumber, time); return time; } @@ -55,7 +98,8 @@ export class Indexer { } get from(): number { - return this.sdk.getDomain(this.domain)?.paginate?.from || 0; //.getFrom(this.domain); + return this.persistance.from + // return this.persistance.from === -1 ? this.sdk.getDomain(this.domain)?.paginate?.from || this.persistance.from + 1 : this.persistance.from; //.getFrom(this.domain); } processEvent(event: NomadEvent) { @@ -83,7 +127,7 @@ export class Indexer { destinationAndNonce, committedRoot, message, - }, ev.blockNumber + }, ev.blockNumber, EventSource.New ) this.processEvent(eventPrepared) @@ -105,7 +149,7 @@ export class Indexer { oldRoot, newRoot, signature, - }, ev.blockNumber + }, ev.blockNumber, EventSource.New ) this.processEvent(eventPrepared) }) @@ -129,7 +173,7 @@ export class Indexer { oldRoot, newRoot, signature, - }, ev.blockNumber + }, ev.blockNumber, EventSource.New ) this.processEvent(eventPrepared) }); @@ -144,17 +188,17 @@ export class Indexer { ContractType.Replica, domain, new Date().valueOf(), { messageHash, success, returnData, - }, ev.blockNumber + }, ev.blockNumber, EventSource.New ) this.processEvent(eventPrepared) }) } async startAll(replicas: number[]) { // , past: number - let from = this.persistance.height + 1; + let from = Math.max(this.persistance.height + 1, this.sdk.getDomain(this.domain)?.paginate?.from || 0); const to = await this.provider.getBlockNumber(); // from = to - past; - console.log(`Wat to fetch from`, from, `to`, to); + console.log(`Want to fetch from`, from, `to`, to, `for`, this.domain); // console.log(this.domain, `starting from height`, from, `but actually from to`, to, `- 1000`, to - 1000); this.subscribeAll(replicas); await this.fetchAll(from, to, replicas); @@ -175,11 +219,106 @@ export class Indexer { } async fetchAll(from: number, to: number, replicas: number[]) { - await this.fetchHome(from, to); - await Promise.all(replicas.map(r => this.fetchReplica(r, from, to))); + + if (this.domain === 1337) { // 1650811245 + xxx(`from sdk: ${this.sdk.getDomain(1650811245)?.paginate?.from}, ${this.from}, pf:${this.persistance.from}, plast: ${Array.from((this.persistance as RamPersistance).iter()).reduce((acc, v) => Math.max(acc, v.block), 0)} current: ${await this.provider.getBlockNumber()}`) + console.log(`Fetching batched for`, this.domain); + const batchSize = 1500; + + let batchedFrom = from; + let batchedTo = batchedFrom + batchSize; + + while (batchedTo <= to) { + if (batchedTo > to) { + batchedTo = to + } + console.log(`Fetching batch`, batchedFrom, `-`, batchedTo); + xxx(`Fetching batch ${batchedFrom} - ${batchedTo}`); + const [_, error] = await retry(async () => { + await this.fetchHome(batchedFrom, batchedTo); + await Promise.all(replicas.map(r => this.fetchReplica(r, batchedFrom, batchedTo))); + }, 5); + + if (error) throw error; + + batchedFrom += batchSize; + batchedTo += batchSize; + await sleep(6000); + } + } else { + console.log(`Fetching norma for`, this.domain); + // await this.fetchHome(from, to); + // await Promise.all(replicas.map(r => this.fetchReplica(r, from, to))); + const [_, error] = await retry(async () => { + await this.fetchHome(from, to); + await Promise.all(replicas.map(r => this.fetchReplica(r, from, to))); + }, 5); + + if (error) throw error; + } + + + this.persistance.sortSorage(); this.savePersistance(); + + // TODO: either drop or make better + const p = this.persistance as RamPersistance; + const h = new Map(); + const r = new Map(); + + let h1 = ''; + let ht = Number.MAX_VALUE; + let r1 = ''; + let rt = Number.MAX_VALUE; + let rtotal = 0; + let htotal = 0; + + for (const event of p.iter()) { + if (event.eventType == EventType.HomeUpdate) { + const e = event.eventData as {oldRoot: string, newRoot: string}; + h.set(e.oldRoot, e.newRoot); + htotal += 1; + if (event.ts < ht) { + ht = event.ts; + h1 = e.oldRoot; + } + } else if (event.eventType == EventType.ReplicaUpdate) { + const e = event.eventData as {oldRoot: string, newRoot: string}; + r.set(e.oldRoot, e.newRoot); + rtotal += 1; + if (event.ts < rt) { + rt = event.ts; + r1 = e.oldRoot; + } + } + } + + console.log(this.domain, `Home started with`, htotal, 'unresolved') + while (true) { + let newRoot = h.get(h1); + if (newRoot) { + h1 = newRoot; + htotal -= 1; + } else { + console.log(this.domain, `Home broke with`, htotal, 'unresolved') + break + } + } + while (true) { + let newRoot = r.get(r1); + if (newRoot) { + r1 = newRoot; + rtotal -= 1; + } else { + console.log(`Replica broke with`, rtotal, 'unresolved') + break + } + } + // TODO END + this.upToDate = true; + console.log(this.domain, `Fetched all`); } savePersistance() { @@ -187,7 +326,9 @@ export class Indexer { } loadPersistance(): Persistance { - return RamPersistance.loadFromFile(`/tmp/persistance_${this.domain}.json`) + const p = RamPersistance.loadFromFile(`/tmp/persistance_${this.domain}.json`); + p.sortSorage(); + return p; } async fetchHome(from: number, to: number) { @@ -205,7 +346,7 @@ export class Indexer { destinationAndNonce: event.args[2], committedRoot: event.args[3], message: event.args[4], - }, event.blockNumber + }, event.blockNumber, EventSource.Past ) )) this.persistance.store(...parsedEvents) @@ -223,22 +364,22 @@ export class Indexer { oldRoot: event.args[1], newRoot: event.args[2], signature: event.args[3], - }, event.blockNumber + }, event.blockNumber, EventSource.Past ) )); this.persistance.store(...parsedEvents) } } - throwPastEvents() { - for (const event of (this.persistance as RamPersistance).iter()) { - this.orchestrator.emit('new_event', event); - } - } + // throwPastEvents() { + // for (const event of (this.persistance as RamPersistance).iter()) { + // this.orchestrator.emit('new_event', event); + // } + // } - startThrowingEvents(past: boolean) { + startThrowingEvents() { this.eventCallback = (event: NomadEvent) => {this.orchestrator.emit('new_event', event)}; - if (past) this.throwPastEvents(); + // if (past) this.throwPastEvents(); } @@ -256,7 +397,7 @@ export class Indexer { oldRoot: event.args[1], newRoot: event.args[2], signature: event.args[3], - }, event.blockNumber + }, event.blockNumber, EventSource.Past ) )); this.persistance.store(...parsedEvents) @@ -273,7 +414,7 @@ export class Indexer { messageHash: event.args[0], success: event.args[1], returnData: event.args[2], - }, event.blockNumber + }, event.blockNumber, EventSource.Past ) )); this.persistance.store(...parsedEvents) @@ -392,26 +533,6 @@ export class RamPersistance extends Persistance { } } -function replacer(key: any, value: any): any { - if(value instanceof Map) { - return { - dataType: 'Map', - value: Array.from(value.entries()), // or with spread: value: [...value] - }; - } else { - return value; - } -} - -function reviver(key: any, value: any): any { - if(typeof value === 'object' && value !== null) { - if (value.dataType === 'Map') { - return new Map(value.value); - } - } - return value; - } - export class EventsRange implements Iterable { private _p: RamPersistance; diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index 5a15e3029..f9624825e 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -1,5 +1,5 @@ import { mainnet } from "@nomad-xyz/sdk"; -import { Logger } from "./consumer"; +import { Processor } from "./consumer"; import { Orchestrator } from "./orchestrator"; import * as dotenv from 'dotenv'; import { ethers } from "ethers"; @@ -23,16 +23,22 @@ const moonbeamRPC = "https://moonbeam.api.onfinality.io/public";//"https://rpc.a const ctx = mainnet;//NomadContext.fromDomains([ethereum, moonbeam]); const ethereumId = ctx.mustGetDomain('ethereum').id; const moonbeamId = ctx.mustGetDomain('moonbeam').id; + const p = new ethers.providers.InfuraProvider('homestead', infuraKey); + // const x = new ethers.providers.JsonRpcProvider(moonbeamRPC); + + ctx.registerProvider( ethereumId, - new ethers.providers.InfuraProvider('homestead', infuraKey) + p ); ctx.registerWalletSigner(ethereumId, signer); ctx.registerRpcProvider(moonbeamId, moonbeamRPC); ctx.registerWalletSigner(moonbeamId, signer); - const c = new Logger(); + const c = new Processor(); + + setInterval(() => c.stats(), 15000); const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0]); await o.indexAll(); diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index a7f6e364b..565faee62 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -1,7 +1,6 @@ import { NomadContext } from '@nomad-xyz/sdk'; import { Consumer } from './consumer'; -import { Indexer } from './indexer'; -// import {EventEmmiter} from 'events'; +import { Indexer, RamPersistance } from './indexer'; import {EventEmitter} from 'events'; import { NomadEvent } from './event'; @@ -53,9 +52,15 @@ export class Orchestrator extends EventEmitter { this.on('new_event', (event: NomadEvent) => { this.consumer.consume(event); }) - Array.from(this.indexers.values()).forEach( + + Array.from(this.indexers.values()).map(indexer => { + const p = (indexer.persistance as RamPersistance).iter(); + return Array.from(p); + }).flat().sort((a,b) => a.ts - b.ts).forEach(e => this.consumer.consume(e)); + + Array.from(this.indexers.values()).map( indexer => { - indexer.startThrowingEvents(true) + indexer.startThrowingEvents() } ) diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts new file mode 100644 index 000000000..5cfafe4db --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -0,0 +1,57 @@ +import { ethers } from "ethers"; +import { NomadEvent } from "./event"; + +export function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }) +} + +export async function retry(callback: () => Promise, tries: number): Promise<[T | undefined, any]> { + let timeout = 3000; + let lastError: any = undefined; + for (let attempt = 0; attempt < tries; attempt++) { + try { + return [await callback(), undefined]; + } catch(e) { + lastError = e; + await sleep(timeout * 2 ** attempt); + } + } + return [undefined, lastError] +} + + +export function replacer(key: any, value: any): any { + if(value instanceof Map) { + return { + dataType: 'Map', + value: Array.from(value.entries()), // or with spread: value: [...value] + }; + } else if(value instanceof NomadEvent) { + return { + dataType: 'NomadEvent', + value: value.toObject(), // or with spread: value: [...value] + }; + } else if(value instanceof ethers.BigNumber) { + return { + dataType: 'BigNumber', + value: value.toHexString(), // or with spread: value: [...value] + }; + } else { + return value; + } +} + +export function reviver(key: any, value: any): any { + if(typeof value === 'object' && value !== null) { + if (value.dataType === 'Map') { + return new Map(value.value); + } else if (value.dataType === 'NomadEvent') { + return NomadEvent.fromObject(value.value); + } else if (value.dataType === 'BigNumber') { + return ethers.BigNumber.from(value.value); + } + } + return value; + } From bd8f80a37d9a6495eeffc6235dc87e6056800ec6 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 17 Jan 2022 19:57:21 +0100 Subject: [PATCH 04/63] woorks --- .../nomad-monitor/src/indexer/consumer.ts | 434 +++---- .../src/indexer/customProvider.ts | 5 - typescript/nomad-monitor/src/indexer/event.ts | 207 +-- .../nomad-monitor/src/indexer/indexer.ts | 1116 +++++++++-------- typescript/nomad-monitor/src/indexer/main.ts | 67 +- .../nomad-monitor/src/indexer/orchestrator.ts | 102 +- typescript/nomad-monitor/src/indexer/utils.ts | 121 +- 7 files changed, 1004 insertions(+), 1048 deletions(-) delete mode 100644 typescript/nomad-monitor/src/indexer/customProvider.ts diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 8fb15878f..bc06d53f5 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -1,290 +1,208 @@ -import { ethers } from "ethers"; -import { EventType, NomadEvent } from "./event"; -import fs from 'fs'; -import { replacer } from "./utils"; +import { ethers } from 'ethers'; +import { EventType, NomadEvent } from './event'; export abstract class Consumer { - abstract consume(event: NomadEvent): void; - abstract stats(): void; + abstract consume(event: NomadEvent): void; + abstract stats(): void; } -function xxx(s: string) { - fs.appendFileSync('/tmp/lol.txt', s+'\n'); +enum MsgState { + Dispatched, + Updated, + Relayed, + Processed, } -// function toBigNumber(c: TypedContainer | ethers.BigNumber): ethers.BigNumber | undefined { -// if (c instanceof ethers.BigNumber) { -// return c -// } else { -// if (c.type === 'BigNumber' && c.hex) { -// return ethers.BigNumber.from(c.hex) -// } -// return undefined -// } -// } - -// type TypedContainer = { -// type: string; -// hex?: string; -// } - - -// type HomeDispatchEvent = { -// committedRoot: string; -// messageHash: string; -// leafIndex: ethers.BigNumber; -// destinationAndNonce: ethers.BigNumber; -// message: string; -// }; - -// type HomeUpdateEvent = { -// homeDomain: number; -// oldRoot: string; -// newRoot: string; -// signature: string; -// }; - -// type ReplicaUpdateEvent = { -// homeDomain: number; -// oldRoot: string; -// newRoot: string; -// signature: string; -// }; - -// type ReplicaProcessEvent = { -// messageHash: string; -// success: boolean; -// returnData: string; -// }; - - enum MsgState { - Dispatched, - Updated, - Relayed, - Processed, +class NomadMessage { + origin: number; + destination: number; + root: string; + hash: string; + leafIndex: ethers.BigNumber; + destinationAndNonce: ethers.BigNumber; + message: string; + + state: MsgState; + + constructor( + origin: number, + destination: number, + root: string, + hash: string, + leafIndex: ethers.BigNumber, + destinationAndNonce: ethers.BigNumber, + message: string, + ) { + this.origin = origin; + this.destination = destination; + this.root = root; + this.hash = hash; + this.leafIndex = leafIndex; + this.destinationAndNonce = destinationAndNonce; + this.message = message; + + this.state = MsgState.Dispatched; } -class NomadMessage { - origin: number; - destination: number; - root: string; - hash: string; - leafIndex: ethers.BigNumber; - destinationAndNonce: ethers.BigNumber; - message: string; - - state: MsgState; - - constructor( - origin: number, - destination: number, - root: string, - hash: string, - leafIndex: ethers.BigNumber, - destinationAndNonce: ethers.BigNumber, - message: string - ) { - this.origin = origin; - this.destination = destination; - this.root = root; - this.hash = hash; - this.leafIndex = leafIndex; - this.destinationAndNonce = destinationAndNonce; - this.message = message; - - this.state = MsgState.Dispatched; - } - - get originAndRoot(): string { - return `${this.origin}${this.root}`; - } + get originAndRoot(): string { + return `${this.origin}${this.root}`; } +} export class Processor extends Consumer { - messages: NomadMessage[]; - msgToIndex: Map; - msgByOriginAndRoot: Map; - consumed: number; // for debug + messages: NomadMessage[]; + msgToIndex: Map; + msgByOriginAndRoot: Map; + consumed: number; // for debug + + constructor() { + super(); + this.messages = []; + this.msgToIndex = new Map(); + this.msgByOriginAndRoot = new Map(); + this.consumed = 0; + } - constructor() { - super(); - this.messages = []; - this.msgToIndex = new Map(); - this.msgByOriginAndRoot = new Map(); - this.consumed = 0; + consume(event: NomadEvent): void { + if (event.eventType === EventType.HomeDispatch) { + this.dispatched(event); + } else if (event.eventType === EventType.HomeUpdate) { + this.homeUpdate(event); + } else if (event.eventType === EventType.ReplicaUpdate) { + this.replicaUpdate(event); + } else if (event.eventType === EventType.ReplicaProcess) { + this.process(event); } - consume(event: NomadEvent): void { + this.consumed += 1; + } - this.kek(event); + dispatched(e: NomadEvent) { + const m = new NomadMessage( + e.domain, + e.destination(), + e.eventData.committedRoot!, + e.eventData.messageHash!, + e.eventData.leafIndex!, + e.eventData.destinationAndNonce!, + e.eventData.message!, + ); + this.add(m); + } - if (event.eventType === EventType.HomeDispatch) { - this.dispatched(event); - } else if (event.eventType === EventType.HomeUpdate) { - this.homeUpdate(event); - } else if (event.eventType === EventType.ReplicaUpdate) { - this.replicaUpdate(event); - } else if (event.eventType === EventType.ReplicaProcess) { - this.process(event); - } + homeUpdate(e: NomadEvent) { + const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); + if (ms.length) ms.forEach(m => { + if (m.state == MsgState.Dispatched) m.state = MsgState.Updated; + }); + } - this.consumed += 1; - } + replicaUpdate(e: NomadEvent) { + const ms = this.getMsgsByOriginAndRoot(e.replicaOrigin, e.eventData.oldRoot!); + if (ms.length) ms.forEach(m => { + if (m.state == MsgState.Updated) m.state = MsgState.Relayed + }); + } - kek(event: NomadEvent) { - if (event.eventData.messageHash === '0x12079a921d759348aa6c300c02376d66eadfac53015ba9a7db0c243826ba402c') { - xxx(`${this.consumed} matched hash: ${JSON.stringify(event, replacer)}`) - } else if (event.eventData.oldRoot === '0xe0115dc26c3c8632490cd9c6c2ce8426ab0ba0cc29fa060be691267f68571ce3') { - xxx(`${this.consumed} matched root: ${JSON.stringify(event, replacer)}`) - } + process(e: NomadEvent) { + const m = this.getMsg(e.eventData.messageHash!); + if (m) { + if (m.state == MsgState.Relayed) m.state = MsgState.Processed; } + } - dispatched(e: NomadEvent) { - // const b = e.eventData as HomeDispatchEvent; - const m = new NomadMessage( - e.domain, - e.destination(), - e.eventData.committedRoot!, - e.eventData.messageHash!, - e.eventData.leafIndex!, - e.eventData.destinationAndNonce!, - e.eventData.message! - ); - this.add(m); + add(m: NomadMessage) { + const index = this.messages.length; + this.msgToIndex.set(m.hash, index); + const x = this.msgByOriginAndRoot.get(m.originAndRoot); + if (x) { + x.push(index) + } else { + this.msgByOriginAndRoot.set(m.originAndRoot, [index]); } + this.messages.push(m); + } - homeUpdate(e: NomadEvent) { - // const b = e.eventData as HomeUpdateEvent; - const m = this.getMsgByOriginAndRoot(e.domain, e.eventData.oldRoot!); - if (m) m.state = MsgState.Updated; - } - - replicaUpdate(e: NomadEvent) { - // const b = e.eventData as ReplicaUpdateEvent; - const m = this.getMsgByOriginAndRoot(e.replicaOrigin, e.eventData.oldRoot!); - if (m) m.state = MsgState.Relayed; - } - - process(e: NomadEvent) { - // const b = e.eventData as ReplicaProcessEvent; - const m = this.getMsg(e.eventData.messageHash!); - if (m) m.state = MsgState.Processed; + getMsg(id: string | number): NomadMessage | undefined { + if (typeof id === 'string') { + const msgIndex = this.msgToIndex.get(id); + if (msgIndex) return this.messages[msgIndex]; + } else { + return this.messages[id]; } + return undefined; + } - add(m: NomadMessage) { - const index = this.messages.length; - this.msgToIndex.set(m.hash, index); - this.msgByOriginAndRoot.set(m.originAndRoot, index); - this.messages.push(m); - } - - getMsg(id: string | number): NomadMessage | undefined { - if (typeof id === "string") { - const msgIndex = this.msgToIndex.get(id); - if (msgIndex) return this.messages[msgIndex]; - } else { - return this.messages[id]; - } - return undefined; - } - - mustGetMsg(id: string | number): NomadMessage { - const m = this.getMsg(id); - if (!m) throw new Error(`Message not found`); - return m; - } - - getMsgByOriginAndRoot( - origin: number, - root: string - ): NomadMessage | undefined { - const originAndRoot = `${origin}${root}`; - const msgIndex = this.msgByOriginAndRoot.get(originAndRoot); - if (msgIndex) return this.messages[msgIndex]; - return undefined; - } - - mustGetMsgByOriginAndRoot(origin: number, root: string): NomadMessage { - const m = this.getMsgByOriginAndRoot(origin, root); - if (!m) throw new Error(`Msg not found by origin and root`); - return m; - } - - // run() { - // while (true) { - // const event = this.r.next(); - // if (!event) { - // break; - // } else if (event.eventType === EventType.HomeDispatched) { - // this.dispatched(event); - // } else if (event.eventType === EventType.HomeUpdated) { - // this.homeUpdate(event); - // } else if (event.eventType === EventType.ReplicaUpdated) { - // this.replicaUpdate(event); - // } else if (event.eventType === EventType.ReplicaProcessed) { - // this.process(event); - // } - // } - // } - - stats(): void { - let dispatched = 0; - let updated = 0; - let relayed = 0; - let processed = 0; - - this.messages.forEach((m) => { - switch (m.state) { - case MsgState.Dispatched: - dispatched += 1; - break; - case MsgState.Updated: - updated += 1; - break; - case MsgState.Relayed: - relayed += 1; - break; - case MsgState.Processed: - processed += 1; - break; - default: - break; - } - }); - console.log( - `D:`, - dispatched, - `U:`, - updated, - `R:`, - relayed, - `P:`, - processed - ); - - console.log(this.getMsg('0x12079a921d759348aa6c300c02376d66eadfac53015ba9a7db0c243826ba402c')) +// mustGetMsg(id: string | number): NomadMessage { +// const m = this.getMsg(id); +// if (!m) throw new Error(`Message not found`); +// return m; +// } + + getMsgsByOriginAndRoot( + origin: number, + root: string, + ): NomadMessage[] { + const originAndRoot = `${origin}${root}`; + const msgIndexs = this.msgByOriginAndRoot.get(originAndRoot); + if (msgIndexs) return msgIndexs.map(msgIndex => this.messages[msgIndex]) + return []; + } - console.log(this.messages.filter(m => m.state == MsgState.Updated).slice(0,5).map(m=> m.hash)) - - // console.log(`x->\n`, this.messages.filter(m => m.state === MsgState.Dispatched)[0]); - // console.log(`1650811245->\n`, this.messages.filter(m => m.origin === 1650811245)); - // console.log(`6648936->\n`, this.messages.filter(m => m.origin === 6648936).length); +// mustGetMsgByOriginAndRoot(origin: number, root: string): NomadMessage { +// const m = this.getMsgByOriginAndRoot(origin, root); +// if (!m) throw new Error(`Msg not found by origin and root`); +// return m; +// } + + stats(): void { + let dispatched = 0; + let updated = 0; + let relayed = 0; + let processed = 0; + + this.messages.forEach((m) => { + switch (m.state) { + case MsgState.Dispatched: + dispatched += 1; + break; + case MsgState.Updated: + updated += 1; + break; + case MsgState.Relayed: + relayed += 1; + break; + case MsgState.Processed: + processed += 1; + break; + default: + break; } + }); + console.log( + `D:`, + dispatched, + `U:`, + updated, + `R:`, + relayed, + `P:`, + processed, + ); + } } - - export class Logger extends Consumer { - i: number; + i: number; - constructor() { - super(); - this.i = 0; - } + constructor() { + super(); + this.i = 0; + } - consume(event: NomadEvent): void { - // console.log(this.i ++, '---->', event); - } + consume(event: NomadEvent): void {} - stats(): void {console.log(`this.i:`, this.i)} -} \ No newline at end of file + stats(): void { + console.log(`this.i:`, this.i); + } +} diff --git a/typescript/nomad-monitor/src/indexer/customProvider.ts b/typescript/nomad-monitor/src/indexer/customProvider.ts deleted file mode 100644 index f52bb63be..000000000 --- a/typescript/nomad-monitor/src/indexer/customProvider.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ethers } from "ethers"; - -// class CustomProvider extends ethers.providers.JsonRpcProvider { - -// } \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index 7c1205d7a..d334c4ec3 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -1,115 +1,132 @@ -import { ethers } from "ethers"; +import { ethers } from 'ethers'; export enum ContractType { - Home = 'home', - Replica = 'replica', + Home = 'home', + Replica = 'replica', } export enum EventType { - HomeDispatch = 'homeDispatch', - HomeUpdate = 'homeUpdate', - ReplicaUpdate = 'replicaUpdate', - ReplicaProcess = 'replicaProcess', + HomeDispatch = 'homeDispatch', + HomeUpdate = 'homeUpdate', + ReplicaUpdate = 'replicaUpdate', + ReplicaProcess = 'replicaProcess', } export enum EventSource { - Past = 'past', - New = 'new', - Storage = 'storage', + Past = 'past', + New = 'new', + Storage = 'storage', } export type EventData = { - messageHash?: string; - leafIndex?: ethers.BigNumber; - destinationAndNonce?: ethers.BigNumber; - committedRoot?: string; - oldRoot?: string; - newRoot?: string; - success?: boolean; - returnData?: ethers.utils.BytesLike; - message?: string; - signature?: string; - homeDomain?: number; -} + messageHash?: string; + leafIndex?: ethers.BigNumber; + destinationAndNonce?: ethers.BigNumber; + committedRoot?: string; + oldRoot?: string; + newRoot?: string; + success?: boolean; + returnData?: ethers.utils.BytesLike; + message?: string; + signature?: string; + homeDomain?: number; +}; export class NomadEvent { - domain: number; - eventType: EventType; - contractType: ContractType; - replicaOrigin: number; - ts: number; - eventData: EventData; - block: number; - source: EventSource + domain: number; + eventType: EventType; + contractType: ContractType; + replicaOrigin: number; + ts: number; + eventData: EventData; + block: number; + source: EventSource; - constructor(domain: number, eventType: EventType, contractType: ContractType, replicaOrigin: number, ts: number, eventData: EventData, block: number, source: EventSource) { - this.domain = domain; - this.eventType = eventType; - this.contractType = contractType; - this.replicaOrigin = replicaOrigin; - this.ts = ts; - this.eventData = eventData; - this.block = block; - this.source = source; - // console.log(`New event '${eventType}' at block`, block, new Date(ts)); - } + constructor( + domain: number, + eventType: EventType, + contractType: ContractType, + replicaOrigin: number, + ts: number, + eventData: EventData, + block: number, + source: EventSource, + ) { + this.domain = domain; + this.eventType = eventType; + this.contractType = contractType; + this.replicaOrigin = replicaOrigin; + this.ts = source === EventSource.Past && contractType == ContractType.Home ? ts - 60000: ts // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination + this.eventData = eventData; + this.block = block; + this.source = source; + } - destination(): number { - if (this.eventType !== EventType.HomeDispatch) { - throw new Error(`Destination method is not availiable for non home-dispatch`) - } - // const eventData = this.eventData as {destinationAndNonce: ethers.BigNumber}; - const [destination] = parseDestinationAndNonce(this.eventData.destinationAndNonce!); - return destination + destination(): number { + if (this.eventType !== EventType.HomeDispatch) { + throw new Error( + `Destination method is not availiable for non home-dispatch`, + ); } + const [destination] = parseDestinationAndNonce( + this.eventData.destinationAndNonce!, + ); + return destination; + } - toObject() { - return { - domain: this.domain, - eventType: this.eventType, - contractType: this.contractType, - replicaOrigin: this.replicaOrigin, - ts: this.ts, - eventData: this.eventData, - block: this.block, - source: EventSource.Storage, - } - } + toObject() { + return { + domain: this.domain, + eventType: this.eventType, + contractType: this.contractType, + replicaOrigin: this.replicaOrigin, + ts: this.ts, + eventData: this.eventData, + block: this.block, + source: EventSource.Storage, + }; + } - static fromObject(v: any): NomadEvent { - const e = v as { - domain: number, - eventType: EventType, - contractType: ContractType, - replicaOrigin: number, - ts: number, - eventData: EventData, - block: number, - }; - return new NomadEvent( - e.domain, - e.eventType, - e.contractType, - e.replicaOrigin, - e.ts, - e.eventData, - e.block, - EventSource.Storage - ) - } + static fromObject(v: any): NomadEvent { + const e = v as { + domain: number; + eventType: EventType; + contractType: ContractType; + replicaOrigin: number; + ts: number; + eventData: EventData; + block: number; + }; + return new NomadEvent( + e.domain, + e.eventType, + e.contractType, + e.replicaOrigin, + e.ts, + e.eventData, + e.block, + EventSource.Storage, + ); + } } -function parseDestinationAndNonce(h: ethers.BigNumber | { hex: string }): [number, number] { - let hexString = ''; - if (h instanceof ethers.BigNumber) { - hexString = h.toHexString(); - } else { - hexString = h.hex; - } - - const without0x = hexString.slice(2); - const destinationLength = without0x.length - 8; - const destinationHex = ethers.BigNumber.from('0x' + without0x.slice(0, destinationLength)); - const nonceHex = ethers.BigNumber.from('0x' + without0x.slice(destinationLength)); - return [destinationHex.toNumber(), nonceHex.toNumber()] -} \ No newline at end of file +function parseDestinationAndNonce( + h: ethers.BigNumber | { hex: string }, +): [number, number] { + let hexString = ''; + if (h instanceof ethers.BigNumber) { + hexString = h.toHexString(); + } else { + hexString = h.hex; + } + + const without0x = hexString.slice(2); + const destinationLength = without0x.length - 8; + const destinationHex = ethers.BigNumber.from( + '0x' + without0x.slice(0, destinationLength), + ); + const nonceHex = ethers.BigNumber.from( + '0x' + without0x.slice(destinationLength), + ); + return [destinationHex.toNumber(), nonceHex.toNumber()]; +} diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index bad213ab1..26ed0d227 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -1,593 +1,597 @@ -import { Orchestrator } from "./orchestrator"; +import { Orchestrator } from './orchestrator'; import { NomadContext } from '@nomad-xyz/sdk'; import fs from 'fs'; -import { ContractType, EventType, NomadEvent, EventSource } from "./event"; -import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; -import { ethers } from "ethers"; -import { replacer, retry, reviver, sleep } from "./utils"; - - -function xxx(s: string) { - fs.appendFileSync('/tmp/kek.txt', s+'\n'); -} - -class KVCache { - m: Map; - path: string; - - constructor(path: string) { - this.m = new Map(); - this.path = path; - this.tryLoad(); - } - - save() { - fs.writeFileSync(this.path, JSON.stringify(this.m, replacer)); - } - - tryLoad() { - try { - this.m = JSON.parse(fs.readFileSync(this.path, 'utf8'), reviver); - } catch(_) { - - } - } - - set(k: K, v: V) { - this.m.set(k, v); - this.save() - } - - get(k: K): V | undefined { - return this.m.get(k); - } -} +import { ContractType, EventType, NomadEvent, EventSource } from './event'; +import { Home, Replica } from '@nomad-xyz/contract-interfaces/core'; +import { ethers } from 'ethers'; +import { KVCache, replacer, retry, reviver } from './utils'; export class Indexer { - domain: number; - sdk: NomadContext; - orchestrator: Orchestrator; - persistance: Persistance; - block2timeCache: KVCache; - provider: ethers.providers.Provider; - upToDate: boolean; - eventCallback: undefined | ((event: NomadEvent) => void); - - constructor(domain: number, sdk: NomadContext, orchestrator: Orchestrator) { - this.domain = domain; - this.sdk = sdk; - this.orchestrator = orchestrator; - const loadPersistance = fs.existsSync(`/tmp/persistance_${this.domain}.json`); // true; - this.persistance = loadPersistance ? this.loadPersistance() : new RamPersistance(`/tmp/persistance_${this.domain}.json`); - // console.log(`Persistance:`, this.persistance); - const it = (this.persistance as RamPersistance).iter(); - let i = 0; - for (const _ of it) { - // console.log(`---> ${event.ts} - ${event.block} - ${event.domain} - ${event.eventType} - ${event.replicaOrigin}`, new Date(event.ts*1000)); - i += 1; - } - console.log(`events found:`, i) - this.block2timeCache = new KVCache(`/tmp/blocktime_cache_${this.domain}`); - this.provider = this.sdk.getProvider(domain)!; - this.upToDate = false; - } - - async getTimeForBlock(blockNumber: number) { - const possibleTime = this.block2timeCache.get(blockNumber); - if (possibleTime) return possibleTime; - const [block, error] = await retry(async () => await this.provider.getBlock(blockNumber), 5); - if (!block) { - throw error || new Error(`Some error happened at retrying getting the block ${blockNumber} for ${this.domain}`); - } - // const block = await this.provider.getBlock(blockNumber) - const time = (block).timestamp * 1000; - this.block2timeCache.set(blockNumber, time); - return time; - } - - stop() { - // TODO: this should close all listeners - } - - async init() { - await this.persistance.init(); - } - - get height(): number { - return this.persistance.height; - } - - get from(): number { - return this.persistance.from - // return this.persistance.from === -1 ? this.sdk.getDomain(this.domain)?.paginate?.from || this.persistance.from + 1 : this.persistance.from; //.getFrom(this.domain); - } - - processEvent(event: NomadEvent) { - if (this.eventCallback != undefined) this.eventCallback(event); - this.persistance.store(event) - } - - subscribeHome() { - const home = this.home(); - home.on(home.filters.Dispatch(), async ( + domain: number; + sdk: NomadContext; + orchestrator: Orchestrator; + persistance: Persistance; + block2timeCache: KVCache; + eventCallback: undefined | ((event: NomadEvent) => void); + + constructor(domain: number, sdk: NomadContext, orchestrator: Orchestrator) { + this.domain = domain; + this.sdk = sdk; + this.orchestrator = orchestrator; + this.persistance = new RamPersistance( + `/tmp/persistance_${this.domain}.json`, + ); + this.block2timeCache = new KVCache(`/tmp/blocktime_cache_${this.domain}`); + } + + get provider(): ethers.providers.Provider { + return this.sdk.getProvider(this.domain)!; + } + + async getBlockTimestamp(blockNumber: number): Promise { + const possibleTime = this.block2timeCache.get(blockNumber); + if (possibleTime) return possibleTime; + const [block, error] = await retry( + async () => await this.provider.getBlock(blockNumber), + 5, + ); + if (!block) { + throw ( + error || + new Error( + `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}`, + ) + ); + } + const time = block.timestamp * 1000; + this.block2timeCache.set(blockNumber, time); + return time; + } + + stop() { + // TODO: this should close all listeners + } + + async init() { + await this.persistance.init(); + } + + get height(): number { + return this.persistance.height; + } + + get from(): number { + return this.persistance.from; + } + + processEvent(event: NomadEvent) { + if (this.eventCallback != undefined) this.eventCallback(event); + this.persistance.store(event); + } + + subscribeHome() { + const home = this.home(); + home.on( + home.filters.Dispatch(), + async ( + messageHash, + leafIndex, + destinationAndNonce, + committedRoot, + message, + ev, + ) => { + const eventPrepared = new NomadEvent( + this.domain, + EventType.HomeDispatch, + ContractType.Home, + 0, + new Date().valueOf(), + { messageHash, leafIndex, destinationAndNonce, committedRoot, message, - ev) => { - - const eventPrepared = new NomadEvent( - this.domain, - EventType.HomeDispatch, - ContractType.Home, - 0, new Date().valueOf(), { - messageHash, - leafIndex, - destinationAndNonce, - committedRoot, - message, - }, ev.blockNumber, EventSource.New - ) - - this.processEvent(eventPrepared) - }); - - home.on(home.filters.Update(), async ( + }, + ev.blockNumber, + EventSource.New, + ); + + this.processEvent(eventPrepared); + }, + ); + + home.on( + home.filters.Update(), + async (homeDomain, oldRoot, newRoot, signature, ev) => { + const eventPrepared = new NomadEvent( + this.domain, + EventType.HomeUpdate, + ContractType.Home, + 0, + new Date().valueOf(), + { homeDomain, oldRoot, newRoot, signature, - ev) => { - - const eventPrepared = new NomadEvent( - this.domain, - EventType.HomeUpdate, - ContractType.Home, - 0, new Date().valueOf(), { - homeDomain, - oldRoot, - newRoot, - signature, - }, ev.blockNumber, EventSource.New - ) - this.processEvent(eventPrepared) - }) - } - - subscribeReplica(domain: number) { - const replica = this.replicaForDomain(domain); - replica.on(replica.filters.Update(), async ( + }, + ev.blockNumber, + EventSource.New, + ); + this.processEvent(eventPrepared); + }, + ); + } + + subscribeReplica(domain: number) { + const replica = this.replicaForDomain(domain); + replica.on( + replica.filters.Update(), + async (homeDomain, oldRoot, newRoot, signature, ev) => { + const eventPrepared = new NomadEvent( + this.domain, + EventType.ReplicaUpdate, + ContractType.Replica, + domain, + new Date().valueOf(), + { homeDomain, oldRoot, newRoot, signature, - ev) => { - - const eventPrepared = new NomadEvent( - this.domain, - EventType.ReplicaUpdate, - ContractType.Replica, - domain, new Date().valueOf(), { - homeDomain, - oldRoot, - newRoot, - signature, - }, ev.blockNumber, EventSource.New - ) - this.processEvent(eventPrepared) - }); - - replica.on(replica.filters.Process(), async ( - messageHash, success, returnData,ev - ) => { - - const eventPrepared = new NomadEvent( - this.domain, - EventType.ReplicaProcess, - ContractType.Replica, - domain, new Date().valueOf(), { - messageHash, success, returnData, - }, ev.blockNumber, EventSource.New - ) - this.processEvent(eventPrepared) - }) - } - - async startAll(replicas: number[]) { // , past: number - let from = Math.max(this.persistance.height + 1, this.sdk.getDomain(this.domain)?.paginate?.from || 0); - const to = await this.provider.getBlockNumber(); - // from = to - past; - console.log(`Want to fetch from`, from, `to`, to, `for`, this.domain); - // console.log(this.domain, `starting from height`, from, `but actually from to`, to, `- 1000`, to - 1000); - this.subscribeAll(replicas); - await this.fetchAll(from, to, replicas); - - } - - subscribeAll(replicas: number[]) { - this.subscribeHome(); - replicas.forEach(r => this.subscribeReplica(r)) - } - - home(): Home { - return this.sdk.getCore(this.domain)!.home; //getHomeAtDomain(this.domain); - } - - replicaForDomain(domain: number): Replica { - return this.sdk.getReplicaFor(domain, this.domain)!; - } - - async fetchAll(from: number, to: number, replicas: number[]) { - - if (this.domain === 1337) { // 1650811245 - xxx(`from sdk: ${this.sdk.getDomain(1650811245)?.paginate?.from}, ${this.from}, pf:${this.persistance.from}, plast: ${Array.from((this.persistance as RamPersistance).iter()).reduce((acc, v) => Math.max(acc, v.block), 0)} current: ${await this.provider.getBlockNumber()}`) - console.log(`Fetching batched for`, this.domain); - const batchSize = 1500; - - let batchedFrom = from; - let batchedTo = batchedFrom + batchSize; - - while (batchedTo <= to) { - if (batchedTo > to) { - batchedTo = to - } - console.log(`Fetching batch`, batchedFrom, `-`, batchedTo); - xxx(`Fetching batch ${batchedFrom} - ${batchedTo}`); - const [_, error] = await retry(async () => { - await this.fetchHome(batchedFrom, batchedTo); - await Promise.all(replicas.map(r => this.fetchReplica(r, batchedFrom, batchedTo))); - }, 5); - - if (error) throw error; - - batchedFrom += batchSize; - batchedTo += batchSize; - await sleep(6000); - } - } else { - console.log(`Fetching norma for`, this.domain); - // await this.fetchHome(from, to); - // await Promise.all(replicas.map(r => this.fetchReplica(r, from, to))); - const [_, error] = await retry(async () => { - await this.fetchHome(from, to); - await Promise.all(replicas.map(r => this.fetchReplica(r, from, to))); - }, 5); - - if (error) throw error; - } - - - - this.persistance.sortSorage(); - this.savePersistance(); - - // TODO: either drop or make better - const p = this.persistance as RamPersistance; - const h = new Map(); - const r = new Map(); - - let h1 = ''; - let ht = Number.MAX_VALUE; - let r1 = ''; - let rt = Number.MAX_VALUE; - let rtotal = 0; - let htotal = 0; - - for (const event of p.iter()) { - if (event.eventType == EventType.HomeUpdate) { - const e = event.eventData as {oldRoot: string, newRoot: string}; - h.set(e.oldRoot, e.newRoot); - htotal += 1; - if (event.ts < ht) { - ht = event.ts; - h1 = e.oldRoot; - } - } else if (event.eventType == EventType.ReplicaUpdate) { - const e = event.eventData as {oldRoot: string, newRoot: string}; - r.set(e.oldRoot, e.newRoot); - rtotal += 1; - if (event.ts < rt) { - rt = event.ts; - r1 = e.oldRoot; - } - } - } - - console.log(this.domain, `Home started with`, htotal, 'unresolved') - while (true) { - let newRoot = h.get(h1); - if (newRoot) { - h1 = newRoot; - htotal -= 1; - } else { - console.log(this.domain, `Home broke with`, htotal, 'unresolved') - break - } - } - while (true) { - let newRoot = r.get(r1); - if (newRoot) { - r1 = newRoot; - rtotal -= 1; - } else { - console.log(`Replica broke with`, rtotal, 'unresolved') - break - } - } - // TODO END - - this.upToDate = true; - console.log(this.domain, `Fetched all`); - } - - savePersistance() { - (this.persistance as RamPersistance).saveToFile(); - } - - loadPersistance(): Persistance { - const p = RamPersistance.loadFromFile(`/tmp/persistance_${this.domain}.json`); - p.sortSorage(); - return p; - } - - async fetchHome(from: number, to: number) { - const home = this.home(); - { - const events = await home.queryFilter(home.filters.Dispatch(), from, to); - const parsedEvents = await Promise.all(events.map(async (event) => - new NomadEvent( - this.domain, - EventType.HomeDispatch, - ContractType.Home, - 0, await this.getTimeForBlock(event.blockNumber), { - messageHash: event.args[0], - leafIndex: event.args[1], - destinationAndNonce: event.args[2], - committedRoot: event.args[3], - message: event.args[4], - }, event.blockNumber, EventSource.Past - ) - )) - this.persistance.store(...parsedEvents) - } - - { - const events = await home.queryFilter(home.filters.Update(), from, to); - const parsedEvents = await Promise.all(events.map(async (event) => - new NomadEvent( - this.domain, - EventType.HomeUpdate, - ContractType.Home, - 0, await this.getTimeForBlock(event.blockNumber), { - homeDomain: event.args[0], - oldRoot: event.args[1], - newRoot: event.args[2], - signature: event.args[3], - }, event.blockNumber, EventSource.Past - ) - )); - this.persistance.store(...parsedEvents) - } - } - - // throwPastEvents() { - // for (const event of (this.persistance as RamPersistance).iter()) { - // this.orchestrator.emit('new_event', event); - // } - // } - - startThrowingEvents() { - this.eventCallback = (event: NomadEvent) => {this.orchestrator.emit('new_event', event)}; - // if (past) this.throwPastEvents(); - - } - - async fetchReplica(domain: number, from: number, to: number) { - const replica = this.replicaForDomain(domain); - { - const events = await replica.queryFilter(replica.filters.Update(), from, to); - const parsedEvents = await Promise.all(events.map(async (event) => - new NomadEvent( - this.domain, - EventType.ReplicaUpdate, - ContractType.Replica, - domain, await this.getTimeForBlock(event.blockNumber), { - homeDomain: event.args[0], - oldRoot: event.args[1], - newRoot: event.args[2], - signature: event.args[3], - }, event.blockNumber, EventSource.Past - ) - )); - this.persistance.store(...parsedEvents) + }, + ev.blockNumber, + EventSource.New, + ); + this.processEvent(eventPrepared); + }, + ); + + replica.on( + replica.filters.Process(), + async (messageHash, success, returnData, ev) => { + const eventPrepared = new NomadEvent( + this.domain, + EventType.ReplicaProcess, + ContractType.Replica, + domain, + new Date().valueOf(), + { + messageHash, + success, + returnData, + }, + ev.blockNumber, + EventSource.New, + ); + this.processEvent(eventPrepared); + }, + ); + } + + async startAll(replicas: number[]) { + // , past: number + let from = Math.max( + this.persistance.height + 1, + this.sdk.getDomain(this.domain)?.paginate?.from || 0, + ); + const to = await this.provider.getBlockNumber(); + // from = to - past; + console.log(`Want to fetch from`, from, `to`, to, `for`, this.domain); + // console.log(this.domain, `starting from height`, from, `but actually from to`, to, `- 1000`, to - 1000); + this.subscribeAll(replicas); + await this.fetchAll(from, to, replicas); + } + + subscribeAll(replicas: number[]) { + this.subscribeHome(); + replicas.forEach((r) => this.subscribeReplica(r)); + } + + home(): Home { + return this.sdk.getCore(this.domain)!.home; //getHomeAtDomain(this.domain); + } + + replicaForDomain(domain: number): Replica { + return this.sdk.getReplicaFor(domain, this.domain)!; + } + + async fetchAll(from: number, to: number, replicas: number[]) { + console.log(`Fetching for`, this.domain); + const [_, error] = await retry(async () => { + await this.fetchHome(from, to); + await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))); + }, 5); + + if (error) throw error; + + this.persistance.sortSorage(); + this.savePersistance(); + + // TODO mb remove or fix + this.dummyTestEventsIntegrity(); + // TODO END + + console.log(this.domain, `Fetched all`); + } + + dummyTestEventsIntegrity() { + // TODO: either drop or make better + // const p = this.persistance as RamPersistance; + const h = new Map(); + const r = new Map(); + + let h1 = ''; + let ht = Number.MAX_VALUE; + let r1 = ''; + let rt = Number.MAX_VALUE; + let rtotal = 0; + let htotal = 0; + + let allEvents = this.persistance.allEvents(); + if (allEvents.length === 0) { + console.warn(`No events to test integrity!!!`); + return; + } + + for (const event of allEvents) { + if (event.eventType == EventType.HomeUpdate) { + const e = event.eventData as { oldRoot: string; newRoot: string }; + h.set(e.oldRoot, e.newRoot); + htotal += 1; + if (event.ts < ht) { + ht = event.ts; + h1 = e.oldRoot; } - - { - const events = await replica.queryFilter(replica.filters.Process(), from, to); - const parsedEvents = await Promise.all(events.map(async (event) => - new NomadEvent( - this.domain, - EventType.ReplicaProcess, - ContractType.Replica, - domain, await this.getTimeForBlock(event.blockNumber), { - messageHash: event.args[0], - success: event.args[1], - returnData: event.args[2], - }, event.blockNumber, EventSource.Past - ) - )); - this.persistance.store(...parsedEvents) + } else if (event.eventType == EventType.ReplicaUpdate) { + const e = event.eventData as { oldRoot: string; newRoot: string }; + r.set(e.oldRoot, e.newRoot); + rtotal += 1; + if (event.ts < rt) { + rt = event.ts; + r1 = e.oldRoot; } - } - + } + } + + if (htotal <= 0) throw new Error(`THis is not supposed to be 0`); + if (rtotal <= 0) throw new Error(`THis is not supposed to be 0`); + + while (true) { + let newRoot = h.get(h1); + if (newRoot) { + h1 = newRoot; + htotal -= 1; + } else { + console.log(this.domain, `Home broke with`, htotal, 'unresolved'); + break; + } + } + while (true) { + let newRoot = r.get(r1); + if (newRoot) { + r1 = newRoot; + rtotal -= 1; + } else { + console.log(`Replica broke with`, rtotal, 'unresolved'); + break; + } + } + + if (htotal !== 0) throw new Error(`THis supposed to be 0`); + if (rtotal !== 0) throw new Error(`THis supposed to be 0`); + } + + savePersistance() { + this.persistance.persist(); + } + + startThrowingEvents() { + this.eventCallback = (event: NomadEvent) => { + this.orchestrator.emit('new_event', event); + }; + } + + async fetchHome(from: number, to: number) { + const home = this.home(); + { + const events = await home.queryFilter(home.filters.Dispatch(), from, to); + const parsedEvents = await Promise.all( + events.map( + async (event) => + new NomadEvent( + this.domain, + EventType.HomeDispatch, + ContractType.Home, + 0, + await this.getBlockTimestamp(event.blockNumber), + { + messageHash: event.args[0], + leafIndex: event.args[1], + destinationAndNonce: event.args[2], + committedRoot: event.args[3], + message: event.args[4], + }, + event.blockNumber, + EventSource.Past, + ), + ), + ); + this.persistance.store(...parsedEvents); + } + + { + const events = await home.queryFilter(home.filters.Update(), from, to); + const parsedEvents = await Promise.all( + events.map( + async (event) => + new NomadEvent( + this.domain, + EventType.HomeUpdate, + ContractType.Home, + 0, + await this.getBlockTimestamp(event.blockNumber), + { + homeDomain: event.args[0], + oldRoot: event.args[1], + newRoot: event.args[2], + signature: event.args[3], + }, + event.blockNumber, + EventSource.Past, + ), + ), + ); + this.persistance.store(...parsedEvents); + } + } + + async fetchReplica(domain: number, from: number, to: number) { + const replica = this.replicaForDomain(domain); + { + const events = await replica.queryFilter( + replica.filters.Update(), + from, + to, + ); + const parsedEvents = await Promise.all( + events.map( + async (event) => + new NomadEvent( + this.domain, + EventType.ReplicaUpdate, + ContractType.Replica, + domain, + await this.getBlockTimestamp(event.blockNumber), + { + homeDomain: event.args[0], + oldRoot: event.args[1], + newRoot: event.args[2], + signature: event.args[3], + }, + event.blockNumber, + EventSource.Past, + ), + ), + ); + this.persistance.store(...parsedEvents); + } + + { + const events = await replica.queryFilter( + replica.filters.Process(), + from, + to, + ); + const parsedEvents = await Promise.all( + events.map( + async (event) => + new NomadEvent( + this.domain, + EventType.ReplicaProcess, + ContractType.Replica, + domain, + await this.getBlockTimestamp(event.blockNumber), + { + messageHash: event.args[0], + success: event.args[1], + returnData: event.args[2], + }, + event.blockNumber, + EventSource.Past, + ), + ), + ); + this.persistance.store(...parsedEvents); + } + } } export abstract class Persistance { - initiated: boolean; - from: number; - height: number; - constructor() { - this.initiated = false; - this.from = -1; - this.height = -1; - } - - abstract store(...events: NomadEvent[]): void; - abstract init(): Promise; - abstract sortSorage(): void; + initiated: boolean; + from: number; + height: number; + constructor() { + this.initiated = false; + this.from = -1; + this.height = -1; + } + + abstract store(...events: NomadEvent[]): void; + abstract init(): Promise; + abstract sortSorage(): void; + abstract allEvents(): NomadEvent[]; + abstract persist(): void; } export class FilePersistance extends Persistance { - path: string; - constructor( path: string) { - super(); - this.path = path; - } - - store(...events: NomadEvent[]): void {} - async init(): Promise { - if (fs.existsSync(this.path)) { - // load from the storage - this.from = 13; - this.height = 14; - return true; - } else { - return false; - } - } - sortSorage(){} + path: string; + constructor(path: string) { + super(); + this.path = path; + } + + store(...events: NomadEvent[]): void {} + async init(): Promise { + if (fs.existsSync(this.path)) { + // load from the storage + this.from = 13; + this.height = 14; + return true; + } else { + return false; + } + } + sortSorage() {} + allEvents(): NomadEvent[] { + return []; + } + persist(): void {} } export class RamPersistance extends Persistance { - block2events: Map; - blocks: number[]; - storePath: string; - - constructor(storePath: string) { - super(); - this.block2events = new Map(); - this.blocks = []; - this.storePath = storePath; - } - - updateFromTo(block: number) { - if (block < this.from || this.from === -1) this.from = block; - if (block > this.height || this.height === -1) this.height = block; - } - - store(...events: NomadEvent[]): void { - events.forEach(event => { - this.updateFromTo(event.block); - const block = this.block2events.get(event.block); - if (block) { - block.push(event) - } else { - this.block2events.set(event.block, [event]); - } - // if (this.height < event.block) { - // this.height = event.block; - // this.blocks.push(event.block); - // this.blocks = this.blocks.sort(); - // } - if (this.blocks.indexOf(event.block) < 0) { - this.blocks.push(event.block); - this.blocks = this.blocks.sort(); - } - }); - this.saveToFile(); - } - async init(): Promise { - return true; - } - sortSorage(){ + block2events: Map; + blocks: number[]; + storePath: string; + + constructor(storePath: string) { + super(); + this.block2events = new Map(); + this.blocks = []; + this.storePath = storePath; + try { + this.load(); + } catch (_) {} + } + + updateFromTo(block: number) { + if (block < this.from || this.from === -1) this.from = block; + if (block > this.height || this.height === -1) this.height = block; + } + + store(...events: NomadEvent[]): void { + events.forEach((event) => { + this.updateFromTo(event.block); + const block = this.block2events.get(event.block); + if (block) { + block.push(event); + } else { + this.block2events.set(event.block, [event]); + } + if (this.blocks.indexOf(event.block) < 0) { + this.blocks.push(event.block); this.blocks = this.blocks.sort(); - } - - iter(): EventsRange { - return new EventsRange(this); - } - - saveToFile() { - fs.writeFileSync(this.storePath, JSON.stringify({ - block2events: this.block2events, - blocks: this.blocks, - initiated: this.initiated, - from: this.from, - height: this.height, - storePath: this.storePath, - }, replacer)) - } - - static loadFromFile(storePath: string): RamPersistance { - const object = JSON.parse(fs.readFileSync(storePath, 'utf8'), reviver) as {block2events: Map, blocks: number[], initiated: boolean, - from: number, - height: number}; - const p = new RamPersistance(storePath); - p.block2events = object.block2events; - p.blocks = object.blocks; - p.initiated = object.initiated; - p.from = object.from; - p.height = object.height; - return p; - } + } + }); + this.persist(); + } + async init(): Promise { + return true; + } + sortSorage() { + this.blocks = this.blocks.sort(); + } + + iter(): EventsRange { + return new EventsRange(this); + } + + persist() { + fs.writeFileSync( + this.storePath, + JSON.stringify( + { + block2events: this.block2events, + blocks: this.blocks, + initiated: this.initiated, + from: this.from, + height: this.height, + storePath: this.storePath, + }, + replacer, + ), + ); + } + + load() { + const object = JSON.parse( + fs.readFileSync(this.storePath, 'utf8'), + reviver, + ) as { + block2events: Map; + blocks: number[]; + initiated: boolean; + from: number; + height: number; + }; + this.block2events = object.block2events; + this.blocks = object.blocks; + this.initiated = object.initiated; + this.from = object.from; + this.height = object.height; + } + + allEvents(): NomadEvent[] { + return Array.from(this.iter()); + } } - export class EventsRange implements Iterable { - private _p: RamPersistance; - private _cacheBlockIndex: number; - private _position: number; - private nextDone: boolean; - - constructor(p: RamPersistance) { - this._p = p; - this._cacheBlockIndex = 0; - this._position = 0; - this.nextDone = false; - } - cachedBlockIndex(index: number): number | undefined { - return this._p.blocks.at(index) - } - - next(value?: any): IteratorResult { - if (this.nextDone) return {done: true, value: undefined}; - let done = false; - const blockNumber = this.cachedBlockIndex(this._cacheBlockIndex); - if (!blockNumber) { - return {done: true, value: undefined}; - } - const block = this._p.block2events.get(blockNumber); - if (!block) { - return {done: true, value: undefined}; - } - let _value = block.at(this._position)!; - - // calculating next positions - if (this._position + 1 < block.length) { - this._position += 1; + private _p: RamPersistance; + private _cacheBlockIndex: number; + private _position: number; + private nextDone: boolean; + + constructor(p: RamPersistance) { + this._p = p; + this._cacheBlockIndex = 0; + this._position = 0; + this.nextDone = false; + } + cachedBlockIndex(index: number): number | undefined { + return this._p.blocks.at(index); + } + + next(value?: any): IteratorResult { + if (this.nextDone) return { done: true, value: undefined }; + let done = false; + const blockNumber = this.cachedBlockIndex(this._cacheBlockIndex); + if (!blockNumber) { + return { done: true, value: undefined }; + } + const block = this._p.block2events.get(blockNumber); + if (!block) { + return { done: true, value: undefined }; + } + let _value = block.at(this._position)!; + + // calculating next positions + if (this._position + 1 < block.length) { + this._position += 1; + } else { + const nextIndex = this._cacheBlockIndex + 1; + const nextBlockNumber = this.cachedBlockIndex(nextIndex); + if (!nextBlockNumber) { + this.nextDone = true; + } else { + if (this._p.block2events.get(nextBlockNumber)) { + this._cacheBlockIndex = nextIndex; + this._position = 0; } else { - const nextIndex = this._cacheBlockIndex + 1; - const nextBlockNumber = this.cachedBlockIndex(nextIndex); - if (!nextBlockNumber) { - this.nextDone = true; - } else { - if (this._p.block2events.get(nextBlockNumber)) { - this._cacheBlockIndex = nextIndex; - this._position = 0; - } else { - this.nextDone = true; - } - } + this.nextDone = true; } - - return { - done, - value: _value - }; + } } - [Symbol.iterator]() { - return this; - } + return { + done, + value: _value, + }; + } + + [Symbol.iterator]() { + return this; + } } diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index f9624825e..165fcaa5e 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -1,46 +1,39 @@ -import { mainnet } from "@nomad-xyz/sdk"; -import { Processor } from "./consumer"; -import { Orchestrator } from "./orchestrator"; +import { mainnet } from '@nomad-xyz/sdk'; +import { Processor } from './consumer'; +import { Orchestrator } from './orchestrator'; import * as dotenv from 'dotenv'; -import { ethers } from "ethers"; +import { ethers } from 'ethers'; dotenv.config({ - // path: '/Users/daniilnaumetc/code/tmp/monitor/typescript/nomad-monitor/src/indexer/.env' + // path: '/Users/daniilnaumetc/code/tmp/monitor/typescript/nomad-monitor/src/indexer/.env' }); -const signer = - process.env.SIGNER!; -const alchemyKey = - process.env.ALCHEMY_KEY!; -const infuraKey = - process.env.INFURA_KEY!; +const signer = process.env.SIGNER!; +const alchemyKey = process.env.ALCHEMY_KEY!; +const infuraKey = process.env.INFURA_KEY!; -console.log(signer, alchemyKey) +console.log(signer, alchemyKey); -const moonbeamRPC = "https://moonbeam.api.onfinality.io/public";//"https://rpc.api.moonbeam.network"; +const moonbeamRPC = 'https://moonbeam.api.onfinality.io/public'; //"https://rpc.api.moonbeam.network"; // const ethereumRPC = `https://eth-mainnet.alchemyapi.io/v2/${alchemyKey}`; (async () => { - const ctx = mainnet;//NomadContext.fromDomains([ethereum, moonbeam]); - const ethereumId = ctx.mustGetDomain('ethereum').id; - const moonbeamId = ctx.mustGetDomain('moonbeam').id; - const p = new ethers.providers.InfuraProvider('homestead', infuraKey); - // const x = new ethers.providers.JsonRpcProvider(moonbeamRPC); - - - - ctx.registerProvider( - ethereumId, - p - ); - ctx.registerWalletSigner(ethereumId, signer); - - ctx.registerRpcProvider(moonbeamId, moonbeamRPC); - ctx.registerWalletSigner(moonbeamId, signer); - const c = new Processor(); - - setInterval(() => c.stats(), 15000); - const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0]); - await o.indexAll(); - - o.startConsuming(); -})(); \ No newline at end of file + const ctx = mainnet; //NomadContext.fromDomains([ethereum, moonbeam]); + const ethereumId = ctx.mustGetDomain('ethereum').id; + const moonbeamId = ctx.mustGetDomain('moonbeam').id; + const p = new ethers.providers.InfuraProvider('homestead', infuraKey); + + ctx.registerProvider(ethereumId, p); + ctx.registerWalletSigner(ethereumId, signer); + + ctx.registerRpcProvider(moonbeamId, moonbeamRPC); + ctx.registerWalletSigner(moonbeamId, signer); + const c = new Processor(); + + setInterval(() => c.stats(), 15000); + const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0]); + await o.indexAll(); + + o.startConsuming(); + + console.log(`--->`, c.messages.filter(m=>m.state == 1)); +})(); diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index 565faee62..e8126b3ee 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -1,68 +1,64 @@ import { NomadContext } from '@nomad-xyz/sdk'; import { Consumer } from './consumer'; -import { Indexer, RamPersistance } from './indexer'; -import {EventEmitter} from 'events'; +import { Indexer } from './indexer'; +import { EventEmitter } from 'events'; import { NomadEvent } from './event'; -// class Database extends EventEmitter { -// constructor() { -// super(); -// this.emit('ready'); -// } -// } - - export class Orchestrator extends EventEmitter { - sdk: NomadContext; - consumer: Consumer; - indexers: Map; - gov: number; + sdk: NomadContext; + consumer: Consumer; + indexers: Map; + gov: number; - constructor(sdk: NomadContext, c: Consumer, gov: number) { - super(); - this.sdk = sdk; - this.consumer = c; - this.indexers = new Map(); - this.gov = gov; + constructor(sdk: NomadContext, c: Consumer, gov: number) { + super(); + this.sdk = sdk; + this.consumer = c; + this.indexers = new Map(); + this.gov = gov; + } - } + async indexAll() { + await Promise.all( + this.sdk.domainNumbers.map((domain: number) => this.index(domain)), + ); + } - async indexAll() { - await Promise.all(this.sdk.domainNumbers.map((domain: number) => this.index(domain))) + async index(domain: number) { + const existingIndexer = this.indexers.get(domain); + if (existingIndexer) { + existingIndexer.stop(); } - async index(domain: number) { - const existingIndexer = this.indexers.get(domain); - if (existingIndexer) { - existingIndexer.stop(); - } + const indexer = new Indexer(domain, this.sdk, this); - const indexer = new Indexer(domain, this.sdk, this); - - if (domain === this.gov) { - - await indexer.startAll(this.sdk.domainNumbers.filter(d => d!=this.gov)) - } else { - await indexer.startAll([this.gov]) - } - this.indexers.set(domain, indexer); + if (domain === this.gov) { + await indexer.startAll( + this.sdk.domainNumbers.filter((d) => d != this.gov), + ); + } else { + await indexer.startAll([this.gov]); } + this.indexers.set(domain, indexer); + } - startConsuming() { - this.on('new_event', (event: NomadEvent) => { - this.consumer.consume(event); - }) - - Array.from(this.indexers.values()).map(indexer => { - const p = (indexer.persistance as RamPersistance).iter(); - return Array.from(p); - }).flat().sort((a,b) => a.ts - b.ts).forEach(e => this.consumer.consume(e)); + startConsuming() { + this.on('new_event', (event: NomadEvent) => { + this.consumer.consume(event); + }); - Array.from(this.indexers.values()).map( - indexer => { - indexer.startThrowingEvents() - } - ) + Array.from(this.indexers.values()) + .map((indexer) => indexer.persistance.allEvents()) + .flat() + .sort((a, b) => a.ts - b.ts) + .forEach((e) => this.consumer.consume(e)); - } -} \ No newline at end of file + Array.from(this.indexers.values()).map((indexer) => { + indexer.startThrowingEvents(); + }); + } + + stop() { + this.removeAllListeners(); + } +} diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index 5cfafe4db..15289b767 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -1,57 +1,90 @@ -import { ethers } from "ethers"; -import { NomadEvent } from "./event"; +import { ethers } from 'ethers'; +import { NomadEvent } from './event'; +import fs from 'fs'; export function sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }) + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); } -export async function retry(callback: () => Promise, tries: number): Promise<[T | undefined, any]> { - let timeout = 3000; - let lastError: any = undefined; - for (let attempt = 0; attempt < tries; attempt++) { - try { - return [await callback(), undefined]; - } catch(e) { - lastError = e; - await sleep(timeout * 2 ** attempt); - } +export async function retry( + callback: () => Promise, + tries: number, +): Promise<[T | undefined, any]> { + let timeout = 3000; + let lastError: any = undefined; + for (let attempt = 0; attempt < tries; attempt++) { + try { + return [await callback(), undefined]; + } catch (e) { + lastError = e; + await sleep(timeout * 2 ** attempt); } - return [undefined, lastError] + } + return [undefined, lastError]; } - export function replacer(key: any, value: any): any { - if(value instanceof Map) { - return { - dataType: 'Map', - value: Array.from(value.entries()), // or with spread: value: [...value] - }; - } else if(value instanceof NomadEvent) { - return { - dataType: 'NomadEvent', - value: value.toObject(), // or with spread: value: [...value] - }; - } else if(value instanceof ethers.BigNumber) { - return { - dataType: 'BigNumber', - value: value.toHexString(), // or with spread: value: [...value] - }; - } else { - return value; - } + if (value instanceof Map) { + return { + dataType: 'Map', + value: Array.from(value.entries()), // or with spread: value: [...value] + }; + } else if (value instanceof NomadEvent) { + return { + dataType: 'NomadEvent', + value: value.toObject(), // or with spread: value: [...value] + }; + } else if (value instanceof ethers.BigNumber) { + return { + dataType: 'BigNumber', + value: value.toHexString(), // or with spread: value: [...value] + }; + } else { + return value; + } } export function reviver(key: any, value: any): any { - if(typeof value === 'object' && value !== null) { - if (value.dataType === 'Map') { - return new Map(value.value); - } else if (value.dataType === 'NomadEvent') { - return NomadEvent.fromObject(value.value); - } else if (value.dataType === 'BigNumber') { - return ethers.BigNumber.from(value.value); - } + if (typeof value === 'object' && value !== null) { + if (value.dataType === 'Map') { + return new Map(value.value); + } else if (value.dataType === 'NomadEvent') { + return NomadEvent.fromObject(value.value); + } else if (value.dataType === 'BigNumber') { + return ethers.BigNumber.from(value.value); } - return value; } + return value; +} + +export class KVCache { + m: Map; + path: string; + + constructor(path: string) { + this.m = new Map(); + this.path = path; + this.tryLoad(); + } + + save() { + fs.writeFileSync(this.path, JSON.stringify(this.m, replacer)); + } + + tryLoad() { + try { + this.m = JSON.parse(fs.readFileSync(this.path, 'utf8'), reviver); + } catch (_) {} + } + + set(k: K, v: V) { + this.m.set(k, v); + this.save(); + } + + get(k: K): V | undefined { + return this.m.get(k); + } +} From 65bf84bc93806c55f21085d42aa24378e56e7e10 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 17 Jan 2022 22:54:10 +0100 Subject: [PATCH 05/63] debugging --- typescript/nomad-monitor/src/indexer/consumer.ts | 12 ------------ typescript/nomad-monitor/src/indexer/event.ts | 4 +++- typescript/nomad-monitor/src/indexer/utils.ts | 4 ++++ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index bc06d53f5..642791d6d 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -133,12 +133,6 @@ export class Processor extends Consumer { return undefined; } -// mustGetMsg(id: string | number): NomadMessage { -// const m = this.getMsg(id); -// if (!m) throw new Error(`Message not found`); -// return m; -// } - getMsgsByOriginAndRoot( origin: number, root: string, @@ -149,12 +143,6 @@ export class Processor extends Consumer { return []; } -// mustGetMsgByOriginAndRoot(origin: number, root: string): NomadMessage { -// const m = this.getMsgByOriginAndRoot(origin, root); -// if (!m) throw new Error(`Msg not found by origin and root`); -// return m; -// } - stats(): void { let dispatched = 0; let updated = 0; diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index d334c4ec3..e9da68175 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -1,4 +1,5 @@ import { ethers } from 'ethers'; +import { logToFile } from './utils'; export enum ContractType { Home = 'home', @@ -56,10 +57,11 @@ export class NomadEvent { this.eventType = eventType; this.contractType = contractType; this.replicaOrigin = replicaOrigin; - this.ts = source === EventSource.Past && contractType == ContractType.Home ? ts - 60000: ts // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination + this.ts = source === EventSource.Past && contractType == ContractType.Home ? ts - 45000: ts // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination this.eventData = eventData; this.block = block; this.source = source; + logToFile(`new event at ${new Date().valueOf()} -> ${JSON.stringify(this.toObject())}`) } destination(): number { diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index 15289b767..8e739ecc2 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -88,3 +88,7 @@ export class KVCache { return this.m.get(k); } } + +export function logToFile(s: string) { + fs.writeFileSync('/tmp/log.log', s); +} \ No newline at end of file From 15b5e815bc4f95258c458bac419d4c82d8a48b4b Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 17 Jan 2022 22:56:25 +0100 Subject: [PATCH 06/63] debugging --- typescript/nomad-monitor/src/indexer/event.ts | 2 +- typescript/nomad-monitor/src/indexer/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index e9da68175..469d73e28 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -57,7 +57,7 @@ export class NomadEvent { this.eventType = eventType; this.contractType = contractType; this.replicaOrigin = replicaOrigin; - this.ts = source === EventSource.Past && contractType == ContractType.Home ? ts - 45000: ts // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination + this.ts = (source === EventSource.Past && contractType == ContractType.Home) ? ts - 45000: ts // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination this.eventData = eventData; this.block = block; this.source = source; diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index 8e739ecc2..8318966e7 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -90,5 +90,5 @@ export class KVCache { } export function logToFile(s: string) { - fs.writeFileSync('/tmp/log.log', s); + fs.appendFileSync('/tmp/log.log', s + '\n'); } \ No newline at end of file From a63a70c57f96a513d140fab6814885411a364432 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 18 Jan 2022 13:15:10 +0100 Subject: [PATCH 07/63] feature: no more subscription --- .../nomad-monitor/src/indexer/consumer.ts | 33 ++-- .../nomad-monitor/src/indexer/indexer.ts | 181 ++++-------------- typescript/nomad-monitor/src/indexer/main.ts | 6 +- .../nomad-monitor/src/indexer/orchestrator.ts | 69 ++++--- 4 files changed, 93 insertions(+), 196 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 642791d6d..2d2aa8b11 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -2,7 +2,7 @@ import { ethers } from 'ethers'; import { EventType, NomadEvent } from './event'; export abstract class Consumer { - abstract consume(event: NomadEvent): void; + abstract consume(...evens: NomadEvent[]): void; abstract stats(): void; } @@ -63,18 +63,21 @@ export class Processor extends Consumer { this.consumed = 0; } - consume(event: NomadEvent): void { - if (event.eventType === EventType.HomeDispatch) { - this.dispatched(event); - } else if (event.eventType === EventType.HomeUpdate) { - this.homeUpdate(event); - } else if (event.eventType === EventType.ReplicaUpdate) { - this.replicaUpdate(event); - } else if (event.eventType === EventType.ReplicaProcess) { - this.process(event); + consume(...events: NomadEvent[]): void { + console.log(`Going to consume`, events.length, `events`); + for (const event of events) { + if (event.eventType === EventType.HomeDispatch) { + this.dispatched(event); + } else if (event.eventType === EventType.HomeUpdate) { + this.homeUpdate(event); + } else if (event.eventType === EventType.ReplicaUpdate) { + this.replicaUpdate(event); + } else if (event.eventType === EventType.ReplicaProcess) { + this.process(event); + } + + this.consumed += 1; } - - this.consumed += 1; } dispatched(e: NomadEvent) { @@ -93,21 +96,21 @@ export class Processor extends Consumer { homeUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); if (ms.length) ms.forEach(m => { - if (m.state == MsgState.Dispatched) m.state = MsgState.Updated; + if (m.state < MsgState.Updated) m.state = MsgState.Updated; }); } replicaUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot(e.replicaOrigin, e.eventData.oldRoot!); if (ms.length) ms.forEach(m => { - if (m.state == MsgState.Updated) m.state = MsgState.Relayed + if (m.state < MsgState.Relayed) m.state = MsgState.Relayed }); } process(e: NomadEvent) { const m = this.getMsg(e.eventData.messageHash!); if (m) { - if (m.state == MsgState.Relayed) m.state = MsgState.Processed; + if (m.state < MsgState.Processed) m.state = MsgState.Processed; } } diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index 26ed0d227..c91aac883 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -48,10 +48,6 @@ export class Indexer { return time; } - stop() { - // TODO: this should close all listeners - } - async init() { await this.persistance.init(); } @@ -64,157 +60,42 @@ export class Indexer { return this.persistance.from; } - processEvent(event: NomadEvent) { - if (this.eventCallback != undefined) this.eventCallback(event); - this.persistance.store(event); + home(): Home { + return this.sdk.getCore(this.domain)!.home; //getHomeAtDomain(this.domain); } - subscribeHome() { - const home = this.home(); - home.on( - home.filters.Dispatch(), - async ( - messageHash, - leafIndex, - destinationAndNonce, - committedRoot, - message, - ev, - ) => { - const eventPrepared = new NomadEvent( - this.domain, - EventType.HomeDispatch, - ContractType.Home, - 0, - new Date().valueOf(), - { - messageHash, - leafIndex, - destinationAndNonce, - committedRoot, - message, - }, - ev.blockNumber, - EventSource.New, - ); - - this.processEvent(eventPrepared); - }, - ); - - home.on( - home.filters.Update(), - async (homeDomain, oldRoot, newRoot, signature, ev) => { - const eventPrepared = new NomadEvent( - this.domain, - EventType.HomeUpdate, - ContractType.Home, - 0, - new Date().valueOf(), - { - homeDomain, - oldRoot, - newRoot, - signature, - }, - ev.blockNumber, - EventSource.New, - ); - this.processEvent(eventPrepared); - }, - ); + replicaForDomain(domain: number): Replica { + return this.sdk.getReplicaFor(domain, this.domain)!; } - subscribeReplica(domain: number) { - const replica = this.replicaForDomain(domain); - replica.on( - replica.filters.Update(), - async (homeDomain, oldRoot, newRoot, signature, ev) => { - const eventPrepared = new NomadEvent( - this.domain, - EventType.ReplicaUpdate, - ContractType.Replica, - domain, - new Date().valueOf(), - { - homeDomain, - oldRoot, - newRoot, - signature, - }, - ev.blockNumber, - EventSource.New, - ); - this.processEvent(eventPrepared); - }, - ); - - replica.on( - replica.filters.Process(), - async (messageHash, success, returnData, ev) => { - const eventPrepared = new NomadEvent( - this.domain, - EventType.ReplicaProcess, - ContractType.Replica, - domain, - new Date().valueOf(), - { - messageHash, - success, - returnData, - }, - ev.blockNumber, - EventSource.New, - ); - this.processEvent(eventPrepared); - }, - ); - } + async updateAll(replicas: number[]) { - async startAll(replicas: number[]) { - // , past: number let from = Math.max( this.persistance.height + 1, this.sdk.getDomain(this.domain)?.paginate?.from || 0, ); const to = await this.provider.getBlockNumber(); - // from = to - past; - console.log(`Want to fetch from`, from, `to`, to, `for`, this.domain); - // console.log(this.domain, `starting from height`, from, `but actually from to`, to, `- 1000`, to - 1000); - this.subscribeAll(replicas); - await this.fetchAll(from, to, replicas); - } - subscribeAll(replicas: number[]) { - this.subscribeHome(); - replicas.forEach((r) => this.subscribeReplica(r)); - } + console.log(`Fetching for`, this.domain, `from:`, from, `to:`, to); + const [fetchedEvents, error] = await retry(async () => { + const homeEvents = await this.fetchHome(from, to); + const replicasEvents = (await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to)))).flat(); + return [...homeEvents, ...replicasEvents]; + }, 5); - home(): Home { - return this.sdk.getCore(this.domain)!.home; //getHomeAtDomain(this.domain); - } + if (error) throw error; + if (!fetchedEvents) throw new Error('kek'); - replicaForDomain(domain: number): Replica { - return this.sdk.getReplicaFor(domain, this.domain)!; - } + fetchedEvents.sort((a,b) => a.ts-b.ts); + this.persistance.store(...fetchedEvents); - async fetchAll(from: number, to: number, replicas: number[]) { - console.log(`Fetching for`, this.domain); - const [_, error] = await retry(async () => { - await this.fetchHome(from, to); - await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))); - }, 5); - if (error) throw error; - this.persistance.sortSorage(); - this.savePersistance(); - // TODO mb remove or fix this.dummyTestEventsIntegrity(); - // TODO END - console.log(this.domain, `Fetched all`); + + return fetchedEvents } dummyTestEventsIntegrity() { @@ -265,7 +146,7 @@ export class Indexer { h1 = newRoot; htotal -= 1; } else { - console.log(this.domain, `Home broke with`, htotal, 'unresolved'); + // console.log(this.domain, `Home broke with`, htotal, 'unresolved'); break; } } @@ -275,7 +156,7 @@ export class Indexer { r1 = newRoot; rtotal -= 1; } else { - console.log(`Replica broke with`, rtotal, 'unresolved'); + // console.log(this.domain, `Replica broke with`, rtotal, 'unresolved'); break; } } @@ -288,13 +169,10 @@ export class Indexer { this.persistance.persist(); } - startThrowingEvents() { - this.eventCallback = (event: NomadEvent) => { - this.orchestrator.emit('new_event', event); - }; - } - async fetchHome(from: number, to: number) { + + let fetchedEvents: NomadEvent[] = []; + const home = this.home(); { const events = await home.queryFilter(home.filters.Dispatch(), from, to); @@ -319,7 +197,7 @@ export class Indexer { ), ), ); - this.persistance.store(...parsedEvents); + fetchedEvents.push(...parsedEvents); } { @@ -344,11 +222,15 @@ export class Indexer { ), ), ); - this.persistance.store(...parsedEvents); + fetchedEvents.push(...parsedEvents); } + + return fetchedEvents } async fetchReplica(domain: number, from: number, to: number) { + let fetchedEvents: NomadEvent[] = []; + const replica = this.replicaForDomain(domain); { const events = await replica.queryFilter( @@ -376,7 +258,7 @@ export class Indexer { ), ), ); - this.persistance.store(...parsedEvents); + fetchedEvents.push(...parsedEvents); } { @@ -404,9 +286,11 @@ export class Indexer { ), ), ); - this.persistance.store(...parsedEvents); + fetchedEvents.push(...parsedEvents); } + return fetchedEvents; } + } export abstract class Persistance { @@ -436,7 +320,6 @@ export class FilePersistance extends Persistance { store(...events: NomadEvent[]): void {} async init(): Promise { if (fs.existsSync(this.path)) { - // load from the storage this.from = 13; this.height = 14; return true; diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index 165fcaa5e..48c3c4276 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -29,11 +29,7 @@ const moonbeamRPC = 'https://moonbeam.api.onfinality.io/public'; //"https://rpc. ctx.registerWalletSigner(moonbeamId, signer); const c = new Processor(); - setInterval(() => c.stats(), 15000); const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0]); - await o.indexAll(); - o.startConsuming(); - - console.log(`--->`, c.messages.filter(m=>m.state == 1)); + await o.startConsuming(); })(); diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index e8126b3ee..07d5aba43 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -2,13 +2,15 @@ import { NomadContext } from '@nomad-xyz/sdk'; import { Consumer } from './consumer'; import { Indexer } from './indexer'; import { EventEmitter } from 'events'; -import { NomadEvent } from './event'; +import { sleep } from './utils'; export class Orchestrator extends EventEmitter { sdk: NomadContext; consumer: Consumer; indexers: Map; gov: number; + done: boolean; + freshStart: boolean; constructor(sdk: NomadContext, c: Consumer, gov: number) { super(); @@ -16,49 +18,62 @@ export class Orchestrator extends EventEmitter { this.consumer = c; this.indexers = new Map(); this.gov = gov; + this.done = false; + this.freshStart = true; + + this.initIndexers() + + this.initalFeedConsumer() } async indexAll() { - await Promise.all( + const events = (await Promise.all( this.sdk.domainNumbers.map((domain: number) => this.index(domain)), - ); + )).flat(); + events.sort((a, b) => a.ts-b.ts); + this.consumer.consume(...events); } async index(domain: number) { - const existingIndexer = this.indexers.get(domain); - if (existingIndexer) { - existingIndexer.stop(); - } - - const indexer = new Indexer(domain, this.sdk, this); + let indexer = this.indexers.get(domain)!; + let replicas = [] if (domain === this.gov) { - await indexer.startAll( - this.sdk.domainNumbers.filter((d) => d != this.gov), - ); + replicas = this.sdk.domainNumbers.filter((d) => d != this.gov) } else { - await indexer.startAll([this.gov]); + replicas = [this.gov]; } - this.indexers.set(domain, indexer); + + return await indexer.updateAll(replicas); } - startConsuming() { - this.on('new_event', (event: NomadEvent) => { - this.consumer.consume(event); - }); + initalFeedConsumer() { + const events = Array.from(this.indexers.values()).map(indexer => indexer.persistance.allEvents()).flat(); + events.sort((a, b) => a.ts-b.ts); + this.consumer.consume(...events); + } - Array.from(this.indexers.values()) - .map((indexer) => indexer.persistance.allEvents()) - .flat() - .sort((a, b) => a.ts - b.ts) - .forEach((e) => this.consumer.consume(e)); + initIndexers() { + for (const domain of this.sdk.domainNumbers) { + const indexer = new Indexer(domain, this.sdk, this); + this.indexers.set(domain, indexer); + } + } - Array.from(this.indexers.values()).map((indexer) => { - indexer.startThrowingEvents(); - }); + async startConsuming() { + while (!this.done) { + console.log(`Started to reindex`) + const start = new Date().valueOf(); + await this.indexAll() + console.log(`Finished reindexing after seconds:`, (new Date().valueOf() - start) / 1000); + + this.consumer.stats(); + + await sleep(15000); + } } stop() { - this.removeAllListeners(); + this.done = true; } } From 7bffcb57f3313204a37348c869221282d4ec77df Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 18 Jan 2022 13:20:46 +0100 Subject: [PATCH 08/63] chore: increased time --- typescript/nomad-monitor/src/indexer/orchestrator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index 07d5aba43..e2e77d29c 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -69,7 +69,7 @@ export class Orchestrator extends EventEmitter { this.consumer.stats(); - await sleep(15000); + await sleep(30000); } } From 7c53aa51cf8279b6c3eecc179a178b0dccc8151a Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 18 Jan 2022 21:08:35 +0100 Subject: [PATCH 09/63] feature: metrics --- .../nomad-monitor/src/indexer/consumer.ts | 249 ++++++++++++++---- typescript/nomad-monitor/src/indexer/event.ts | 2 - typescript/nomad-monitor/src/indexer/main.ts | 14 +- .../nomad-monitor/src/indexer/metrics.ts | 128 +++++++++ .../nomad-monitor/src/indexer/orchestrator.ts | 51 +++- typescript/nomad-monitor/src/indexer/types.ts | 119 +++++++++ typescript/nomad-monitor/src/indexer/utils.ts | 3 + 7 files changed, 508 insertions(+), 58 deletions(-) create mode 100644 typescript/nomad-monitor/src/indexer/metrics.ts create mode 100644 typescript/nomad-monitor/src/indexer/types.ts diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 2d2aa8b11..dd7e6510a 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -1,9 +1,121 @@ import { ethers } from 'ethers'; import { EventType, NomadEvent } from './event'; +import { Statistics } from './types'; + + + +class StatisticsCollector { + + s: Statistics; + constructor(domains: number[]) { + this.s = new Statistics(domains); + } + + addDispatched(domain: number) { + this.s.counts.total.dispatched += 1; + this.s.counts.domainStatistics.get(domain)!.dispatched += 1; + } + + addUpdated(domain: number) { + this.s.counts.total.updated += 1; + this.s.counts.domainStatistics.get(domain)!.updated += 1; + } + + addRelayed(domain: number) { + this.s.counts.total.relayed += 1; + this.s.counts.domainStatistics.get(domain)!.relayed += 1; + } + + addProcessed(domain: number) { + this.s.counts.total.processed += 1; + this.s.counts.domainStatistics.get(domain)!.processed += 1; + } + + contributeUpdateTimings(m: NomadMessage) { + const inUpdateStat = m.timings.inUpdated(); + if (inUpdateStat) { + this.s.timings.total.meanUpdate.add(inUpdateStat) + this.s.timings.domainStatistics.get(m.origin)!.meanUpdate.add(inUpdateStat) + } + } + + contributeRelayTimings(m: NomadMessage) { + this.contributeUpdateTimings(m); + const inRelayStat = m.timings.inRelayed(); + if (inRelayStat) { + this.s.timings.total.meanRelay.add(inRelayStat) + this.s.timings.domainStatistics.get(m.origin)!.meanRelay.add(inRelayStat) + } + } + + contributeProcessTimings(m: NomadMessage) { + this.contributeRelayTimings(m); + const inProcessStat = m.timings.inProcessed(); + if (inProcessStat) { + this.s.timings.total.meanProcess.add(inProcessStat) + this.s.timings.domainStatistics.get(m.origin)!.meanProcess.add(inProcessStat) + } + + const e2e = m.timings.e2e(); + if (e2e) { + this.s.timings.total.meanE2E.add(e2e) + this.s.timings.domainStatistics.get(m.origin)!.meanE2E.add(e2e) + } + } + + contributeToCount(m: NomadMessage) { + switch (m.state) { + case MsgState.Dispatched: + this.addDispatched(m.origin); + break; + case MsgState.Updated: + this.addUpdated(m.origin); + // this.contributeUpdateTimings(m); + break; + case MsgState.Relayed: + this.addRelayed(m.origin); + // this.contributeRelayTimings(m); + break; + case MsgState.Processed: + this.addProcessed(m.origin); + // this.contributeProcessTimings(m); + break; + default: + break; + } + } + + contributeToTime(m: NomadMessage) { + switch (m.state) { + // case MsgState.Dispatched: + // this.addDispatched(m.origin); + // break; + case MsgState.Updated: + // this.addUpdated(m.origin); + this.contributeUpdateTimings(m); + break; + case MsgState.Relayed: + // this.addRelayed(m.origin); + this.contributeRelayTimings(m); + break; + case MsgState.Processed: + // this.addProcessed(m.origin); + this.contributeProcessTimings(m); + break; + default: + break; + } + } + + stats(): Statistics { + return this.s; + } +} + export abstract class Consumer { abstract consume(...evens: NomadEvent[]): void; - abstract stats(): void; + abstract stats(): Statistics; } enum MsgState { @@ -13,6 +125,62 @@ enum MsgState { Processed, } +class Timings { + dispatchedAt: number; + updatedAt: number; + relayedAt: number; + processedAt: number; + + constructor(ts: number) { + this.dispatchedAt = ts; + this.updatedAt = 0; + this.relayedAt = 0; + this.processedAt = 0; + } + + updated(ts: number) { + this.updatedAt = ts; + } + + relayed(ts: number) { + this.relayedAt = ts; + } + + processed(ts: number) { + this.processedAt = ts; + } + + inUpdated(): number | undefined { + if (this.updatedAt) { + return this.updatedAt - this.dispatchedAt; + } + return undefined + } + + inRelayed(): number | undefined { + if (this.relayedAt) { + return this.relayedAt - (this.updatedAt || this.dispatchedAt); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available + } + return undefined + } + + inProcessed(): number | undefined { + if (this.processedAt) { + return this.processedAt - (this.relayedAt || this.updatedAt || this.dispatchedAt); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available + } + return undefined + } + + e2e(): number | undefined { + if (this.processedAt) { + return this.processedAt - (this.dispatchedAt || this.updatedAt || this.relayedAt); // same as for .inRelayed() and .inProcessed() but opposit order + } + return undefined + } + + +} + class NomadMessage { origin: number; destination: number; @@ -21,8 +189,8 @@ class NomadMessage { leafIndex: ethers.BigNumber; destinationAndNonce: ethers.BigNumber; message: string; - state: MsgState; + timings: Timings; constructor( origin: number, @@ -32,6 +200,7 @@ class NomadMessage { leafIndex: ethers.BigNumber, destinationAndNonce: ethers.BigNumber, message: string, + createdAt: number, ) { this.origin = origin; this.destination = destination; @@ -42,6 +211,7 @@ class NomadMessage { this.message = message; this.state = MsgState.Dispatched; + this.timings = new Timings(createdAt); } get originAndRoot(): string { @@ -54,6 +224,8 @@ export class Processor extends Consumer { msgToIndex: Map; msgByOriginAndRoot: Map; consumed: number; // for debug + domains: number[]; + constructor() { super(); @@ -61,6 +233,7 @@ export class Processor extends Consumer { this.msgToIndex = new Map(); this.msgByOriginAndRoot = new Map(); this.consumed = 0; + this.domains = []; } consume(...events: NomadEvent[]): void { @@ -89,28 +262,40 @@ export class Processor extends Consumer { e.eventData.leafIndex!, e.eventData.destinationAndNonce!, e.eventData.message!, + e.ts, ); this.add(m); + + if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } homeUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); if (ms.length) ms.forEach(m => { - if (m.state < MsgState.Updated) m.state = MsgState.Updated; + if (m.state < MsgState.Updated) { + m.state = MsgState.Updated; + m.timings.updated(e.ts); + } }); } replicaUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot(e.replicaOrigin, e.eventData.oldRoot!); if (ms.length) ms.forEach(m => { - if (m.state < MsgState.Relayed) m.state = MsgState.Relayed + if (m.state < MsgState.Relayed) { + m.state = MsgState.Relayed; + m.timings.relayed(e.ts); + } }); } process(e: NomadEvent) { const m = this.getMsg(e.eventData.messageHash!); if (m) { - if (m.state < MsgState.Processed) m.state = MsgState.Processed; + if (m.state < MsgState.Processed) { + m.state = MsgState.Processed; + m.timings.processed(e.ts); + } } } @@ -146,54 +331,20 @@ export class Processor extends Consumer { return []; } - stats(): void { - let dispatched = 0; - let updated = 0; - let relayed = 0; - let processed = 0; + stats(): Statistics { + const collector = new StatisticsCollector(this.domains); + this.messages.forEach((m) => { - switch (m.state) { - case MsgState.Dispatched: - dispatched += 1; - break; - case MsgState.Updated: - updated += 1; - break; - case MsgState.Relayed: - relayed += 1; - break; - case MsgState.Processed: - processed += 1; - break; - default: - break; - } + collector.contributeToCount(m) }); - console.log( - `D:`, - dispatched, - `U:`, - updated, - `R:`, - relayed, - `P:`, - processed, - ); - } -} -export class Logger extends Consumer { - i: number; - - constructor() { - super(); - this.i = 0; - } + this.messages.slice(this.messages.length - 50).forEach((m) => { + collector.contributeToTime(m) + }); - consume(event: NomadEvent): void {} + - stats(): void { - console.log(`this.i:`, this.i); + return collector.stats(); } -} +} \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index 469d73e28..03377f9b6 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -1,5 +1,4 @@ import { ethers } from 'ethers'; -import { logToFile } from './utils'; export enum ContractType { Home = 'home', @@ -61,7 +60,6 @@ export class NomadEvent { this.eventData = eventData; this.block = block; this.source = source; - logToFile(`new event at ${new Date().valueOf()} -> ${JSON.stringify(this.toObject())}`) } destination(): number { diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index 48c3c4276..120cddc7d 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -3,6 +3,8 @@ import { Processor } from './consumer'; import { Orchestrator } from './orchestrator'; import * as dotenv from 'dotenv'; import { ethers } from 'ethers'; +import { IndexerCollector } from './metrics'; +import Logger from 'bunyan'; dotenv.config({ // path: '/Users/daniilnaumetc/code/tmp/monitor/typescript/nomad-monitor/src/indexer/.env' }); @@ -28,8 +30,18 @@ const moonbeamRPC = 'https://moonbeam.api.onfinality.io/public'; //"https://rpc. ctx.registerRpcProvider(moonbeamId, moonbeamRPC); ctx.registerWalletSigner(moonbeamId, signer); const c = new Processor(); + const m = new IndexerCollector('development', createLogger('indexer', 'development')) - const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0]); + const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0], m); await o.startConsuming(); })(); + +function createLogger(name: string, environment: string) { + return Logger.createLogger({ + name, + serializers: Logger.stdSerializers, + level: 'debug', + environment: environment, + }); +} \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/metrics.ts b/typescript/nomad-monitor/src/indexer/metrics.ts new file mode 100644 index 000000000..510de73d7 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/metrics.ts @@ -0,0 +1,128 @@ +import { MetricsCollector } from '../metrics'; +import { Gauge } from 'prom-client'; +import Logger from 'bunyan'; + +const prefix = `fancy_monitor` + +export class IndexerCollector extends MetricsCollector { + private numDispatchedGauge: Gauge; + private numUpdatedGauge: Gauge; + private numRelayedGauge: Gauge; + private numProcessedGauge: Gauge; + + private meanUpdateTimeGauge: Gauge; + private meanRelayTimeGauge: Gauge; + private meanProcessTimeGauge: Gauge; + private meanEndToEndTimeGauge: Gauge; + + constructor(environment: string, logger: Logger) { + super(environment, logger); + + // Count + + this.numDispatchedGauge = new Gauge({ + name: prefix + '_number_messages_dispatched', + help: 'Gauge that indicates how many messages have been dispatched for a network.', + labelNames: ['network', 'environment'], + }); + + this.numUpdatedGauge = new Gauge({ + name: prefix + '_number_messages_updated', + help: 'Gauge that indicates how many messages have been updated for a network.', + labelNames: ['network', 'environment'], + }); + + this.numRelayedGauge = new Gauge({ + name: prefix + '_number_messages_relayed', + help: 'Gauge that indicates how many messages have been relayed for a network.', + labelNames: ['network', 'environment'], + }); + + this.numProcessedGauge = new Gauge({ + name: prefix + '_number_messages_processed', + help: 'Gauge that indicates how many messages have been processed for a network.', + labelNames: ['network', 'environment'], + }); + + // Time + + this.meanUpdateTimeGauge = new Gauge({ + name: prefix + '_mean_update_time', + help: 'Gauge that indicates how long does it take to move from dispatched to updated.', + labelNames: ['network', 'environment'], + }); + + this.meanRelayTimeGauge = new Gauge({ + name: prefix + '_mean_relay_time', + help: 'Gauge that indicates how long does it take to move from updated to relayed.', + labelNames: ['network', 'environment'], + }); + + this.meanProcessTimeGauge = new Gauge({ + name: prefix + '_mean_process_time', + help: 'Gauge that indicates how long does it take to move from relayed to processed.', + labelNames: ['network', 'environment'], + }); + + this.meanEndToEndTimeGauge = new Gauge({ + name: prefix + '_mean_e2e_time', + help: 'Gauge that indicates how long does it take to move from dispatched to processed.', + labelNames: ['network', 'environment'], + }); + } + + /** + * Sets the state for a bridge. + */ + setNetworkState( + network: string, + dispatched: number, + updated: number, + relayed: number, + processed: number, + updateTime: number, + relayTime: number, + processTime: number, + e2eTime: number, + ) { + this.numDispatchedGauge.set( + { network, environment: this.environment }, + dispatched, + ); + + this.numUpdatedGauge.set( + { network, environment: this.environment }, + updated, + ); + + this.numRelayedGauge.set( + { network, environment: this.environment }, + relayed, + ); + + this.numProcessedGauge.set( + { network, environment: this.environment }, + processed, + ); + + this.meanUpdateTimeGauge.set( + { network, environment: this.environment }, + updateTime, + ); + + this.meanRelayTimeGauge.set( + { network, environment: this.environment }, + relayTime, + ); + + this.meanProcessTimeGauge.set( + { network, environment: this.environment }, + processTime, + ); + + this.meanEndToEndTimeGauge.set( + { network, environment: this.environment }, + e2eTime, + ); + } +} diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index e2e77d29c..0d18daa53 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -1,25 +1,27 @@ import { NomadContext } from '@nomad-xyz/sdk'; import { Consumer } from './consumer'; import { Indexer } from './indexer'; -import { EventEmitter } from 'events'; -import { sleep } from './utils'; +import { IndexerCollector } from './metrics'; +import { Statistics } from './types'; +import { replacer, sleep } from './utils'; -export class Orchestrator extends EventEmitter { +export class Orchestrator { sdk: NomadContext; consumer: Consumer; indexers: Map; gov: number; done: boolean; freshStart: boolean; + metrics: IndexerCollector; - constructor(sdk: NomadContext, c: Consumer, gov: number) { - super(); + constructor(sdk: NomadContext, c: Consumer, gov: number, metrics: IndexerCollector) { this.sdk = sdk; this.consumer = c; this.indexers = new Map(); this.gov = gov; this.done = false; this.freshStart = true; + this.metrics = metrics this.initIndexers() @@ -67,12 +69,49 @@ export class Orchestrator extends EventEmitter { await this.indexAll() console.log(`Finished reindexing after seconds:`, (new Date().valueOf() - start) / 1000); - this.consumer.stats(); + const stats = this.consumer.stats(); + console.log(`stats->`, JSON.stringify(stats, replacer)); + + this.reportAllMetrics(stats); await sleep(30000); } } + reportAllMetrics(statistics: Statistics) { + for (const domain of this.sdk.domainNumbers) { + this.reportMetrics(domain, statistics) + } + } + + reportMetrics(domain: number, statistics: Statistics) { + const { + counts: { + dispatched, + updated, + relayed, + processed, + }, + timings: { + meanUpdate, + meanRelay, + meanProcess, + meanE2E, + } + } = statistics.forDomain(domain); + this.metrics.setNetworkState( + this.sdk.getDomain(domain)!.name, + dispatched, + updated, + relayed, + processed, + meanUpdate, + meanRelay, + meanProcess, + meanE2E, + ) + } + stop() { this.done = true; } diff --git a/typescript/nomad-monitor/src/indexer/types.ts b/typescript/nomad-monitor/src/indexer/types.ts new file mode 100644 index 000000000..14c9a1f44 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/types.ts @@ -0,0 +1,119 @@ + +export class Mean { + count: number; + total: number; + constructor() { + this.count = 0; + this.total = 0; + } + add(value: number) { + this.count += 1; + this.total += value; + } + mean(): number { + return this.total/this.count + } +} + + + export class BasicCountStages { + dispatched: number; + updated: number; + relayed: number; + processed: number; + constructor() { + this.dispatched = 0; + this.updated = 0; + this.relayed = 0; + this.processed = 0; + } + + toObject() { + return { + dispatched: this.dispatched, + updated: this.updated, + relayed: this.relayed, + processed: this.processed, + } + } + } + + export class BasicTiming { + meanUpdate: Mean; + meanRelay: Mean; + meanProcess: Mean; + meanE2E: Mean; + constructor() { + this.meanUpdate = new Mean(); + this.meanRelay = new Mean(); + this.meanProcess = new Mean(); + this.meanE2E = new Mean(); + } + + toObject() { + return { + meanUpdate: this.meanUpdate.mean(), + meanRelay: this.meanRelay.mean(), + meanProcess: this.meanProcess.mean(), + meanE2E: this.meanE2E.mean(), + } + } + } + + export class RootTimingStatistic { + total: BasicTiming; + domainStatistics: Map; + constructor(domains: number[]) { + this.total = new BasicTiming(); + this.domainStatistics = new Map(domains.map(d=>[d, new BasicTiming()])) + } + + toObject() { + return { + total: this.total.toObject(), + domainStatistics: Array.from(this.domainStatistics.entries()).map(([d, v]) => [d, v.toObject()]), + } + } + } + + export class RootCountStagesStatistic { + total: BasicCountStages; + domainStatistics: Map; + constructor(domains: number[]) { + this.total = new BasicCountStages(); + this.domainStatistics = new Map(domains.map(d=>[d, new BasicCountStages()])) + } + + toObject() { + return { + total: this.total.toObject(), + domainStatistics: Array.from(this.domainStatistics.entries()).map(([d, v]) => [d, v.toObject()]), + } + } + } + + + export class Statistics { + counts: RootCountStagesStatistic; + timings: RootTimingStatistic; + constructor(domains: number[]) { + this.counts = new RootCountStagesStatistic(domains); + this.timings = new RootTimingStatistic(domains); + } + + toObject() { + return { + counts: this.counts.toObject(), + timings: this.timings.toObject(), + } + } + + forDomain(domain: number){ + const counts = this.counts.domainStatistics.get(domain)!.toObject(); + const timings = this.timings.domainStatistics.get(domain)!.toObject(); + return { + counts, timings + } + } + } + \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index 8318966e7..9273ac9ab 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -1,6 +1,7 @@ import { ethers } from 'ethers'; import { NomadEvent } from './event'; import fs from 'fs'; +import { Mean } from './types'; export function sleep(ms: number) { return new Promise((resolve) => { @@ -41,6 +42,8 @@ export function replacer(key: any, value: any): any { dataType: 'BigNumber', value: value.toHexString(), // or with spread: value: [...value] }; + } else if (value instanceof Mean) { + return value.mean(); } else { return value; } From c98c43cee0215f3e3fc0b8be9b9e258465d7309a Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 19 Jan 2022 14:03:27 +0100 Subject: [PATCH 10/63] chore: a bit cleaning, prettier --- .../nomad-monitor/src/indexer/consumer.ts | 102 ++++----- typescript/nomad-monitor/src/indexer/event.ts | 8 +- .../nomad-monitor/src/indexer/indexer.ts | 41 ++-- typescript/nomad-monitor/src/indexer/main.ts | 21 +- .../nomad-monitor/src/indexer/metrics.ts | 68 +++--- .../nomad-monitor/src/indexer/orchestrator.ts | 73 +++--- typescript/nomad-monitor/src/indexer/types.ts | 209 +++++++++--------- typescript/nomad-monitor/src/indexer/utils.ts | 2 +- 8 files changed, 261 insertions(+), 263 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index dd7e6510a..8293c1919 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -2,10 +2,7 @@ import { ethers } from 'ethers'; import { EventType, NomadEvent } from './event'; import { Statistics } from './types'; - - class StatisticsCollector { - s: Statistics; constructor(domains: number[]) { this.s = new Statistics(domains); @@ -34,8 +31,10 @@ class StatisticsCollector { contributeUpdateTimings(m: NomadMessage) { const inUpdateStat = m.timings.inUpdated(); if (inUpdateStat) { - this.s.timings.total.meanUpdate.add(inUpdateStat) - this.s.timings.domainStatistics.get(m.origin)!.meanUpdate.add(inUpdateStat) + this.s.timings.total.meanUpdate.add(inUpdateStat); + this.s.timings.domainStatistics + .get(m.origin)! + .meanUpdate.add(inUpdateStat); } } @@ -43,8 +42,8 @@ class StatisticsCollector { this.contributeUpdateTimings(m); const inRelayStat = m.timings.inRelayed(); if (inRelayStat) { - this.s.timings.total.meanRelay.add(inRelayStat) - this.s.timings.domainStatistics.get(m.origin)!.meanRelay.add(inRelayStat) + this.s.timings.total.meanRelay.add(inRelayStat); + this.s.timings.domainStatistics.get(m.origin)!.meanRelay.add(inRelayStat); } } @@ -52,14 +51,16 @@ class StatisticsCollector { this.contributeRelayTimings(m); const inProcessStat = m.timings.inProcessed(); if (inProcessStat) { - this.s.timings.total.meanProcess.add(inProcessStat) - this.s.timings.domainStatistics.get(m.origin)!.meanProcess.add(inProcessStat) + this.s.timings.total.meanProcess.add(inProcessStat); + this.s.timings.domainStatistics + .get(m.origin)! + .meanProcess.add(inProcessStat); } const e2e = m.timings.e2e(); if (e2e) { - this.s.timings.total.meanE2E.add(e2e) - this.s.timings.domainStatistics.get(m.origin)!.meanE2E.add(e2e) + this.s.timings.total.meanE2E.add(e2e); + this.s.timings.domainStatistics.get(m.origin)!.meanE2E.add(e2e); } } @@ -70,15 +71,12 @@ class StatisticsCollector { break; case MsgState.Updated: this.addUpdated(m.origin); - // this.contributeUpdateTimings(m); break; case MsgState.Relayed: this.addRelayed(m.origin); - // this.contributeRelayTimings(m); break; case MsgState.Processed: this.addProcessed(m.origin); - // this.contributeProcessTimings(m); break; default: break; @@ -87,19 +85,13 @@ class StatisticsCollector { contributeToTime(m: NomadMessage) { switch (m.state) { - // case MsgState.Dispatched: - // this.addDispatched(m.origin); - // break; case MsgState.Updated: - // this.addUpdated(m.origin); this.contributeUpdateTimings(m); break; case MsgState.Relayed: - // this.addRelayed(m.origin); this.contributeRelayTimings(m); break; case MsgState.Processed: - // this.addProcessed(m.origin); this.contributeProcessTimings(m); break; default: @@ -112,7 +104,6 @@ class StatisticsCollector { } } - export abstract class Consumer { abstract consume(...evens: NomadEvent[]): void; abstract stats(): Statistics; @@ -154,31 +145,35 @@ class Timings { if (this.updatedAt) { return this.updatedAt - this.dispatchedAt; } - return undefined + return undefined; } inRelayed(): number | undefined { if (this.relayedAt) { return this.relayedAt - (this.updatedAt || this.dispatchedAt); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available } - return undefined + return undefined; } inProcessed(): number | undefined { if (this.processedAt) { - return this.processedAt - (this.relayedAt || this.updatedAt || this.dispatchedAt); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available + return ( + this.processedAt - + (this.relayedAt || this.updatedAt || this.dispatchedAt) + ); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available } - return undefined + return undefined; } e2e(): number | undefined { if (this.processedAt) { - return this.processedAt - (this.dispatchedAt || this.updatedAt || this.relayedAt); // same as for .inRelayed() and .inProcessed() but opposit order + return ( + this.processedAt - + (this.dispatchedAt || this.updatedAt || this.relayedAt) + ); // same as for .inRelayed() and .inProcessed() but opposit order } - return undefined + return undefined; } - - } class NomadMessage { @@ -226,7 +221,6 @@ export class Processor extends Consumer { consumed: number; // for debug domains: number[]; - constructor() { super(); this.messages = []; @@ -237,7 +231,6 @@ export class Processor extends Consumer { } consume(...events: NomadEvent[]): void { - console.log(`Going to consume`, events.length, `events`); for (const event of events) { if (event.eventType === EventType.HomeDispatch) { this.dispatched(event); @@ -248,7 +241,7 @@ export class Processor extends Consumer { } else if (event.eventType === EventType.ReplicaProcess) { this.process(event); } - + this.consumed += 1; } } @@ -271,31 +264,36 @@ export class Processor extends Consumer { homeUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); - if (ms.length) ms.forEach(m => { + if (ms.length) + ms.forEach((m) => { if (m.state < MsgState.Updated) { m.state = MsgState.Updated; m.timings.updated(e.ts); } - }); + }); } replicaUpdate(e: NomadEvent) { - const ms = this.getMsgsByOriginAndRoot(e.replicaOrigin, e.eventData.oldRoot!); - if (ms.length) ms.forEach(m => { + const ms = this.getMsgsByOriginAndRoot( + e.replicaOrigin, + e.eventData.oldRoot!, + ); + if (ms.length) + ms.forEach((m) => { if (m.state < MsgState.Relayed) { m.state = MsgState.Relayed; m.timings.relayed(e.ts); } - }); + }); } process(e: NomadEvent) { const m = this.getMsg(e.eventData.messageHash!); if (m) { - if (m.state < MsgState.Processed) { - m.state = MsgState.Processed; - m.timings.processed(e.ts); - } + if (m.state < MsgState.Processed) { + m.state = MsgState.Processed; + m.timings.processed(e.ts); + } } } @@ -304,9 +302,9 @@ export class Processor extends Consumer { this.msgToIndex.set(m.hash, index); const x = this.msgByOriginAndRoot.get(m.originAndRoot); if (x) { - x.push(index) + x.push(index); } else { - this.msgByOriginAndRoot.set(m.originAndRoot, [index]); + this.msgByOriginAndRoot.set(m.originAndRoot, [index]); } this.messages.push(m); } @@ -321,30 +319,24 @@ export class Processor extends Consumer { return undefined; } - getMsgsByOriginAndRoot( - origin: number, - root: string, - ): NomadMessage[] { + getMsgsByOriginAndRoot(origin: number, root: string): NomadMessage[] { const originAndRoot = `${origin}${root}`; const msgIndexs = this.msgByOriginAndRoot.get(originAndRoot); - if (msgIndexs) return msgIndexs.map(msgIndex => this.messages[msgIndex]) + if (msgIndexs) return msgIndexs.map((msgIndex) => this.messages[msgIndex]); return []; } stats(): Statistics { - const collector = new StatisticsCollector(this.domains); - + this.messages.forEach((m) => { - collector.contributeToCount(m) + collector.contributeToCount(m); }); this.messages.slice(this.messages.length - 50).forEach((m) => { - collector.contributeToTime(m) + collector.contributeToTime(m); }); - - return collector.stats(); } -} \ No newline at end of file +} diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index 03377f9b6..501dc2439 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -13,8 +13,7 @@ export enum EventType { } export enum EventSource { - Past = 'past', - New = 'new', + Fetch = 'fetch', Storage = 'storage', } @@ -56,7 +55,10 @@ export class NomadEvent { this.eventType = eventType; this.contractType = contractType; this.replicaOrigin = replicaOrigin; - this.ts = (source === EventSource.Past && contractType == ContractType.Home) ? ts - 45000: ts // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination + this.ts = + /*source === EventSource.Fetch && */ contractType == ContractType.Home + ? ts - 45000 + : ts; // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination this.eventData = eventData; this.block = block; this.source = source; diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index c91aac883..f1be17c4c 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -21,7 +21,9 @@ export class Indexer { this.persistance = new RamPersistance( `/tmp/persistance_${this.domain}.json`, ); - this.block2timeCache = new KVCache(`/tmp/blocktime_cache_${this.domain}`); + this.block2timeCache = new KVCache( + `/tmp/persistance_blocktime_cache_${this.domain}`, + ); } get provider(): ethers.providers.Provider { @@ -61,7 +63,7 @@ export class Indexer { } home(): Home { - return this.sdk.getCore(this.domain)!.home; //getHomeAtDomain(this.domain); + return this.sdk.getCore(this.domain)!.home; } replicaForDomain(domain: number): Replica { @@ -69,38 +71,37 @@ export class Indexer { } async updateAll(replicas: number[]) { - let from = Math.max( this.persistance.height + 1, this.sdk.getDomain(this.domain)?.paginate?.from || 0, ); const to = await this.provider.getBlockNumber(); - console.log(`Fetching for`, this.domain, `from:`, from, `to:`, to); + this.orchestrator.logger.info( + `Fetching events for domain ${this.domain} from: ${from}, to: ${to}`, + ); const [fetchedEvents, error] = await retry(async () => { const homeEvents = await this.fetchHome(from, to); - const replicasEvents = (await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to)))).flat(); + const replicasEvents = ( + await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))) + ).flat(); return [...homeEvents, ...replicasEvents]; }, 5); if (error) throw error; if (!fetchedEvents) throw new Error('kek'); - fetchedEvents.sort((a,b) => a.ts-b.ts); + fetchedEvents.sort((a, b) => a.ts - b.ts); this.persistance.store(...fetchedEvents); - - - this.dummyTestEventsIntegrity(); - console.log(this.domain, `Fetched all`); + this.orchestrator.logger.info(`Fetched all for domain ${this.domain}`); - return fetchedEvents + return fetchedEvents; } dummyTestEventsIntegrity() { // TODO: either drop or make better - // const p = this.persistance as RamPersistance; const h = new Map(); const r = new Map(); @@ -113,7 +114,7 @@ export class Indexer { let allEvents = this.persistance.allEvents(); if (allEvents.length === 0) { - console.warn(`No events to test integrity!!!`); + this.orchestrator.logger.warn(`No events to test integrity!!!`); return; } @@ -146,7 +147,6 @@ export class Indexer { h1 = newRoot; htotal -= 1; } else { - // console.log(this.domain, `Home broke with`, htotal, 'unresolved'); break; } } @@ -156,7 +156,6 @@ export class Indexer { r1 = newRoot; rtotal -= 1; } else { - // console.log(this.domain, `Replica broke with`, rtotal, 'unresolved'); break; } } @@ -170,7 +169,6 @@ export class Indexer { } async fetchHome(from: number, to: number) { - let fetchedEvents: NomadEvent[] = []; const home = this.home(); @@ -193,7 +191,7 @@ export class Indexer { message: event.args[4], }, event.blockNumber, - EventSource.Past, + EventSource.Fetch, ), ), ); @@ -218,14 +216,14 @@ export class Indexer { signature: event.args[3], }, event.blockNumber, - EventSource.Past, + EventSource.Fetch, ), ), ); fetchedEvents.push(...parsedEvents); } - return fetchedEvents + return fetchedEvents; } async fetchReplica(domain: number, from: number, to: number) { @@ -254,7 +252,7 @@ export class Indexer { signature: event.args[3], }, event.blockNumber, - EventSource.Past, + EventSource.Fetch, ), ), ); @@ -282,7 +280,7 @@ export class Indexer { returnData: event.args[2], }, event.blockNumber, - EventSource.Past, + EventSource.Fetch, ), ), ); @@ -290,7 +288,6 @@ export class Indexer { } return fetchedEvents; } - } export abstract class Persistance { diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index 120cddc7d..b29ac5b37 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -5,21 +5,17 @@ import * as dotenv from 'dotenv'; import { ethers } from 'ethers'; import { IndexerCollector } from './metrics'; import Logger from 'bunyan'; -dotenv.config({ - // path: '/Users/daniilnaumetc/code/tmp/monitor/typescript/nomad-monitor/src/indexer/.env' -}); +dotenv.config({}); const signer = process.env.SIGNER!; -const alchemyKey = process.env.ALCHEMY_KEY!; +// const alchemyKey = process.env.ALCHEMY_KEY!; const infuraKey = process.env.INFURA_KEY!; +const environment = 'development'; -console.log(signer, alchemyKey); - -const moonbeamRPC = 'https://moonbeam.api.onfinality.io/public'; //"https://rpc.api.moonbeam.network"; -// const ethereumRPC = `https://eth-mainnet.alchemyapi.io/v2/${alchemyKey}`; +const moonbeamRPC = 'https://moonbeam.api.onfinality.io/public'; (async () => { - const ctx = mainnet; //NomadContext.fromDomains([ethereum, moonbeam]); + const ctx = mainnet; const ethereumId = ctx.mustGetDomain('ethereum').id; const moonbeamId = ctx.mustGetDomain('moonbeam').id; const p = new ethers.providers.InfuraProvider('homestead', infuraKey); @@ -29,10 +25,11 @@ const moonbeamRPC = 'https://moonbeam.api.onfinality.io/public'; //"https://rpc. ctx.registerRpcProvider(moonbeamId, moonbeamRPC); ctx.registerWalletSigner(moonbeamId, signer); + const logger = createLogger('indexer', environment); const c = new Processor(); - const m = new IndexerCollector('development', createLogger('indexer', 'development')) + const m = new IndexerCollector(environment, logger); - const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0], m); + const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0], m, logger); await o.startConsuming(); })(); @@ -44,4 +41,4 @@ function createLogger(name: string, environment: string) { level: 'debug', environment: environment, }); -} \ No newline at end of file +} diff --git a/typescript/nomad-monitor/src/indexer/metrics.ts b/typescript/nomad-monitor/src/indexer/metrics.ts index 510de73d7..e5c33a8d9 100644 --- a/typescript/nomad-monitor/src/indexer/metrics.ts +++ b/typescript/nomad-monitor/src/indexer/metrics.ts @@ -2,7 +2,7 @@ import { MetricsCollector } from '../metrics'; import { Gauge } from 'prom-client'; import Logger from 'bunyan'; -const prefix = `fancy_monitor` +const prefix = `fancy_monitor`; export class IndexerCollector extends MetricsCollector { private numDispatchedGauge: Gauge; @@ -33,41 +33,41 @@ export class IndexerCollector extends MetricsCollector { }); this.numRelayedGauge = new Gauge({ - name: prefix + '_number_messages_relayed', - help: 'Gauge that indicates how many messages have been relayed for a network.', - labelNames: ['network', 'environment'], + name: prefix + '_number_messages_relayed', + help: 'Gauge that indicates how many messages have been relayed for a network.', + labelNames: ['network', 'environment'], }); this.numProcessedGauge = new Gauge({ - name: prefix + '_number_messages_processed', - help: 'Gauge that indicates how many messages have been processed for a network.', - labelNames: ['network', 'environment'], + name: prefix + '_number_messages_processed', + help: 'Gauge that indicates how many messages have been processed for a network.', + labelNames: ['network', 'environment'], }); // Time this.meanUpdateTimeGauge = new Gauge({ - name: prefix + '_mean_update_time', - help: 'Gauge that indicates how long does it take to move from dispatched to updated.', - labelNames: ['network', 'environment'], + name: prefix + '_mean_update_time', + help: 'Gauge that indicates how long does it take to move from dispatched to updated.', + labelNames: ['network', 'environment'], }); - + this.meanRelayTimeGauge = new Gauge({ - name: prefix + '_mean_relay_time', - help: 'Gauge that indicates how long does it take to move from updated to relayed.', - labelNames: ['network', 'environment'], + name: prefix + '_mean_relay_time', + help: 'Gauge that indicates how long does it take to move from updated to relayed.', + labelNames: ['network', 'environment'], }); - + this.meanProcessTimeGauge = new Gauge({ - name: prefix + '_mean_process_time', - help: 'Gauge that indicates how long does it take to move from relayed to processed.', - labelNames: ['network', 'environment'], + name: prefix + '_mean_process_time', + help: 'Gauge that indicates how long does it take to move from relayed to processed.', + labelNames: ['network', 'environment'], }); - + this.meanEndToEndTimeGauge = new Gauge({ - name: prefix + '_mean_e2e_time', - help: 'Gauge that indicates how long does it take to move from dispatched to processed.', - labelNames: ['network', 'environment'], + name: prefix + '_mean_e2e_time', + help: 'Gauge that indicates how long does it take to move from dispatched to processed.', + labelNames: ['network', 'environment'], }); } @@ -89,40 +89,40 @@ export class IndexerCollector extends MetricsCollector { { network, environment: this.environment }, dispatched, ); - + this.numUpdatedGauge.set( { network, environment: this.environment }, updated, ); - + this.numRelayedGauge.set( { network, environment: this.environment }, relayed, ); this.numProcessedGauge.set( - { network, environment: this.environment }, - processed, + { network, environment: this.environment }, + processed, ); this.meanUpdateTimeGauge.set( - { network, environment: this.environment }, - updateTime, + { network, environment: this.environment }, + updateTime, ); this.meanRelayTimeGauge.set( - { network, environment: this.environment }, - relayTime, + { network, environment: this.environment }, + relayTime, ); this.meanProcessTimeGauge.set( - { network, environment: this.environment }, - processTime, + { network, environment: this.environment }, + processTime, ); this.meanEndToEndTimeGauge.set( - { network, environment: this.environment }, - e2eTime, + { network, environment: this.environment }, + e2eTime, ); } } diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index 0d18daa53..1e50cc152 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -1,4 +1,5 @@ import { NomadContext } from '@nomad-xyz/sdk'; +import Logger from 'bunyan'; import { Consumer } from './consumer'; import { Indexer } from './indexer'; import { IndexerCollector } from './metrics'; @@ -13,35 +14,45 @@ export class Orchestrator { done: boolean; freshStart: boolean; metrics: IndexerCollector; - - constructor(sdk: NomadContext, c: Consumer, gov: number, metrics: IndexerCollector) { + logger: Logger; + + constructor( + sdk: NomadContext, + c: Consumer, + gov: number, + metrics: IndexerCollector, + logger: Logger, + ) { this.sdk = sdk; this.consumer = c; this.indexers = new Map(); this.gov = gov; this.done = false; this.freshStart = true; - this.metrics = metrics - - this.initIndexers() + this.metrics = metrics; + this.logger = logger; - this.initalFeedConsumer() + this.initIndexers(); + this.initalFeedConsumer(); } async indexAll() { - const events = (await Promise.all( - this.sdk.domainNumbers.map((domain: number) => this.index(domain)), - )).flat(); - events.sort((a, b) => a.ts-b.ts); + const events = ( + await Promise.all( + this.sdk.domainNumbers.map((domain: number) => this.index(domain)), + ) + ).flat(); + events.sort((a, b) => a.ts - b.ts); + this.logger.info(`Received ${events.length} events after reindexing`); this.consumer.consume(...events); } async index(domain: number) { let indexer = this.indexers.get(domain)!; - let replicas = [] + let replicas = []; if (domain === this.gov) { - replicas = this.sdk.domainNumbers.filter((d) => d != this.gov) + replicas = this.sdk.domainNumbers.filter((d) => d != this.gov); } else { replicas = [this.gov]; } @@ -50,8 +61,10 @@ export class Orchestrator { } initalFeedConsumer() { - const events = Array.from(this.indexers.values()).map(indexer => indexer.persistance.allEvents()).flat(); - events.sort((a, b) => a.ts-b.ts); + const events = Array.from(this.indexers.values()) + .map((indexer) => indexer.persistance.allEvents()) + .flat(); + events.sort((a, b) => a.ts - b.ts); this.consumer.consume(...events); } @@ -64,40 +77,34 @@ export class Orchestrator { async startConsuming() { while (!this.done) { - console.log(`Started to reindex`) + this.logger.info(`Started to reindex`); const start = new Date().valueOf(); - await this.indexAll() - console.log(`Finished reindexing after seconds:`, (new Date().valueOf() - start) / 1000); + await this.indexAll(); + this.logger.info( + `Finished reindexing after ${ + (new Date().valueOf() - start) / 1000 + } seconds`, + ); const stats = this.consumer.stats(); - console.log(`stats->`, JSON.stringify(stats, replacer)); + this.logger.debug(JSON.stringify(stats, replacer)); this.reportAllMetrics(stats); - + await sleep(30000); } } reportAllMetrics(statistics: Statistics) { for (const domain of this.sdk.domainNumbers) { - this.reportMetrics(domain, statistics) + this.reportMetrics(domain, statistics); } } reportMetrics(domain: number, statistics: Statistics) { const { - counts: { - dispatched, - updated, - relayed, - processed, - }, - timings: { - meanUpdate, - meanRelay, - meanProcess, - meanE2E, - } + counts: { dispatched, updated, relayed, processed }, + timings: { meanUpdate, meanRelay, meanProcess, meanE2E }, } = statistics.forDomain(domain); this.metrics.setNetworkState( this.sdk.getDomain(domain)!.name, @@ -109,7 +116,7 @@ export class Orchestrator { meanRelay, meanProcess, meanE2E, - ) + ); } stop() { diff --git a/typescript/nomad-monitor/src/indexer/types.ts b/typescript/nomad-monitor/src/indexer/types.ts index 14c9a1f44..75db0aeae 100644 --- a/typescript/nomad-monitor/src/indexer/types.ts +++ b/typescript/nomad-monitor/src/indexer/types.ts @@ -1,119 +1,122 @@ - export class Mean { - count: number; - total: number; - constructor() { - this.count = 0; - this.total = 0; - } - add(value: number) { - this.count += 1; - this.total += value; - } - mean(): number { - return this.total/this.count - } + count: number; + total: number; + constructor() { + this.count = 0; + this.total = 0; + } + add(value: number) { + this.count += 1; + this.total += value; + } + mean(): number { + return this.total / this.count; + } } - - - export class BasicCountStages { - dispatched: number; - updated: number; - relayed: number; - processed: number; - constructor() { - this.dispatched = 0; - this.updated = 0; - this.relayed = 0; - this.processed = 0; - } - toObject() { - return { - dispatched: this.dispatched, - updated: this.updated, - relayed: this.relayed, - processed: this.processed, - } - } +export class BasicCountStages { + dispatched: number; + updated: number; + relayed: number; + processed: number; + constructor() { + this.dispatched = 0; + this.updated = 0; + this.relayed = 0; + this.processed = 0; } - export class BasicTiming { - meanUpdate: Mean; - meanRelay: Mean; - meanProcess: Mean; - meanE2E: Mean; - constructor() { - this.meanUpdate = new Mean(); - this.meanRelay = new Mean(); - this.meanProcess = new Mean(); - this.meanE2E = new Mean(); - } + toObject() { + return { + dispatched: this.dispatched, + updated: this.updated, + relayed: this.relayed, + processed: this.processed, + }; + } +} - toObject() { - return { - meanUpdate: this.meanUpdate.mean(), - meanRelay: this.meanRelay.mean(), - meanProcess: this.meanProcess.mean(), - meanE2E: this.meanE2E.mean(), - } - } +export class BasicTiming { + meanUpdate: Mean; + meanRelay: Mean; + meanProcess: Mean; + meanE2E: Mean; + constructor() { + this.meanUpdate = new Mean(); + this.meanRelay = new Mean(); + this.meanProcess = new Mean(); + this.meanE2E = new Mean(); } - - export class RootTimingStatistic { - total: BasicTiming; - domainStatistics: Map; - constructor(domains: number[]) { - this.total = new BasicTiming(); - this.domainStatistics = new Map(domains.map(d=>[d, new BasicTiming()])) - } - toObject() { - return { - total: this.total.toObject(), - domainStatistics: Array.from(this.domainStatistics.entries()).map(([d, v]) => [d, v.toObject()]), - } - } + toObject() { + return { + meanUpdate: this.meanUpdate.mean(), + meanRelay: this.meanRelay.mean(), + meanProcess: this.meanProcess.mean(), + meanE2E: this.meanE2E.mean(), + }; } +} - export class RootCountStagesStatistic { - total: BasicCountStages; - domainStatistics: Map; - constructor(domains: number[]) { - this.total = new BasicCountStages(); - this.domainStatistics = new Map(domains.map(d=>[d, new BasicCountStages()])) - } +export class RootTimingStatistic { + total: BasicTiming; + domainStatistics: Map; + constructor(domains: number[]) { + this.total = new BasicTiming(); + this.domainStatistics = new Map(domains.map((d) => [d, new BasicTiming()])); + } - toObject() { - return { - total: this.total.toObject(), - domainStatistics: Array.from(this.domainStatistics.entries()).map(([d, v]) => [d, v.toObject()]), - } - } + toObject() { + return { + total: this.total.toObject(), + domainStatistics: Array.from(this.domainStatistics.entries()).map( + ([d, v]) => [d, v.toObject()], + ), + }; } +} - - export class Statistics { - counts: RootCountStagesStatistic; - timings: RootTimingStatistic; - constructor(domains: number[]) { - this.counts = new RootCountStagesStatistic(domains); - this.timings = new RootTimingStatistic(domains); - } +export class RootCountStagesStatistic { + total: BasicCountStages; + domainStatistics: Map; + constructor(domains: number[]) { + this.total = new BasicCountStages(); + this.domainStatistics = new Map( + domains.map((d) => [d, new BasicCountStages()]), + ); + } + + toObject() { + return { + total: this.total.toObject(), + domainStatistics: Array.from(this.domainStatistics.entries()).map( + ([d, v]) => [d, v.toObject()], + ), + }; + } +} - toObject() { - return { - counts: this.counts.toObject(), - timings: this.timings.toObject(), - } - } +export class Statistics { + counts: RootCountStagesStatistic; + timings: RootTimingStatistic; + constructor(domains: number[]) { + this.counts = new RootCountStagesStatistic(domains); + this.timings = new RootTimingStatistic(domains); + } + + toObject() { + return { + counts: this.counts.toObject(), + timings: this.timings.toObject(), + }; + } - forDomain(domain: number){ - const counts = this.counts.domainStatistics.get(domain)!.toObject(); - const timings = this.timings.domainStatistics.get(domain)!.toObject(); - return { - counts, timings - } - } + forDomain(domain: number) { + const counts = this.counts.domainStatistics.get(domain)!.toObject(); + const timings = this.timings.domainStatistics.get(domain)!.toObject(); + return { + counts, + timings, + }; } - \ No newline at end of file +} diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index 9273ac9ab..b627dd593 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -94,4 +94,4 @@ export class KVCache { export function logToFile(s: string) { fs.appendFileSync('/tmp/log.log', s + '\n'); -} \ No newline at end of file +} From dd3790ae3b8059214665d64b0fd938ed9f39dcff Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 19 Jan 2022 19:39:16 +0100 Subject: [PATCH 11/63] chore: reorg stuff --- typescript/nomad-monitor/src/indexer/main.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index b29ac5b37..140ab1812 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -7,24 +7,20 @@ import { IndexerCollector } from './metrics'; import Logger from 'bunyan'; dotenv.config({}); -const signer = process.env.SIGNER!; -// const alchemyKey = process.env.ALCHEMY_KEY!; const infuraKey = process.env.INFURA_KEY!; -const environment = 'development'; +const moonbeamRPC = process.env.MOONBEAM_RPC!; +const environment = process.env.ENVIRONMENT!; -const moonbeamRPC = 'https://moonbeam.api.onfinality.io/public'; (async () => { const ctx = mainnet; const ethereumId = ctx.mustGetDomain('ethereum').id; const moonbeamId = ctx.mustGetDomain('moonbeam').id; - const p = new ethers.providers.InfuraProvider('homestead', infuraKey); + const infura = new ethers.providers.InfuraProvider('homestead', infuraKey); - ctx.registerProvider(ethereumId, p); - ctx.registerWalletSigner(ethereumId, signer); + ctx.registerProvider(ethereumId, infura); ctx.registerRpcProvider(moonbeamId, moonbeamRPC); - ctx.registerWalletSigner(moonbeamId, signer); const logger = createLogger('indexer', environment); const c = new Processor(); const m = new IndexerCollector(environment, logger); From 0977f8262dd9f09fa5fcf4c77b03aa7beda1cbe3 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Fri, 21 Jan 2022 12:45:36 +0100 Subject: [PATCH 12/63] feature: db for messages --- .../nomad-monitor/src/indexer/consumer.ts | 183 +++++++++++++++++- typescript/nomad-monitor/src/indexer/event.ts | 8 +- .../nomad-monitor/src/indexer/indexer.ts | 39 ++-- typescript/nomad-monitor/src/indexer/main.ts | 4 +- .../1642713407510_my-first-migration.js | 37 ++++ .../nomad-monitor/src/indexer/orchestrator.ts | 17 +- .../src/nomad/messages/BridgeMessage.ts | 2 +- 7 files changed, 261 insertions(+), 29 deletions(-) create mode 100644 typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 8293c1919..332723ce0 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -1,3 +1,4 @@ +import { parseMessage } from '@nomad-xyz/sdk/src/nomad/messages/NomadMessage'; import { ethers } from 'ethers'; import { EventType, NomadEvent } from './event'; import { Statistics } from './types'; @@ -105,7 +106,7 @@ class StatisticsCollector { } export abstract class Consumer { - abstract consume(...evens: NomadEvent[]): void; + abstract consume(...evens: NomadEvent[]): Promise; abstract stats(): Statistics; } @@ -176,6 +177,10 @@ class Timings { } } +function bytes32ToAddress(s: string) { + return '0x'+s.slice(26) +} + class NomadMessage { origin: number; destination: number; @@ -183,7 +188,16 @@ class NomadMessage { hash: string; leafIndex: ethers.BigNumber; destinationAndNonce: ethers.BigNumber; - message: string; + raw: string; + nomadSender: string; + nomadRecipient: string; + bridgeMsgType?: string; + bridgeMsgTo?: string; + bridgeMsgAmount?: ethers.BigNumber; + bridgeMsgAllowFast?: boolean; + bridgeMsgDetailsHash?: string; + bridgeMsgTokenDomain?: number; + bridgeMsgTokenId?: string; state: MsgState; timings: Timings; @@ -203,8 +217,23 @@ class NomadMessage { this.hash = hash; this.leafIndex = leafIndex; this.destinationAndNonce = destinationAndNonce; - this.message = message; - + this.raw = message; + const parsed = parseMessage(message); + this.nomadSender = parsed.sender; + this.nomadRecipient = parsed.recipient; + try { + const bridgeMessage = parseBody(parsed.body); + this.bridgeMsgType = bridgeMessage.action.type as string; + this.bridgeMsgTo = bridgeMessage.action.to; + this.bridgeMsgAmount = bridgeMessage.action.amount; + this.bridgeMsgAllowFast = bridgeMessage.action.allowFast; + this.bridgeMsgDetailsHash = bridgeMessage.action.detailsHash; + this.bridgeMsgTokenDomain = bridgeMessage.token.domain as number; + this.bridgeMsgTokenId = bridgeMessage.token.id as string; + } catch(e) { + // pass + } + this.state = MsgState.Dispatched; this.timings = new Timings(createdAt); } @@ -212,6 +241,109 @@ class NomadMessage { get originAndRoot(): string { return `${this.origin}${this.root}`; } + + intoDB(): [string, number, number, string, string, string, number, number, number, number, number, string | undefined, string | undefined, string | undefined, boolean | undefined, string | undefined , number | undefined, string | undefined + ] { + return [ + this.hash, + this.origin, + this.destination, + bytes32ToAddress(this.nomadSender), + bytes32ToAddress(this.nomadRecipient), + this.root, + this.state, + this.timings.dispatchedAt, + this.timings.updatedAt, + this.timings.relayedAt, + this.timings.processedAt, + this.bridgeMsgType, + this.bridgeMsgTo ? bytes32ToAddress(this.bridgeMsgTo): undefined, + this.bridgeMsgAmount?.toString(), + this.bridgeMsgAllowFast, + this.bridgeMsgDetailsHash, + this.bridgeMsgTokenDomain, + this.bridgeMsgTokenId ? bytes32ToAddress(this.bridgeMsgTokenId): undefined, + ] + } +} + +import {Pool} from 'pg'; +import { parseBody } from '@nomad-xyz/sdk/src/nomad/messages/BridgeMessage'; + +// expand(3, 2) returns "($1, $2), ($3, $4), ($5, $6)" +function expand(rowCount: number, columnCount: number, startAt=1){ + var index = startAt + return Array(rowCount).fill(0).map(v => `(${Array(columnCount).fill(0).map(v => `$${index++}`).join(", ")})`).join(", ") +} + +class DBDriver { + pool: Pool; + syncedOnce: boolean; + + constructor() { + this.pool = new Pool(); + this.syncedOnce = false; + } + + async connect() { + await this.pool.connect() + } + + get startupSync() { + const value = this.syncedOnce; + this.syncedOnce = true; + return !value; + } + + async insert(messages: NomadMessage[]) { + const rows = messages.length; + if (!rows) return ; + const columns = 18; + const query = `INSERT INTO messages (hash, origin, destination, sender, recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, bridge_msg_to, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id) VALUES ${expand(rows, columns)};`; + const values = messages.map(m => m.intoDB()).flat(); + return await this.pool.query(query, values) + } + + async update(messages: NomadMessage[]) { + const rows = messages.length; + if (!rows) return ; + const promises = messages.map(m => { + const query = `UPDATE messages SET + origin = $2, + destination = $3, + sender = $4, + recipient = $5, + root = $6, + state = $7, + dispatched_at = $8, + updated_at = $9, + relayed_at = $10, + processed_at = $11, + bridge_msg_type = $12, + bridge_msg_to = $13, + bridge_msg_amount = $14, + bridge_msg_allow_fast = $15, + bridge_msg_details_hash = $16, + bridge_msg_token_domain = $17, + bridge_msg_token_id = $18 + WHERE hash = $1 + `; + return this.pool.query(query, m.intoDB()); + }) + + return await Promise.all(promises); + } + + async getExistingHashes(): Promise { + const res = await this.pool.query(`select hash from messages;`); + return res.rows.map(r => r.hash) as string[] + } + +} + +enum DBAction{ + Insert, + Update, } export class Processor extends Consumer { @@ -220,6 +352,9 @@ export class Processor extends Consumer { msgByOriginAndRoot: Map; consumed: number; // for debug domains: number[]; + syncInsertQueue: string[]; + syncUpdateQueue: string[]; + db: DBDriver; constructor() { super(); @@ -228,9 +363,13 @@ export class Processor extends Consumer { this.msgByOriginAndRoot = new Map(); this.consumed = 0; this.domains = []; + this.syncInsertQueue = []; + this.syncUpdateQueue = []; + + this.db = new DBDriver(); } - consume(...events: NomadEvent[]): void { + async consume(...events: NomadEvent[]): Promise { for (const event of events) { if (event.eventType === EventType.HomeDispatch) { this.dispatched(event); @@ -244,6 +383,36 @@ export class Processor extends Consumer { this.consumed += 1; } + + await this.sync() + } + + async sync() { + const [inserts, updates] = await this.getMsgForSync(); + console.log(inserts.filter(i => !i).length) + await Promise.all([this.db.insert(inserts), this.db.update(updates)]) + } + + addToSyncQueue(hash: string, action: DBAction) { + if (this.syncInsertQueue.indexOf(hash) < 0 && this.syncUpdateQueue.indexOf(hash) < 0) { + if (action == DBAction.Insert) this.syncInsertQueue.push(hash) + else this.syncUpdateQueue.push(hash) + } + } + + async getMsgForSync(): Promise<[NomadMessage[], NomadMessage[]]> { + let existingHashes: string[] = []; + if (this.db.startupSync) { + existingHashes = await this.db.getExistingHashes(); + this.syncUpdateQueue.push( + ...this.syncInsertQueue.filter(hash => existingHashes.indexOf(hash) >= 0) + ) + } + const inserts = this.syncInsertQueue.filter(hash => this.syncUpdateQueue.indexOf(hash) < 0).map(hash => this.getMsg(hash)!).filter(m=>!!m); + this.syncInsertQueue = []; + const updates = this.syncUpdateQueue.map(hash => this.getMsg(hash)!).filter(m=>!!m); + this.syncUpdateQueue = []; + return [inserts, updates]; } dispatched(e: NomadEvent) { @@ -258,6 +427,7 @@ export class Processor extends Consumer { e.ts, ); this.add(m); + this.addToSyncQueue(m.hash, DBAction.Insert); if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } @@ -269,6 +439,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Updated) { m.state = MsgState.Updated; m.timings.updated(e.ts); + this.addToSyncQueue(m.hash, DBAction.Update); } }); } @@ -283,6 +454,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Relayed) { m.state = MsgState.Relayed; m.timings.relayed(e.ts); + this.addToSyncQueue(m.hash, DBAction.Update); } }); } @@ -293,6 +465,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Processed) { m.state = MsgState.Processed; m.timings.processed(e.ts); + this.addToSyncQueue(m.hash, DBAction.Update); } } } diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index 501dc2439..e05c34bd7 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -113,13 +113,15 @@ export class NomadEvent { } function parseDestinationAndNonce( - h: ethers.BigNumber | { hex: string }, + h: ethers.BigNumber | { hex?: string, _hex?: string } ): [number, number] { let hexString = ''; if (h instanceof ethers.BigNumber) { hexString = h.toHexString(); - } else { - hexString = h.hex; + } else{ + const hex = h.hex || h._hex; + if (!hex) throw new Error(`Has no hex: ${JSON.stringify(h)}`); + hexString = hex; } const without0x = hexString.slice(2); diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index f1be17c4c..3a6ea72ed 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -1,5 +1,5 @@ import { Orchestrator } from './orchestrator'; -import { NomadContext } from '@nomad-xyz/sdk'; +import { NomadContext } from '@nomad-xyz/sdk/src'; import fs from 'fs'; import { ContractType, EventType, NomadEvent, EventSource } from './event'; import { Home, Replica } from '@nomad-xyz/contract-interfaces/core'; @@ -301,7 +301,7 @@ export abstract class Persistance { } abstract store(...events: NomadEvent[]): void; - abstract init(): Promise; + abstract init(): Promise; abstract sortSorage(): void; abstract allEvents(): NomadEvent[]; abstract persist(): void; @@ -315,13 +315,13 @@ export class FilePersistance extends Persistance { } store(...events: NomadEvent[]): void {} - async init(): Promise { + async init(): Promise { if (fs.existsSync(this.path)) { this.from = 13; this.height = 14; - return true; + return ; } else { - return false; + return ; } } sortSorage() {} @@ -341,9 +341,7 @@ export class RamPersistance extends Persistance { this.block2events = new Map(); this.blocks = []; this.storePath = storePath; - try { - this.load(); - } catch (_) {} + } updateFromTo(block: number) { @@ -367,8 +365,11 @@ export class RamPersistance extends Persistance { }); this.persist(); } - async init(): Promise { - return true; + async init(): Promise { + try { + await this.load(); + } catch (_) {} + return; } sortSorage() { this.blocks = this.blocks.sort(); @@ -378,7 +379,7 @@ export class RamPersistance extends Persistance { return new EventsRange(this); } - persist() { + persistToFile() { fs.writeFileSync( this.storePath, JSON.stringify( @@ -395,7 +396,21 @@ export class RamPersistance extends Persistance { ); } - load() { + persistToDB() { + + } + + persist() { + this.persistToFile(); + this.persistToDB() + } + + async load() { + this.loadFromFile() + } + + loadFromFile() { + const object = JSON.parse( fs.readFileSync(this.storePath, 'utf8'), reviver, diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index 140ab1812..ca9634e13 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -1,4 +1,4 @@ -import { mainnet } from '@nomad-xyz/sdk'; +import { mainnet } from '@nomad-xyz/sdk/src'; import { Processor } from './consumer'; import { Orchestrator } from './orchestrator'; import * as dotenv from 'dotenv'; @@ -11,6 +11,7 @@ const infuraKey = process.env.INFURA_KEY!; const moonbeamRPC = process.env.MOONBEAM_RPC!; const environment = process.env.ENVIRONMENT!; +console.log(infuraKey, moonbeamRPC, environment); (async () => { const ctx = mainnet; @@ -26,6 +27,7 @@ const environment = process.env.ENVIRONMENT!; const m = new IndexerCollector(environment, logger); const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0], m, logger); + await o.init(); await o.startConsuming(); })(); diff --git a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js new file mode 100644 index 000000000..73fc9be6f --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js @@ -0,0 +1,37 @@ +/* eslint-disable camelcase */ + +exports.shorthands = undefined; + +exports.up = pgm => { + pgm.createTable('messages', { + id: 'id', + hash: {type: 'varchar(66)', notNull: true}, + origin: {type: 'integer', notNull: true}, + destination: {type: 'integer', notNull: true}, + sender: {type: 'varchar(42)', notNull: true}, + recipient: {type: 'varchar(42)', notNull: true}, + root: {type: 'varchar(66)', notNull: true}, + state: {type: 'integer', notNull: true}, + dispatched_at: {type: 'bigint', notNull: true}, + updated_at: {type: 'bigint', notNull: true}, + relayed_at: {type: 'bigint', notNull: true}, + processed_at: {type: 'bigint', notNull: true}, + bridge_msg_type: {type: 'varchar(14)', notNull: false}, + bridge_msg_to: {type: 'varchar(42)', notNull: false}, + bridge_msg_amount: {type: 'varchar(256)', notNull: false}, + bridge_msg_allow_fast: {type: 'boolean', notNull: false}, + bridge_msg_details_hash: {type: 'varchar(66)', notNull: false}, + bridge_msg_token_domain: {type: 'integer', notNull: false}, + bridge_msg_token_id: {type: 'varchar(42)', notNull: false}, + createdAt: { + type: 'timestamp', + notNull: true, + default: pgm.func('current_timestamp'), + }, + }) + pgm.createIndex('messages', 'id') +}; + +exports.down = pgm => { + pgm.dropTable('messages') +}; diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index 1e50cc152..6ca5a6baf 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -1,4 +1,4 @@ -import { NomadContext } from '@nomad-xyz/sdk'; +import { NomadContext } from '@nomad-xyz/sdk/src'; import Logger from 'bunyan'; import { Consumer } from './consumer'; import { Indexer } from './indexer'; @@ -31,9 +31,11 @@ export class Orchestrator { this.freshStart = true; this.metrics = metrics; this.logger = logger; + } - this.initIndexers(); - this.initalFeedConsumer(); + async init() { + await this.initIndexers(); + await this.initalFeedConsumer(); } async indexAll() { @@ -44,7 +46,7 @@ export class Orchestrator { ).flat(); events.sort((a, b) => a.ts - b.ts); this.logger.info(`Received ${events.length} events after reindexing`); - this.consumer.consume(...events); + await this.consumer.consume(...events); } async index(domain: number) { @@ -60,17 +62,18 @@ export class Orchestrator { return await indexer.updateAll(replicas); } - initalFeedConsumer() { + async initalFeedConsumer() { const events = Array.from(this.indexers.values()) .map((indexer) => indexer.persistance.allEvents()) .flat(); events.sort((a, b) => a.ts - b.ts); - this.consumer.consume(...events); + await this.consumer.consume(...events); } - initIndexers() { + async initIndexers() { for (const domain of this.sdk.domainNumbers) { const indexer = new Indexer(domain, this.sdk, this); + await indexer.init(); this.indexers.set(domain, indexer); } } diff --git a/typescript/nomad-sdk/src/nomad/messages/BridgeMessage.ts b/typescript/nomad-sdk/src/nomad/messages/BridgeMessage.ts index a3177068e..20d94225f 100644 --- a/typescript/nomad-sdk/src/nomad/messages/BridgeMessage.ts +++ b/typescript/nomad-sdk/src/nomad/messages/BridgeMessage.ts @@ -50,7 +50,7 @@ function parseAction(buf: Uint8Array): Action { throw new Error('Bad action'); } -function parseBody(messageBody: string): ParsedTransferMessage { +export function parseBody(messageBody: string): ParsedTransferMessage { const buf = arrayify(messageBody); const tokenId = buf.slice(0, 36); From ecf37f59dbbffe850cb4625c978ffe4f9f686f88 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Fri, 21 Jan 2022 18:05:48 +0100 Subject: [PATCH 13/63] feature: more db --- .../nomad-monitor/src/indexer/consumer.ts | 143 +++++------------- typescript/nomad-monitor/src/indexer/db.ts | 87 +++++++++++ .../nomad-monitor/src/indexer/indexer.ts | 62 +++++--- typescript/nomad-monitor/src/indexer/main.ts | 11 +- .../1642713407510_my-first-migration.js | 22 ++- .../nomad-monitor/src/indexer/orchestrator.ts | 4 + typescript/nomad-monitor/src/indexer/utils.ts | 31 ++-- 7 files changed, 215 insertions(+), 145 deletions(-) create mode 100644 typescript/nomad-monitor/src/indexer/db.ts diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 332723ce0..0f4670945 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -2,6 +2,9 @@ import { parseMessage } from '@nomad-xyz/sdk/src/nomad/messages/NomadMessage'; import { ethers } from 'ethers'; import { EventType, NomadEvent } from './event'; import { Statistics } from './types'; +import { parseBody } from '@nomad-xyz/sdk/src/nomad/messages/BridgeMessage'; +import { DBDriver } from './db'; + class StatisticsCollector { s: Statistics; @@ -181,7 +184,7 @@ function bytes32ToAddress(s: string) { return '0x'+s.slice(26) } -class NomadMessage { +export class NomadMessage { origin: number; destination: number; root: string; @@ -267,84 +270,9 @@ class NomadMessage { } } -import {Pool} from 'pg'; -import { parseBody } from '@nomad-xyz/sdk/src/nomad/messages/BridgeMessage'; - -// expand(3, 2) returns "($1, $2), ($3, $4), ($5, $6)" -function expand(rowCount: number, columnCount: number, startAt=1){ - var index = startAt - return Array(rowCount).fill(0).map(v => `(${Array(columnCount).fill(0).map(v => `$${index++}`).join(", ")})`).join(", ") -} -class DBDriver { - pool: Pool; - syncedOnce: boolean; - - constructor() { - this.pool = new Pool(); - this.syncedOnce = false; - } - - async connect() { - await this.pool.connect() - } - - get startupSync() { - const value = this.syncedOnce; - this.syncedOnce = true; - return !value; - } - - async insert(messages: NomadMessage[]) { - const rows = messages.length; - if (!rows) return ; - const columns = 18; - const query = `INSERT INTO messages (hash, origin, destination, sender, recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, bridge_msg_to, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id) VALUES ${expand(rows, columns)};`; - const values = messages.map(m => m.intoDB()).flat(); - return await this.pool.query(query, values) - } - - async update(messages: NomadMessage[]) { - const rows = messages.length; - if (!rows) return ; - const promises = messages.map(m => { - const query = `UPDATE messages SET - origin = $2, - destination = $3, - sender = $4, - recipient = $5, - root = $6, - state = $7, - dispatched_at = $8, - updated_at = $9, - relayed_at = $10, - processed_at = $11, - bridge_msg_type = $12, - bridge_msg_to = $13, - bridge_msg_amount = $14, - bridge_msg_allow_fast = $15, - bridge_msg_details_hash = $16, - bridge_msg_token_domain = $17, - bridge_msg_token_id = $18 - WHERE hash = $1 - `; - return this.pool.query(query, m.intoDB()); - }) - - return await Promise.all(promises); - } - - async getExistingHashes(): Promise { - const res = await this.pool.query(`select hash from messages;`); - return res.rows.map(r => r.hash) as string[] - } -} -enum DBAction{ - Insert, - Update, -} export class Processor extends Consumer { messages: NomadMessage[]; @@ -352,21 +280,19 @@ export class Processor extends Consumer { msgByOriginAndRoot: Map; consumed: number; // for debug domains: number[]; - syncInsertQueue: string[]; - syncUpdateQueue: string[]; + syncQueue: string[]; db: DBDriver; - constructor() { + constructor(db: DBDriver) { super(); this.messages = []; this.msgToIndex = new Map(); this.msgByOriginAndRoot = new Map(); this.consumed = 0; this.domains = []; - this.syncInsertQueue = []; - this.syncUpdateQueue = []; + this.syncQueue = []; - this.db = new DBDriver(); + this.db = db; } async consume(...events: NomadEvent[]): Promise { @@ -389,30 +315,37 @@ export class Processor extends Consumer { async sync() { const [inserts, updates] = await this.getMsgForSync(); - console.log(inserts.filter(i => !i).length) - await Promise.all([this.db.insert(inserts), this.db.update(updates)]) + await Promise.all([this.db.insertMessage(inserts), this.db.updateMessage(updates)]) } - addToSyncQueue(hash: string, action: DBAction) { - if (this.syncInsertQueue.indexOf(hash) < 0 && this.syncUpdateQueue.indexOf(hash) < 0) { - if (action == DBAction.Insert) this.syncInsertQueue.push(hash) - else this.syncUpdateQueue.push(hash) - } + addToSyncQueue(hash: string) { + if (this.syncQueue.indexOf(hash) < 0 ) this.syncQueue.push(hash) } async getMsgForSync(): Promise<[NomadMessage[], NomadMessage[]]> { - let existingHashes: string[] = []; - if (this.db.startupSync) { - existingHashes = await this.db.getExistingHashes(); - this.syncUpdateQueue.push( - ...this.syncInsertQueue.filter(hash => existingHashes.indexOf(hash) >= 0) - ) - } - const inserts = this.syncInsertQueue.filter(hash => this.syncUpdateQueue.indexOf(hash) < 0).map(hash => this.getMsg(hash)!).filter(m=>!!m); - this.syncInsertQueue = []; - const updates = this.syncUpdateQueue.map(hash => this.getMsg(hash)!).filter(m=>!!m); - this.syncUpdateQueue = []; - return [inserts, updates]; + let existingHashes = await this.db.getExistingHashes(); + + const insert: string[] = []; + const update: string[] = []; + + this.syncQueue.forEach(hash => { + if (existingHashes.indexOf(hash) < 0) { + insert.push(hash); + } else { + update.push(hash); + } + }); + + this.syncQueue = []; + + return [ + this.mapHashesToMessages(insert), + this.mapHashesToMessages(update), + ]; + } + + mapHashesToMessages(hashes: string[]): NomadMessage[] { + return hashes.map(hash => this.getMsg(hash)!).filter(m=>!!m); } dispatched(e: NomadEvent) { @@ -427,7 +360,7 @@ export class Processor extends Consumer { e.ts, ); this.add(m); - this.addToSyncQueue(m.hash, DBAction.Insert); + this.addToSyncQueue(m.hash); if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } @@ -439,7 +372,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Updated) { m.state = MsgState.Updated; m.timings.updated(e.ts); - this.addToSyncQueue(m.hash, DBAction.Update); + this.addToSyncQueue(m.hash); } }); } @@ -454,7 +387,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Relayed) { m.state = MsgState.Relayed; m.timings.relayed(e.ts); - this.addToSyncQueue(m.hash, DBAction.Update); + this.addToSyncQueue(m.hash); } }); } @@ -465,7 +398,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Processed) { m.state = MsgState.Processed; m.timings.processed(e.ts); - this.addToSyncQueue(m.hash, DBAction.Update); + this.addToSyncQueue(m.hash); } } } diff --git a/typescript/nomad-monitor/src/indexer/db.ts b/typescript/nomad-monitor/src/indexer/db.ts new file mode 100644 index 000000000..569f8d7e0 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/db.ts @@ -0,0 +1,87 @@ +import { NomadMessage } from './consumer'; +import {Pool} from 'pg'; + +// expand(3, 2) returns "($1, $2), ($3, $4), ($5, $6)" +function expand(rowCount: number, columnCount: number, startAt=1){ + var index = startAt + return Array(rowCount).fill(0).map(v => `(${Array(columnCount).fill(0).map(v => `$${index++}`).join(", ")})`).join(", ") + } + + export class DBDriver { + pool: Pool; + syncedOnce: boolean; + + constructor() { + this.syncedOnce = false; + this.pool = new Pool();; + } + + async connect() { + await this.pool.connect() + } + + get startupSync() { + const value = this.syncedOnce; + this.syncedOnce = true; + return !value; + } + + async insertMessage(messages: NomadMessage[]) { + const rows = messages.length; + if (!rows) return ; + const columns = 18; + const query = `INSERT INTO messages (hash, origin, destination, sender, recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, bridge_msg_to, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id) VALUES ${expand(rows, columns)};`; + const values = messages.map(m => m.intoDB()).flat(); + return await this.pool.query(query, values) + } + + async updateMessage(messages: NomadMessage[]) { + const rows = messages.length; + if (!rows) return ; + const promises = messages.map(m => { + const query = `UPDATE messages SET + origin = $2, + destination = $3, + sender = $4, + recipient = $5, + root = $6, + state = $7, + dispatched_at = $8, + updated_at = $9, + relayed_at = $10, + processed_at = $11, + bridge_msg_type = $12, + bridge_msg_to = $13, + bridge_msg_amount = $14, + bridge_msg_allow_fast = $15, + bridge_msg_details_hash = $16, + bridge_msg_token_domain = $17, + bridge_msg_token_id = $18 + WHERE hash = $1 + `; + return this.pool.query(query, m.intoDB()); + }) + + return await Promise.all(promises); + } + + async getExistingHashes(): Promise { + const res = await this.pool.query(`select hash from messages;`); + return res.rows.map(r => r.hash) as string[] + } + + async getAllKeyPair(namespace: string): Promise> { + const res = await this.pool.query(`select key, value from kv_storage where namespace = $1;`, [namespace]); + return new Map(res.rows.map(r => [r.key, r.value])) + } + + async setKeyPair(namespace: string, k: string, v: string): Promise { + await this.pool.query(`INSERT INTO kv_storage (namespace, key, value) + VALUES($1,$2,$3) + ON CONFLICT (namespace, key) + DO + UPDATE SET value = $3;`, [namespace, k, v]); + } + + } + \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index 3a6ea72ed..351acf248 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -11,7 +11,7 @@ export class Indexer { sdk: NomadContext; orchestrator: Orchestrator; persistance: Persistance; - block2timeCache: KVCache; + block2timeCache: KVCache; eventCallback: undefined | ((event: NomadEvent) => void); constructor(domain: number, sdk: NomadContext, orchestrator: Orchestrator) { @@ -19,10 +19,11 @@ export class Indexer { this.sdk = sdk; this.orchestrator = orchestrator; this.persistance = new RamPersistance( - `/tmp/persistance_${this.domain}.json`, + `/tmp/persistance_${this.domain}.json` ); this.block2timeCache = new KVCache( - `/tmp/persistance_blocktime_cache_${this.domain}`, + String(this.domain), + this.orchestrator.db ); } @@ -31,11 +32,11 @@ export class Indexer { } async getBlockTimestamp(blockNumber: number): Promise { - const possibleTime = this.block2timeCache.get(blockNumber); - if (possibleTime) return possibleTime; + const possibleTime = this.block2timeCache.get(String(blockNumber)); + if (possibleTime) return parseInt(possibleTime); const [block, error] = await retry( async () => await this.provider.getBlock(blockNumber), - 5, + 6, ); if (!block) { throw ( @@ -46,11 +47,12 @@ export class Indexer { ); } const time = block.timestamp * 1000; - this.block2timeCache.set(blockNumber, time); + await this.block2timeCache.set(String(blockNumber), String(time)); return time; } async init() { + await this.block2timeCache.init(); await this.persistance.init(); } @@ -80,24 +82,45 @@ export class Indexer { this.orchestrator.logger.info( `Fetching events for domain ${this.domain} from: ${from}, to: ${to}`, ); - const [fetchedEvents, error] = await retry(async () => { - const homeEvents = await this.fetchHome(from, to); - const replicasEvents = ( - await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))) - ).flat(); - return [...homeEvents, ...replicasEvents]; - }, 5); - if (error) throw error; - if (!fetchedEvents) throw new Error('kek'); + const fetchEvents = async (from: number, to: number) => { + const [fetchedEvents, error] = await retry(async () => { + const homeEvents = await this.fetchHome(from, to); + const replicasEvents = ( + await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))) + ).flat(); - fetchedEvents.sort((a, b) => a.ts - b.ts); - this.persistance.store(...fetchedEvents); + return [...homeEvents, ...replicasEvents] + }, 5); + + if (error) throw error; + return fetchedEvents + } + + const allEvents = []; + + const batchSize = 20000; + let batchFrom = from; + let batchTo = from + batchSize; + + while (true) { + const events = await fetchEvents(batchFrom, batchTo); + if (!events) throw new Error(`KEk`); + allEvents.push(...events); + if (batchTo >= to) break; + batchFrom = batchTo + 1; + batchTo = Math.min(to, batchFrom + batchSize); + } + + if (!allEvents) throw new Error('kek'); + + allEvents.sort((a, b) => a.ts - b.ts); + this.persistance.store(...allEvents); this.dummyTestEventsIntegrity(); this.orchestrator.logger.info(`Fetched all for domain ${this.domain}`); - return fetchedEvents; + return allEvents; } dummyTestEventsIntegrity() { @@ -341,7 +364,6 @@ export class RamPersistance extends Persistance { this.block2events = new Map(); this.blocks = []; this.storePath = storePath; - } updateFromTo(block: number) { diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index ca9634e13..a7500cddb 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -5,14 +5,14 @@ import * as dotenv from 'dotenv'; import { ethers } from 'ethers'; import { IndexerCollector } from './metrics'; import Logger from 'bunyan'; +import { DBDriver } from './db'; + dotenv.config({}); const infuraKey = process.env.INFURA_KEY!; const moonbeamRPC = process.env.MOONBEAM_RPC!; const environment = process.env.ENVIRONMENT!; -console.log(infuraKey, moonbeamRPC, environment); - (async () => { const ctx = mainnet; const ethereumId = ctx.mustGetDomain('ethereum').id; @@ -23,10 +23,13 @@ console.log(infuraKey, moonbeamRPC, environment); ctx.registerRpcProvider(moonbeamId, moonbeamRPC); const logger = createLogger('indexer', environment); - const c = new Processor(); + + const db = new DBDriver(); + + const c = new Processor(db); const m = new IndexerCollector(environment, logger); - const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0], m, logger); + const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0], m, logger, db); await o.init(); await o.startConsuming(); diff --git a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js index 73fc9be6f..803716be6 100644 --- a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js +++ b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js @@ -29,9 +29,27 @@ exports.up = pgm => { default: pgm.func('current_timestamp'), }, }) - pgm.createIndex('messages', 'id') + pgm.createIndex('messages', 'id'); + + pgm.createTable('kv_storage', { + id: 'id', + namespace: {type: 'varchar', notNull: true}, + key: {type: 'varchar', notNull: true}, + value: {type: 'varchar', notNull: true}, + createdAt: { + type: 'timestamp', + notNull: true, + default: pgm.func('current_timestamp'), + }, + }) + pgm.createIndex('kv_storage', 'id'); + pgm.addConstraint('kv_storage', 'unique_namespace_key', { + unique: ['namespace', 'key'] + }) }; exports.down = pgm => { - pgm.dropTable('messages') + pgm.dropTable('messages'); + pgm.dropConstraint('kv_storage', 'unique_namespace_key') + pgm.dropTable('kv_storage'); }; diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index 6ca5a6baf..23ebdd3b1 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -1,6 +1,7 @@ import { NomadContext } from '@nomad-xyz/sdk/src'; import Logger from 'bunyan'; import { Consumer } from './consumer'; +import { DBDriver } from './db'; import { Indexer } from './indexer'; import { IndexerCollector } from './metrics'; import { Statistics } from './types'; @@ -15,6 +16,7 @@ export class Orchestrator { freshStart: boolean; metrics: IndexerCollector; logger: Logger; + db: DBDriver; constructor( sdk: NomadContext, @@ -22,6 +24,7 @@ export class Orchestrator { gov: number, metrics: IndexerCollector, logger: Logger, + db: DBDriver ) { this.sdk = sdk; this.consumer = c; @@ -31,6 +34,7 @@ export class Orchestrator { this.freshStart = true; this.metrics = metrics; this.logger = logger; + this.db = db; } async init() { diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index b627dd593..4975b358b 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -2,6 +2,7 @@ import { ethers } from 'ethers'; import { NomadEvent } from './event'; import fs from 'fs'; import { Mean } from './types'; +import { DBDriver } from './db'; export function sleep(ms: number) { return new Promise((resolve) => { @@ -13,7 +14,7 @@ export async function retry( callback: () => Promise, tries: number, ): Promise<[T | undefined, any]> { - let timeout = 3000; + let timeout = 5000; let lastError: any = undefined; for (let attempt = 0; attempt < tries; attempt++) { try { @@ -62,32 +63,34 @@ export function reviver(key: any, value: any): any { return value; } -export class KVCache { - m: Map; - path: string; +export class KVCache { + m: Map; + name: string; + db: DBDriver; - constructor(path: string) { + constructor(name: string, db: DBDriver) { + this.db = db; this.m = new Map(); - this.path = path; - this.tryLoad(); + this.name = name; } - save() { - fs.writeFileSync(this.path, JSON.stringify(this.m, replacer)); + async init() { + await this.tryLoad(); } - tryLoad() { + async tryLoad() { try { - this.m = JSON.parse(fs.readFileSync(this.path, 'utf8'), reviver); + this.m = await this.db.getAllKeyPair(this.name) + } catch (_) {} } - set(k: K, v: V) { + async set(k: string, v: string) { this.m.set(k, v); - this.save(); + await this.db.setKeyPair(this.name, k, v); } - get(k: K): V | undefined { + get(k: string): string | undefined { return this.m.get(k); } } From 046e8a47d96bd649d106b567bb0ad94a8cccae68 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Sun, 23 Jan 2022 14:48:14 +0100 Subject: [PATCH 14/63] db and nonce --- .../nomad-monitor/src/indexer/consumer.ts | 72 +++++--- typescript/nomad-monitor/src/indexer/db.ts | 163 ++++++++++-------- typescript/nomad-monitor/src/indexer/event.ts | 10 +- .../nomad-monitor/src/indexer/indexer.ts | 45 +---- typescript/nomad-monitor/src/indexer/main.ts | 13 +- .../1642713407510_my-first-migration.js | 32 ++-- .../nomad-monitor/src/indexer/orchestrator.ts | 2 +- typescript/nomad-monitor/src/indexer/utils.ts | 3 +- 8 files changed, 182 insertions(+), 158 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 0f4670945..196bc53b0 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -4,7 +4,7 @@ import { EventType, NomadEvent } from './event'; import { Statistics } from './types'; import { parseBody } from '@nomad-xyz/sdk/src/nomad/messages/BridgeMessage'; import { DBDriver } from './db'; - +import Logger from 'bunyan'; class StatisticsCollector { s: Statistics; @@ -181,12 +181,13 @@ class Timings { } function bytes32ToAddress(s: string) { - return '0x'+s.slice(26) + return '0x' + s.slice(26); } export class NomadMessage { origin: number; destination: number; + nonce: number; root: string; hash: string; leafIndex: ethers.BigNumber; @@ -207,6 +208,7 @@ export class NomadMessage { constructor( origin: number, destination: number, + nonce: number, root: string, hash: string, leafIndex: ethers.BigNumber, @@ -216,6 +218,7 @@ export class NomadMessage { ) { this.origin = origin; this.destination = destination; + this.nonce = nonce; this.root = root; this.hash = hash; this.leafIndex = leafIndex; @@ -233,10 +236,10 @@ export class NomadMessage { this.bridgeMsgDetailsHash = bridgeMessage.action.detailsHash; this.bridgeMsgTokenDomain = bridgeMessage.token.domain as number; this.bridgeMsgTokenId = bridgeMessage.token.id as string; - } catch(e) { + } catch (e) { // pass } - + this.state = MsgState.Dispatched; this.timings = new Timings(createdAt); } @@ -245,12 +248,32 @@ export class NomadMessage { return `${this.origin}${this.root}`; } - intoDB(): [string, number, number, string, string, string, number, number, number, number, number, string | undefined, string | undefined, string | undefined, boolean | undefined, string | undefined , number | undefined, string | undefined + intoDB(): [ + string, + number, + number, + number, + string, + string, + string, + number, + number, + number, + number, + number, + string | undefined, + string | undefined, + string | undefined, + boolean | undefined, + string | undefined, + number | undefined, + string | undefined, ] { return [ this.hash, this.origin, this.destination, + this.nonce, bytes32ToAddress(this.nomadSender), bytes32ToAddress(this.nomadRecipient), this.root, @@ -260,20 +283,18 @@ export class NomadMessage { this.timings.relayedAt, this.timings.processedAt, this.bridgeMsgType, - this.bridgeMsgTo ? bytes32ToAddress(this.bridgeMsgTo): undefined, + this.bridgeMsgTo ? bytes32ToAddress(this.bridgeMsgTo) : undefined, this.bridgeMsgAmount?.toString(), this.bridgeMsgAllowFast, this.bridgeMsgDetailsHash, this.bridgeMsgTokenDomain, - this.bridgeMsgTokenId ? bytes32ToAddress(this.bridgeMsgTokenId): undefined, - ] + this.bridgeMsgTokenId + ? bytes32ToAddress(this.bridgeMsgTokenId) + : undefined, + ]; } } - - - - export class Processor extends Consumer { messages: NomadMessage[]; msgToIndex: Map; @@ -282,8 +303,9 @@ export class Processor extends Consumer { domains: number[]; syncQueue: string[]; db: DBDriver; + logger: Logger; - constructor(db: DBDriver) { + constructor(db: DBDriver, logger: Logger) { super(); this.messages = []; this.msgToIndex = new Map(); @@ -293,6 +315,7 @@ export class Processor extends Consumer { this.syncQueue = []; this.db = db; + this.logger = logger; } async consume(...events: NomadEvent[]): Promise { @@ -310,16 +333,22 @@ export class Processor extends Consumer { this.consumed += 1; } - await this.sync() + await this.sync(); } async sync() { const [inserts, updates] = await this.getMsgForSync(); - await Promise.all([this.db.insertMessage(inserts), this.db.updateMessage(updates)]) + + this.logger.info(`Inserting ${inserts.length} messages and updating ${updates.length}`); + + await Promise.all([ + this.db.insertMessage(inserts), + this.db.updateMessage(updates), + ]); } addToSyncQueue(hash: string) { - if (this.syncQueue.indexOf(hash) < 0 ) this.syncQueue.push(hash) + if (this.syncQueue.indexOf(hash) < 0) this.syncQueue.push(hash); } async getMsgForSync(): Promise<[NomadMessage[], NomadMessage[]]> { @@ -328,7 +357,7 @@ export class Processor extends Consumer { const insert: string[] = []; const update: string[] = []; - this.syncQueue.forEach(hash => { + this.syncQueue.forEach((hash) => { if (existingHashes.indexOf(hash) < 0) { insert.push(hash); } else { @@ -338,20 +367,17 @@ export class Processor extends Consumer { this.syncQueue = []; - return [ - this.mapHashesToMessages(insert), - this.mapHashesToMessages(update), - ]; + return [this.mapHashesToMessages(insert), this.mapHashesToMessages(update)]; } mapHashesToMessages(hashes: string[]): NomadMessage[] { - return hashes.map(hash => this.getMsg(hash)!).filter(m=>!!m); + return hashes.map((hash) => this.getMsg(hash)!).filter((m) => !!m); } dispatched(e: NomadEvent) { const m = new NomadMessage( e.domain, - e.destination(), + ...e.destinationAndNonce(), e.eventData.committedRoot!, e.eventData.messageHash!, e.eventData.leafIndex!, diff --git a/typescript/nomad-monitor/src/indexer/db.ts b/typescript/nomad-monitor/src/indexer/db.ts index 569f8d7e0..2f5b9aed7 100644 --- a/typescript/nomad-monitor/src/indexer/db.ts +++ b/typescript/nomad-monitor/src/indexer/db.ts @@ -1,87 +1,104 @@ import { NomadMessage } from './consumer'; -import {Pool} from 'pg'; +import { Pool } from 'pg'; -// expand(3, 2) returns "($1, $2), ($3, $4), ($5, $6)" -function expand(rowCount: number, columnCount: number, startAt=1){ - var index = startAt - return Array(rowCount).fill(0).map(v => `(${Array(columnCount).fill(0).map(v => `$${index++}`).join(", ")})`).join(", ") +// expand(3, 2) returns "($1, $2), ($3, $4), ($5, $6)" +function expand(rowCount: number, columnCount: number, startAt = 1) { + var index = startAt; + return Array(rowCount) + .fill(0) + .map( + (v) => + `(${Array(columnCount) + .fill(0) + .map((v) => `$${index++}`) + .join(', ')})`, + ) + .join(', '); +} + +export class DBDriver { + pool: Pool; + syncedOnce: boolean; + + constructor() { + this.syncedOnce = false; + this.pool = new Pool(); + } + + async connect() { + await this.pool.connect(); } - - export class DBDriver { - pool: Pool; - syncedOnce: boolean; - - constructor() { - this.syncedOnce = false; - this.pool = new Pool();; - } - - async connect() { - await this.pool.connect() - } - - get startupSync() { - const value = this.syncedOnce; - this.syncedOnce = true; - return !value; - } - - async insertMessage(messages: NomadMessage[]) { - const rows = messages.length; - if (!rows) return ; - const columns = 18; - const query = `INSERT INTO messages (hash, origin, destination, sender, recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, bridge_msg_to, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id) VALUES ${expand(rows, columns)};`; - const values = messages.map(m => m.intoDB()).flat(); - return await this.pool.query(query, values) - } - - async updateMessage(messages: NomadMessage[]) { - const rows = messages.length; - if (!rows) return ; - const promises = messages.map(m => { - const query = `UPDATE messages SET + + get startupSync() { + const value = this.syncedOnce; + this.syncedOnce = true; + return !value; + } + + async insertMessage(messages: NomadMessage[]) { + const rows = messages.length; + if (!rows) return; + const columns = 19; + const query = `INSERT INTO messages (hash, origin, destination, nonce, sender, recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, bridge_msg_to, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id) VALUES ${expand( + rows, + columns, + )};`; + const values = messages.map((m) => m.intoDB()).flat(); + return await this.pool.query(query, values); + } + + async updateMessage(messages: NomadMessage[]) { + const rows = messages.length; + if (!rows) return; + const promises = messages.map((m) => { + const query = `UPDATE messages SET origin = $2, destination = $3, - sender = $4, - recipient = $5, - root = $6, - state = $7, - dispatched_at = $8, - updated_at = $9, - relayed_at = $10, - processed_at = $11, - bridge_msg_type = $12, - bridge_msg_to = $13, - bridge_msg_amount = $14, - bridge_msg_allow_fast = $15, - bridge_msg_details_hash = $16, - bridge_msg_token_domain = $17, - bridge_msg_token_id = $18 + nonce = $4, + sender = $5, + recipient = $6, + root = $7, + state = $8, + dispatched_at = $9, + updated_at = $10, + relayed_at = $11, + processed_at = $12, + bridge_msg_type = $13, + bridge_msg_to = $14, + bridge_msg_amount = $15, + bridge_msg_allow_fast = $16, + bridge_msg_details_hash = $17, + bridge_msg_token_domain = $18, + bridge_msg_token_id = $19 WHERE hash = $1 `; - return this.pool.query(query, m.intoDB()); - }) + return this.pool.query(query, m.intoDB()); + }); + + return await Promise.all(promises); + } - return await Promise.all(promises); - } - - async getExistingHashes(): Promise { - const res = await this.pool.query(`select hash from messages;`); - return res.rows.map(r => r.hash) as string[] - } + async getExistingHashes(): Promise { + const res = await this.pool.query(`select hash from messages;`); + return res.rows.map((r) => r.hash) as string[]; + } - async getAllKeyPair(namespace: string): Promise> { - const res = await this.pool.query(`select key, value from kv_storage where namespace = $1;`, [namespace]); - return new Map(res.rows.map(r => [r.key, r.value])) - } + async getAllKeyPair(namespace: string): Promise> { + const res = await this.pool.query( + `select key, value from kv_storage where namespace = $1;`, + [namespace], + ); + return new Map(res.rows.map((r) => [r.key, r.value])); + } - async setKeyPair(namespace: string, k: string, v: string): Promise { - await this.pool.query(`INSERT INTO kv_storage (namespace, key, value) + async setKeyPair(namespace: string, k: string, v: string): Promise { + await this.pool.query( + `INSERT INTO kv_storage (namespace, key, value) VALUES($1,$2,$3) ON CONFLICT (namespace, key) DO - UPDATE SET value = $3;`, [namespace, k, v]); - } - + UPDATE SET value = $3;`, + [namespace, k, v], + ); } - \ No newline at end of file +} diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index e05c34bd7..a47dd170c 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -64,16 +64,16 @@ export class NomadEvent { this.source = source; } - destination(): number { + destinationAndNonce(): [number, number] { if (this.eventType !== EventType.HomeDispatch) { throw new Error( `Destination method is not availiable for non home-dispatch`, ); } - const [destination] = parseDestinationAndNonce( + const [destination, nonce] = parseDestinationAndNonce( this.eventData.destinationAndNonce!, ); - return destination; + return [destination, nonce]; } toObject() { @@ -113,12 +113,12 @@ export class NomadEvent { } function parseDestinationAndNonce( - h: ethers.BigNumber | { hex?: string, _hex?: string } + h: ethers.BigNumber | { hex?: string; _hex?: string }, ): [number, number] { let hexString = ''; if (h instanceof ethers.BigNumber) { hexString = h.toHexString(); - } else{ + } else { const hex = h.hex || h._hex; if (!hex) throw new Error(`Has no hex: ${JSON.stringify(h)}`); hexString = hex; diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index 351acf248..d2605980a 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -19,11 +19,11 @@ export class Indexer { this.sdk = sdk; this.orchestrator = orchestrator; this.persistance = new RamPersistance( - `/tmp/persistance_${this.domain}.json` + `/tmp/persistance_${this.domain}.json`, ); this.block2timeCache = new KVCache( String(this.domain), - this.orchestrator.db + this.orchestrator.db, ); } @@ -90,13 +90,13 @@ export class Indexer { await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))) ).flat(); - return [...homeEvents, ...replicasEvents] + return [...homeEvents, ...replicasEvents]; }, 5); if (error) throw error; - return fetchedEvents - } - + return fetchedEvents; + }; + const allEvents = []; const batchSize = 20000; @@ -330,30 +330,6 @@ export abstract class Persistance { abstract persist(): void; } -export class FilePersistance extends Persistance { - path: string; - constructor(path: string) { - super(); - this.path = path; - } - - store(...events: NomadEvent[]): void {} - async init(): Promise { - if (fs.existsSync(this.path)) { - this.from = 13; - this.height = 14; - return ; - } else { - return ; - } - } - sortSorage() {} - allEvents(): NomadEvent[] { - return []; - } - persist(): void {} -} - export class RamPersistance extends Persistance { block2events: Map; blocks: number[]; @@ -418,21 +394,18 @@ export class RamPersistance extends Persistance { ); } - persistToDB() { - - } + persistToDB() {} persist() { this.persistToFile(); - this.persistToDB() + this.persistToDB(); } async load() { - this.loadFromFile() + this.loadFromFile(); } loadFromFile() { - const object = JSON.parse( fs.readFileSync(this.storePath, 'utf8'), reviver, diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index a7500cddb..950ecbc37 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -25,11 +25,18 @@ const environment = process.env.ENVIRONMENT!; const logger = createLogger('indexer', environment); const db = new DBDriver(); - - const c = new Processor(db); + + const c = new Processor(db, logger); const m = new IndexerCollector(environment, logger); - const o = new Orchestrator(mainnet, c, mainnet.domainNumbers[0], m, logger, db); + const o = new Orchestrator( + mainnet, + c, + mainnet.domainNumbers[0], + m, + logger, + db, + ); await o.init(); await o.startConsuming(); diff --git a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js index 803716be6..ade6e457a 100644 --- a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js +++ b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js @@ -5,24 +5,26 @@ exports.shorthands = undefined; exports.up = pgm => { pgm.createTable('messages', { id: 'id', - hash: {type: 'varchar(66)', notNull: true}, + hash: {type: 'varchar(66)', notNull: true, unique: true}, // Nomad TX id origin: {type: 'integer', notNull: true}, destination: {type: 'integer', notNull: true}, - sender: {type: 'varchar(42)', notNull: true}, - recipient: {type: 'varchar(42)', notNull: true}, + nonce: {type: 'integer', notNull: true}, + sender: {type: 'varchar(42)', notNull: true}, // Nomad sender (bridge router) + recipient: {type: 'varchar(42)', notNull: true}, // Nomad recipient (bridge router) root: {type: 'varchar(66)', notNull: true}, - state: {type: 'integer', notNull: true}, - dispatched_at: {type: 'bigint', notNull: true}, - updated_at: {type: 'bigint', notNull: true}, - relayed_at: {type: 'bigint', notNull: true}, - processed_at: {type: 'bigint', notNull: true}, - bridge_msg_type: {type: 'varchar(14)', notNull: false}, - bridge_msg_to: {type: 'varchar(42)', notNull: false}, - bridge_msg_amount: {type: 'varchar(256)', notNull: false}, - bridge_msg_allow_fast: {type: 'boolean', notNull: false}, - bridge_msg_details_hash: {type: 'varchar(66)', notNull: false}, - bridge_msg_token_domain: {type: 'integer', notNull: false}, - bridge_msg_token_id: {type: 'varchar(42)', notNull: false}, + state: {type: 'integer', notNull: true}, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) + dispatched_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state dispatched + updated_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state updated + relayed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state relayed + processed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state processed + // Bridge message internals + bridge_msg_type: {type: 'varchar(14)', notNull: false}, // Mostly 'transaction' + bridge_msg_to: {type: 'varchar(42)', notNull: false}, // Real recipient on destination domain + bridge_msg_amount: {type: 'varchar(256)', notNull: false}, // Amount of Token + bridge_msg_allow_fast: {type: 'boolean', notNull: false}, // Allow fast? + bridge_msg_details_hash: {type: 'varchar(66)', notNull: false}, // Details hash - don't know what it is (need to do homework) + bridge_msg_token_domain: {type: 'integer', notNull: false}, // Token domain + bridge_msg_token_id: {type: 'varchar(42)', notNull: false}, // Token id (address) createdAt: { type: 'timestamp', notNull: true, diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/orchestrator.ts index 23ebdd3b1..95037b7ca 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/orchestrator.ts @@ -24,7 +24,7 @@ export class Orchestrator { gov: number, metrics: IndexerCollector, logger: Logger, - db: DBDriver + db: DBDriver, ) { this.sdk = sdk; this.consumer = c; diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index 4975b358b..6b6aa1a2f 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -80,8 +80,7 @@ export class KVCache { async tryLoad() { try { - this.m = await this.db.getAllKeyPair(this.name) - + this.m = await this.db.getAllKeyPair(this.name); } catch (_) {} } From 1b66d2bb35250d2cbfdf572778e835880cd5ca86 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Sun, 23 Jan 2022 23:27:05 +0100 Subject: [PATCH 15/63] feature: sender, received event --- .../nomad-monitor/src/indexer/consumer.ts | 201 ++++++++++++++++-- typescript/nomad-monitor/src/indexer/db.ts | 13 +- typescript/nomad-monitor/src/indexer/event.ts | 27 ++- .../nomad-monitor/src/indexer/indexer.ts | 68 +++++- .../1642713407510_my-first-migration.js | 7 +- typescript/nomad-monitor/src/indexer/types.ts | 6 + typescript/nomad-monitor/src/indexer/utils.ts | 11 + 7 files changed, 306 insertions(+), 27 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index 196bc53b0..ab08e9a1e 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -27,6 +27,11 @@ class StatisticsCollector { this.s.counts.domainStatistics.get(domain)!.relayed += 1; } + addReceived(domain: number) { + this.s.counts.total.received += 1; + this.s.counts.domainStatistics.get(domain)!.received += 1; + } + addProcessed(domain: number) { this.s.counts.total.processed += 1; this.s.counts.domainStatistics.get(domain)!.processed += 1; @@ -51,8 +56,17 @@ class StatisticsCollector { } } - contributeProcessTimings(m: NomadMessage) { + contributeReceiveTimings(m: NomadMessage) { this.contributeRelayTimings(m); + const inReceiveStat = m.timings.inReceived(); + if (inReceiveStat) { + this.s.timings.total.meanReceive.add(inReceiveStat); + this.s.timings.domainStatistics.get(m.origin)!.meanReceive.add(inReceiveStat); + } + } + + contributeProcessTimings(m: NomadMessage) { + this.contributeReceiveTimings(m); const inProcessStat = m.timings.inProcessed(); if (inProcessStat) { this.s.timings.total.meanProcess.add(inProcessStat); @@ -79,6 +93,9 @@ class StatisticsCollector { case MsgState.Relayed: this.addRelayed(m.origin); break; + case MsgState.Received: + this.addReceived(m.origin); + break; case MsgState.Processed: this.addProcessed(m.origin); break; @@ -95,6 +112,9 @@ class StatisticsCollector { case MsgState.Relayed: this.contributeRelayTimings(m); break; + case MsgState.Received: + this.contributeReceiveTimings(m); + break; case MsgState.Processed: this.contributeProcessTimings(m); break; @@ -117,6 +137,7 @@ enum MsgState { Dispatched, Updated, Relayed, + Received, Processed, } @@ -125,12 +146,14 @@ class Timings { updatedAt: number; relayedAt: number; processedAt: number; + receivedAt: number; constructor(ts: number) { this.dispatchedAt = ts; this.updatedAt = 0; this.relayedAt = 0; this.processedAt = 0; + this.receivedAt = 0; } updated(ts: number) { @@ -141,6 +164,10 @@ class Timings { this.relayedAt = ts; } + received(ts: number) { + this.receivedAt = ts; + } + processed(ts: number) { this.processedAt = ts; } @@ -159,11 +186,21 @@ class Timings { return undefined; } + inReceived(): number | undefined { + if (this.receivedAt) { + return ( + this.receivedAt - + (this.relayedAt || this.updatedAt || this.dispatchedAt) + ); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available + } + return undefined; + } + inProcessed(): number | undefined { if (this.processedAt) { return ( this.processedAt - - (this.relayedAt || this.updatedAt || this.dispatchedAt) + (this.receivedAt || this.relayedAt || this.updatedAt || this.dispatchedAt) ); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available } return undefined; @@ -173,7 +210,7 @@ class Timings { if (this.processedAt) { return ( this.processedAt - - (this.dispatchedAt || this.updatedAt || this.relayedAt) + (this.dispatchedAt || this.updatedAt || this.relayedAt || this.receivedAt) ); // same as for .inRelayed() and .inProcessed() but opposit order } return undefined; @@ -184,6 +221,8 @@ function bytes32ToAddress(s: string) { return '0x' + s.slice(26); } + + export class NomadMessage { origin: number; destination: number; @@ -193,8 +232,10 @@ export class NomadMessage { leafIndex: ethers.BigNumber; destinationAndNonce: ethers.BigNumber; raw: string; + sender?: string; nomadSender: string; nomadRecipient: string; + hasBridgeMessage: boolean; bridgeMsgType?: string; bridgeMsgTo?: string; bridgeMsgAmount?: ethers.BigNumber; @@ -225,18 +266,20 @@ export class NomadMessage { this.destinationAndNonce = destinationAndNonce; this.raw = message; const parsed = parseMessage(message); - this.nomadSender = parsed.sender; - this.nomadRecipient = parsed.recipient; + this.nomadSender = bytes32ToAddress(parsed.sender); + this.nomadRecipient = bytes32ToAddress(parsed.recipient); try { const bridgeMessage = parseBody(parsed.body); this.bridgeMsgType = bridgeMessage.action.type as string; - this.bridgeMsgTo = bridgeMessage.action.to; + this.bridgeMsgTo = bytes32ToAddress(bridgeMessage.action.to); this.bridgeMsgAmount = bridgeMessage.action.amount; this.bridgeMsgAllowFast = bridgeMessage.action.allowFast; this.bridgeMsgDetailsHash = bridgeMessage.action.detailsHash; this.bridgeMsgTokenDomain = bridgeMessage.token.domain as number; - this.bridgeMsgTokenId = bridgeMessage.token.id as string; + this.bridgeMsgTokenId = bytes32ToAddress(bridgeMessage.token.id as string); + this.hasBridgeMessage = true; } catch (e) { + this.hasBridgeMessage = false; // pass } @@ -244,6 +287,10 @@ export class NomadMessage { this.timings = new Timings(createdAt); } + updateSender(sender: string) { + this.sender = sender; + } + get originAndRoot(): string { return `${this.origin}${this.root}`; } @@ -268,14 +315,15 @@ export class NomadMessage { string | undefined, number | undefined, string | undefined, + string | undefined, ] { return [ this.hash, this.origin, this.destination, this.nonce, - bytes32ToAddress(this.nomadSender), - bytes32ToAddress(this.nomadRecipient), + this.nomadSender, + this.nomadRecipient, this.root, this.state, this.timings.dispatchedAt, @@ -283,18 +331,95 @@ export class NomadMessage { this.timings.relayedAt, this.timings.processedAt, this.bridgeMsgType, - this.bridgeMsgTo ? bytes32ToAddress(this.bridgeMsgTo) : undefined, + this.bridgeMsgTo, this.bridgeMsgAmount?.toString(), this.bridgeMsgAllowFast, this.bridgeMsgDetailsHash, this.bridgeMsgTokenDomain, - this.bridgeMsgTokenId - ? bytes32ToAddress(this.bridgeMsgTokenId) - : undefined, + this.bridgeMsgTokenId, + this.sender, ]; } } +class SenderLostAndFound { + p: Processor; + dispatchEventsWithMessages: [NomadEvent, NomadMessage][]; + bridgeRouterSendEvents: NomadEvent[]; + constructor(p: Processor) { + this.p = p; + this.dispatchEventsWithMessages = [] + this.bridgeRouterSendEvents = [] + } + + bridgeRouterSend(e: NomadEvent): string | undefined { + // check if we have dispatch events with block >= current && block <= current + 4; + const hash = this.findMatchingDispatchAndUpdateAndRemove(e); + if (hash) { + return hash + } else { + //add event for further fixing from dispatch side + this.bridgeRouterSendEvents.push(e); + return undefined; + } + + } + findMatchingDispatchAndUpdateAndRemove(brSend: NomadEvent): string|undefined { + const index = this.dispatchEventsWithMessages.findIndex( + ([dispatch, m]) => this.match(dispatch, brSend, m) + ); + + if (index >= 0) { + const some = this.dispatchEventsWithMessages.at(index); + if (some) { + const [_, msg] = some; + // console.log(`Set sender to ${brSend.eventData.from!} for message: ${msg.hash}`) + msg.sender = brSend.eventData.from!; + this.dispatchEventsWithMessages.splice(index, 1); + return msg.hash; + } + + } + return undefined; + } + + match(dispatch: NomadEvent, brSend: NomadEvent, m: NomadMessage): boolean { + return brSend.eventData.toDomain! === m.destination && //brSend.eventData.token?.toLowerCase() === m.bridgeMsgTokenId?.toLowerCase() && + bytes32ToAddress(brSend.eventData.toId!).toLowerCase() === m.bridgeMsgTo?.toLowerCase() && + brSend.eventData.amount!.eq(m.bridgeMsgAmount!) && + ( + brSend.block === dispatch.block //&& // (dispatch.block - brSend.block <= 2 || brSend.block - dispatch.block <= 30) + ) + } + + findMatchingBRSendUpdateAndRemove(dispatch: NomadEvent, m: NomadMessage): boolean { + const index = this.bridgeRouterSendEvents.findIndex( + (brSend) => this.match(dispatch, brSend, m) + ); + if (index >= 0) { + const brSend = this.bridgeRouterSendEvents.at(index); + if (brSend) { + m.sender = brSend.eventData.from!; + } + this.bridgeRouterSendEvents.splice(index, 1); + return true; + } + return false; + } + + dispatch(e: NomadEvent, m: NomadMessage): boolean { + if (!m.hasBridgeMessage) return false; + + if (this.findMatchingBRSendUpdateAndRemove(e, m)) { + return true; + } else { + this.dispatchEventsWithMessages.push([e,m]); + return false; + } + + } +} + export class Processor extends Consumer { messages: NomadMessage[]; msgToIndex: Map; @@ -304,6 +429,7 @@ export class Processor extends Consumer { syncQueue: string[]; db: DBDriver; logger: Logger; + senderRegistry: SenderLostAndFound; constructor(db: DBDriver, logger: Logger) { super(); @@ -313,6 +439,7 @@ export class Processor extends Consumer { this.consumed = 0; this.domains = []; this.syncQueue = []; + this.senderRegistry = new SenderLostAndFound(this); this.db = db; this.logger = logger; @@ -328,6 +455,10 @@ export class Processor extends Consumer { this.replicaUpdate(event); } else if (event.eventType === EventType.ReplicaProcess) { this.process(event); + } else if (event.eventType === EventType.BridgeRouterSend) { + this.bridgeRouterSend(event); + } else if (event.eventType === EventType.BridgeRouterReceive) { + this.bridgeRouterReceive(event); } this.consumed += 1; @@ -341,6 +472,8 @@ export class Processor extends Consumer { this.logger.info(`Inserting ${inserts.length} messages and updating ${updates.length}`); + console.log(`Updating:`, updates.length, updates[0]?.sender) + await Promise.all([ this.db.insertMessage(inserts), this.db.updateMessage(updates), @@ -374,7 +507,9 @@ export class Processor extends Consumer { return hashes.map((hash) => this.getMsg(hash)!).filter((m) => !!m); } + dispatched(e: NomadEvent) { + const m = new NomadMessage( e.domain, ...e.destinationAndNonce(), @@ -385,12 +520,17 @@ export class Processor extends Consumer { e.eventData.message!, e.ts, ); + + this.senderRegistry.dispatch(e, m); + this.add(m); this.addToSyncQueue(m.hash); if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } + + homeUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); if (ms.length) @@ -429,15 +569,40 @@ export class Processor extends Consumer { } } + + + bridgeRouterSend(e: NomadEvent) { + + const hash = this.senderRegistry.bridgeRouterSend(e); + if (hash) { + // console.log(`Setting bridgeRouterSend for `, this.getMsg(hash)?.sender) + this.addToSyncQueue(hash) + } + + } + + bridgeRouterReceive(e: NomadEvent) { + const m = this.getMsgsByOriginAndNonce(...e.originAndNonce()) + + if (m) { + if (m.state < MsgState.Received) { + m.state = MsgState.Received; + m.timings.received(e.ts); + this.addToSyncQueue(m.hash); + } + } + } + add(m: NomadMessage) { const index = this.messages.length; this.msgToIndex.set(m.hash, index); - const x = this.msgByOriginAndRoot.get(m.originAndRoot); - if (x) { - x.push(index); + const msgByOriginAndRoot = this.msgByOriginAndRoot.get(m.originAndRoot); + if (msgByOriginAndRoot) { + msgByOriginAndRoot.push(index); } else { this.msgByOriginAndRoot.set(m.originAndRoot, [index]); } + this.messages.push(m); } @@ -458,6 +623,10 @@ export class Processor extends Consumer { return []; } + getMsgsByOriginAndNonce(origin: number, nonce: number): NomadMessage | undefined { + return this.messages.find((m) => m.nonce === nonce && m.origin === origin) + } + stats(): Statistics { const collector = new StatisticsCollector(this.domains); diff --git a/typescript/nomad-monitor/src/indexer/db.ts b/typescript/nomad-monitor/src/indexer/db.ts index 2f5b9aed7..f8d08ab02 100644 --- a/typescript/nomad-monitor/src/indexer/db.ts +++ b/typescript/nomad-monitor/src/indexer/db.ts @@ -38,8 +38,8 @@ export class DBDriver { async insertMessage(messages: NomadMessage[]) { const rows = messages.length; if (!rows) return; - const columns = 19; - const query = `INSERT INTO messages (hash, origin, destination, nonce, sender, recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, bridge_msg_to, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id) VALUES ${expand( + const columns = 20; + const query = `INSERT INTO messages (hash, origin, destination, nonce, nomad_sender, nomad_recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, recipient, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id, sender) VALUES ${expand( rows, columns, )};`; @@ -55,8 +55,8 @@ export class DBDriver { origin = $2, destination = $3, nonce = $4, - sender = $5, - recipient = $6, + nomad_sender = $5, + nomad_recipient = $6, root = $7, state = $8, dispatched_at = $9, @@ -64,12 +64,13 @@ export class DBDriver { relayed_at = $11, processed_at = $12, bridge_msg_type = $13, - bridge_msg_to = $14, + recipient = $14, bridge_msg_amount = $15, bridge_msg_allow_fast = $16, bridge_msg_details_hash = $17, bridge_msg_token_domain = $18, - bridge_msg_token_id = $19 + bridge_msg_token_id = $19, + sender = $20 WHERE hash = $1 `; return this.pool.query(query, m.intoDB()); diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/event.ts index a47dd170c..18d1f599c 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/event.ts @@ -3,6 +3,7 @@ import { ethers } from 'ethers'; export enum ContractType { Home = 'home', Replica = 'replica', + BridgeRouter = 'bridgeRouter', } export enum EventType { @@ -10,6 +11,8 @@ export enum EventType { HomeUpdate = 'homeUpdate', ReplicaUpdate = 'replicaUpdate', ReplicaProcess = 'replicaProcess', + BridgeRouterSend = 'bridgeRouterSend', + BridgeRouterReceive = 'bridgeRouterReceive', } export enum EventSource { @@ -29,6 +32,16 @@ export type EventData = { message?: string; signature?: string; homeDomain?: number; + // Bridge router options + token?: string; + from?: string; + toDomain?: number; + toId?: string; + amount?: ethers.BigNumber; + fastLiquidityEnabled?: boolean; + originAndNonce?: ethers.BigNumber; + recipient?: string; + liquidityProvider?: string; }; export class NomadEvent { @@ -56,7 +69,7 @@ export class NomadEvent { this.contractType = contractType; this.replicaOrigin = replicaOrigin; this.ts = - /*source === EventSource.Fetch && */ contractType == ContractType.Home + /*source === EventSource.Fetch && */ (contractType == ContractType.Home || contractType == ContractType.BridgeRouter) ? ts - 45000 : ts; // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination this.eventData = eventData; @@ -76,6 +89,18 @@ export class NomadEvent { return [destination, nonce]; } + originAndNonce(): [number, number] { + if (this.eventType !== EventType.BridgeRouterReceive) { + throw new Error( + `Destination method is not availiable for non BridgeRouterReceive`, + ); + } + const [origin, nonce] = parseDestinationAndNonce( + this.eventData.originAndNonce!, + ); + return [origin, nonce]; + } + toObject() { return { domain: this.domain, diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/indexer.ts index d2605980a..21a5516f4 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/indexer.ts @@ -5,6 +5,7 @@ import { ContractType, EventType, NomadEvent, EventSource } from './event'; import { Home, Replica } from '@nomad-xyz/contract-interfaces/core'; import { ethers } from 'ethers'; import { KVCache, replacer, retry, reviver } from './utils'; +import { BridgeRouter } from '@nomad-xyz/contract-interfaces/bridge'; export class Indexer { domain: number; @@ -68,6 +69,10 @@ export class Indexer { return this.sdk.getCore(this.domain)!.home; } + bridgeRouter(): BridgeRouter { + return this.sdk.getBridge(this.domain)!.bridgeRouter; + } + replicaForDomain(domain: number): Replica { return this.sdk.getReplicaFor(domain, this.domain)!; } @@ -89,8 +94,9 @@ export class Indexer { const replicasEvents = ( await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))) ).flat(); + const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); - return [...homeEvents, ...replicasEvents]; + return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; }, 5); if (error) throw error; @@ -191,6 +197,66 @@ export class Indexer { this.persistance.persist(); } + async fetchBridgeRouter(from: number, to: number) { + const br = this.bridgeRouter(); + const allEvents = []; + { + const events = await br.queryFilter(br.filters.Send(), from, to); + const parsedEvents = await Promise.all( + events.map( + async (event) => + new NomadEvent( + this.domain, + EventType.BridgeRouterSend, + ContractType.BridgeRouter, + 0, + await this.getBlockTimestamp(event.blockNumber), + { + token: event.args[0], + from: event.args[1], + toDomain: event.args[2], + toId: event.args[3], + amount: event.args[4], + fastLiquidityEnabled: event.args[5], + }, + event.blockNumber, + EventSource.Fetch, + ), + ), + ); + allEvents.push(...parsedEvents) + } + + { + const events = await br.queryFilter(br.filters.Receive(), from, to); + const parsedEvents = await Promise.all( + events.map( + async (event) => + new NomadEvent( + this.domain, + EventType.BridgeRouterReceive, + ContractType.BridgeRouter, + 0, + await this.getBlockTimestamp(event.blockNumber), + { + originAndNonce: event.args[0], + token: event.args[1], + recipient: event.args[2], + liquidityProvider: event.args[3], + amount: event.args[4], + }, + event.blockNumber, + EventSource.Fetch, + ), + ), + ); + allEvents.push(...parsedEvents) + } + + return allEvents + + } + async fetchHome(from: number, to: number) { let fetchedEvents: NomadEvent[] = []; diff --git a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js index ade6e457a..7438588bb 100644 --- a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js +++ b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js @@ -9,8 +9,8 @@ exports.up = pgm => { origin: {type: 'integer', notNull: true}, destination: {type: 'integer', notNull: true}, nonce: {type: 'integer', notNull: true}, - sender: {type: 'varchar(42)', notNull: true}, // Nomad sender (bridge router) - recipient: {type: 'varchar(42)', notNull: true}, // Nomad recipient (bridge router) + nomad_sender: {type: 'varchar(42)', notNull: true}, // Nomad sender (bridge router) + nomad_recipient: {type: 'varchar(42)', notNull: true}, // Nomad recipient (bridge router) root: {type: 'varchar(66)', notNull: true}, state: {type: 'integer', notNull: true}, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) dispatched_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state dispatched @@ -18,8 +18,9 @@ exports.up = pgm => { relayed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state relayed processed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state processed // Bridge message internals + sender: {type: 'varchar(42)', notNull: false}, // sender bridge_msg_type: {type: 'varchar(14)', notNull: false}, // Mostly 'transaction' - bridge_msg_to: {type: 'varchar(42)', notNull: false}, // Real recipient on destination domain + recipient: {type: 'varchar(42)', notNull: false}, // Real recipient on destination domain bridge_msg_amount: {type: 'varchar(256)', notNull: false}, // Amount of Token bridge_msg_allow_fast: {type: 'boolean', notNull: false}, // Allow fast? bridge_msg_details_hash: {type: 'varchar(66)', notNull: false}, // Details hash - don't know what it is (need to do homework) diff --git a/typescript/nomad-monitor/src/indexer/types.ts b/typescript/nomad-monitor/src/indexer/types.ts index 75db0aeae..88b88208b 100644 --- a/typescript/nomad-monitor/src/indexer/types.ts +++ b/typescript/nomad-monitor/src/indexer/types.ts @@ -18,11 +18,13 @@ export class BasicCountStages { dispatched: number; updated: number; relayed: number; + received: number; processed: number; constructor() { this.dispatched = 0; this.updated = 0; this.relayed = 0; + this.received = 0; this.processed = 0; } @@ -31,6 +33,7 @@ export class BasicCountStages { dispatched: this.dispatched, updated: this.updated, relayed: this.relayed, + received: this.received, processed: this.processed, }; } @@ -39,11 +42,13 @@ export class BasicCountStages { export class BasicTiming { meanUpdate: Mean; meanRelay: Mean; + meanReceive: Mean; meanProcess: Mean; meanE2E: Mean; constructor() { this.meanUpdate = new Mean(); this.meanRelay = new Mean(); + this.meanReceive = new Mean(); this.meanProcess = new Mean(); this.meanE2E = new Mean(); } @@ -52,6 +57,7 @@ export class BasicTiming { return { meanUpdate: this.meanUpdate.mean(), meanRelay: this.meanRelay.mean(), + meanReceive: this.meanReceive.mean(), meanProcess: this.meanProcess.mean(), meanE2E: this.meanE2E.mean(), }; diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/utils.ts index 6b6aa1a2f..250e03f89 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/utils.ts @@ -58,6 +58,8 @@ export function reviver(key: any, value: any): any { return NomadEvent.fromObject(value.value); } else if (value.dataType === 'BigNumber') { return ethers.BigNumber.from(value.value); + } else if (value.type === 'BigNumber') { + return ethers.BigNumber.from(value.hex); } } return value; @@ -97,3 +99,12 @@ export class KVCache { export function logToFile(s: string) { fs.appendFileSync('/tmp/log.log', s + '\n'); } + +import crypto from 'crypto'; + +export function hash(...vals: string[]): string { + const hash = crypto.createHash('md5'); + vals.forEach(v => hash.update(v)) + return hash.digest('hex') + +} \ No newline at end of file From 1a9f660069f2055812b9925838cc5f2f7b955904 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 25 Jan 2022 12:30:42 +0100 Subject: [PATCH 16/63] message time --- .../nomad-monitor/src/indexer/consumer.ts | 53 +++++++++++++++---- .../src/nomad/messages/GovernanceMessage.ts | 2 +- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/consumer.ts index ab08e9a1e..b5339ec0c 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/consumer.ts @@ -3,9 +3,11 @@ import { ethers } from 'ethers'; import { EventType, NomadEvent } from './event'; import { Statistics } from './types'; import { parseBody } from '@nomad-xyz/sdk/src/nomad/messages/BridgeMessage'; +import { parseAction } from '@nomad-xyz/sdk/src/nomad/messages/GovernanceMessage'; import { DBDriver } from './db'; import Logger from 'bunyan'; + class StatisticsCollector { s: Statistics; constructor(domains: number[]) { @@ -222,6 +224,11 @@ function bytes32ToAddress(s: string) { } +enum MessageType { + NoMessage, + TransferMessage, + GovernanceMessage, +}; export class NomadMessage { origin: number; @@ -235,7 +242,7 @@ export class NomadMessage { sender?: string; nomadSender: string; nomadRecipient: string; - hasBridgeMessage: boolean; + hasMessage: MessageType; bridgeMsgType?: string; bridgeMsgTo?: string; bridgeMsgAmount?: ethers.BigNumber; @@ -268,8 +275,21 @@ export class NomadMessage { const parsed = parseMessage(message); this.nomadSender = bytes32ToAddress(parsed.sender); this.nomadRecipient = bytes32ToAddress(parsed.recipient); + this.hasMessage = MessageType.NoMessage; + + this.tryParseMessage(parsed.body); + + this.state = MsgState.Dispatched; + this.timings = new Timings(createdAt); + } + + tryParseMessage(body: string) { + this.tryParseTransferMessage(body) || this.tryParseGovernanceMessage(body) + } + + tryParseTransferMessage(body: string): boolean { try { - const bridgeMessage = parseBody(parsed.body); + const bridgeMessage = parseBody(body); this.bridgeMsgType = bridgeMessage.action.type as string; this.bridgeMsgTo = bytes32ToAddress(bridgeMessage.action.to); this.bridgeMsgAmount = bridgeMessage.action.amount; @@ -277,14 +297,27 @@ export class NomadMessage { this.bridgeMsgDetailsHash = bridgeMessage.action.detailsHash; this.bridgeMsgTokenDomain = bridgeMessage.token.domain as number; this.bridgeMsgTokenId = bytes32ToAddress(bridgeMessage.token.id as string); - this.hasBridgeMessage = true; - } catch (e) { - this.hasBridgeMessage = false; - // pass + this.hasMessage = MessageType.TransferMessage; + return true; + } catch(e) { + return false; } + } - this.state = MsgState.Dispatched; - this.timings = new Timings(createdAt); + tryParseGovernanceMessage(body: string): boolean { + try { + const message = parseAction(body); + if (message.type == "batch") { + message.batchHash + } else { + message.address; + message.domain; + } + this.hasMessage = MessageType.GovernanceMessage; + return true; + } catch(e) { + return false; + } } updateSender(sender: string) { @@ -408,7 +441,7 @@ class SenderLostAndFound { } dispatch(e: NomadEvent, m: NomadMessage): boolean { - if (!m.hasBridgeMessage) return false; + if (m.hasMessage !== MessageType.TransferMessage) return false; if (this.findMatchingBRSendUpdateAndRemove(e, m)) { return true; @@ -472,8 +505,6 @@ export class Processor extends Consumer { this.logger.info(`Inserting ${inserts.length} messages and updating ${updates.length}`); - console.log(`Updating:`, updates.length, updates[0]?.sender) - await Promise.all([ this.db.insertMessage(inserts), this.db.updateMessage(updates), diff --git a/typescript/nomad-sdk/src/nomad/messages/GovernanceMessage.ts b/typescript/nomad-sdk/src/nomad/messages/GovernanceMessage.ts index 5d71eb8cb..f765257cc 100644 --- a/typescript/nomad-sdk/src/nomad/messages/GovernanceMessage.ts +++ b/typescript/nomad-sdk/src/nomad/messages/GovernanceMessage.ts @@ -33,7 +33,7 @@ type TransferGovernor = { export type Action = Batch | TransferGovernor; -function parseAction(raw: ethers.BytesLike): Action { +export function parseAction(raw: ethers.BytesLike): Action { const buf = ethers.utils.arrayify(raw); const actionType = buf[0]; if (buf.length === ACTION_LEN.batch && actionType === ActionTypes.batch) { From 9f71642400b395b6270254f118d0a347751db0ab Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 25 Jan 2022 15:43:50 +0100 Subject: [PATCH 17/63] feature: evm tx --- .../nomad-monitor/src/indexer/api/index.ts | 27 ++++++ .../src/indexer/{ => core}/consumer.ts | 86 +++++++++++++++++-- .../src/indexer/{ => core}/db.ts | 73 +++++++++++++--- .../src/indexer/{ => core}/event.ts | 1 + .../nomad-monitor/src/indexer/core/index.ts | 49 +++++++++++ .../src/indexer/{ => core}/indexer.ts | 76 +++++++++------- .../src/indexer/{ => core}/metrics.ts | 2 +- .../src/indexer/{ => core}/orchestrator.ts | 6 +- .../src/indexer/{ => core}/types.ts | 0 .../src/indexer/{ => core}/utils.ts | 6 +- typescript/nomad-monitor/src/indexer/main.ts | 63 +++----------- .../1642713407510_my-first-migration.js | 5 ++ 12 files changed, 287 insertions(+), 107 deletions(-) create mode 100644 typescript/nomad-monitor/src/indexer/api/index.ts rename typescript/nomad-monitor/src/indexer/{ => core}/consumer.ts (89%) rename typescript/nomad-monitor/src/indexer/{ => core}/db.ts (53%) rename typescript/nomad-monitor/src/indexer/{ => core}/event.ts (99%) create mode 100644 typescript/nomad-monitor/src/indexer/core/index.ts rename typescript/nomad-monitor/src/indexer/{ => core}/indexer.ts (86%) rename typescript/nomad-monitor/src/indexer/{ => core}/metrics.ts (98%) rename typescript/nomad-monitor/src/indexer/{ => core}/orchestrator.ts (97%) rename typescript/nomad-monitor/src/indexer/{ => core}/types.ts (100%) rename typescript/nomad-monitor/src/indexer/{ => core}/utils.ts (96%) diff --git a/typescript/nomad-monitor/src/indexer/api/index.ts b/typescript/nomad-monitor/src/indexer/api/index.ts new file mode 100644 index 000000000..9d91454f8 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/api/index.ts @@ -0,0 +1,27 @@ +import express from 'express'; +import { DB } from '../core/db'; +import * as dotenv from 'dotenv'; +dotenv.config({}); + + +const PORT = process.env.PORT; + +export async function run(db: DB) { + const app = express(); + + app.get('/healthcheck', (req,res) => res.send('OK!')); + + app.get('/tx/:tx', async (req,res) => { + const message = await db.getMessageByEvm(req.params.tx); + return res.json(message.toObject()) + }); + + app.get('/hash/:hash', async (req,res) => { + const message = await db.getMessageByHash(req.params.hash); + return res.json(message.toObject()) + }); + + app.listen(PORT, () => { + console.log(`⚡️[server]: Server is running at https://localhost:${PORT}`); + }); +} \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/consumer.ts b/typescript/nomad-monitor/src/indexer/core/consumer.ts similarity index 89% rename from typescript/nomad-monitor/src/indexer/consumer.ts rename to typescript/nomad-monitor/src/indexer/core/consumer.ts index b5339ec0c..2ef52ef7c 100644 --- a/typescript/nomad-monitor/src/indexer/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/core/consumer.ts @@ -4,7 +4,7 @@ import { EventType, NomadEvent } from './event'; import { Statistics } from './types'; import { parseBody } from '@nomad-xyz/sdk/src/nomad/messages/BridgeMessage'; import { parseAction } from '@nomad-xyz/sdk/src/nomad/messages/GovernanceMessage'; -import { DBDriver } from './db'; +import { DB } from './db'; import Logger from 'bunyan'; @@ -237,7 +237,6 @@ export class NomadMessage { root: string; hash: string; leafIndex: ethers.BigNumber; - destinationAndNonce: ethers.BigNumber; raw: string; sender?: string; nomadSender: string; @@ -252,6 +251,9 @@ export class NomadMessage { bridgeMsgTokenId?: string; state: MsgState; timings: Timings; + block: number; + evm?: string; + constructor( origin: number, @@ -260,9 +262,10 @@ export class NomadMessage { root: string, hash: string, leafIndex: ethers.BigNumber, - destinationAndNonce: ethers.BigNumber, + // destinationAndNonce: ethers.BigNumber, message: string, createdAt: number, + block: number, ) { this.origin = origin; this.destination = destination; @@ -270,7 +273,7 @@ export class NomadMessage { this.root = root; this.hash = hash; this.leafIndex = leafIndex; - this.destinationAndNonce = destinationAndNonce; + // this.destinationAndNonce = destinationAndNonce; this.raw = message; const parsed = parseMessage(message); this.nomadSender = bytes32ToAddress(parsed.sender); @@ -281,6 +284,59 @@ export class NomadMessage { this.state = MsgState.Dispatched; this.timings = new Timings(createdAt); + this.block = block; + } + + toObject() { + return { + origin: this.origin, + destination: this.destination, + nonce: this.nonce, + root: this.root, + hash: this.hash, + leafIndex: this.leafIndex, + sender: this.sender, + nomadSender: this.nomadSender, + nomadRecipient: this.nomadRecipient, + hasMessage: this.hasMessage, + bridgeMsgType: this.bridgeMsgType, + bridgeMsgTo: this.bridgeMsgTo, + bridgeMsgAmount: this.bridgeMsgAmount, + bridgeMsgAllowFast: this.bridgeMsgAllowFast, + bridgeMsgDetailsHash: this.bridgeMsgDetailsHash, + bridgeMsgTokenDomain: this.bridgeMsgTokenDomain, + bridgeMsgTokenId: this.bridgeMsgTokenId, + state: this.state, + timings: this.timings, + tx: this.evm, + } + } + + static fromDB( + origin: number, + destination: number, + nonce: number, + root: string, + hash: string, + leafIndex: ethers.BigNumber, + message: string, + createdAt: number, + updatedAt: number, + relayedAt: number, + receivedAt: number, + processedAt: number, + block: number, + sender: string, + evm: string, + ): NomadMessage { + const m = new NomadMessage(origin, destination, nonce, root, hash, leafIndex, message, createdAt, block); + m.timings.updated(updatedAt); + m.timings.relayed(relayedAt); + m.timings.received(receivedAt); + m.timings.processed(processedAt); + m.updateSender(sender); + m.evm = evm; + return m; } tryParseMessage(body: string) { @@ -313,6 +369,7 @@ export class NomadMessage { message.address; message.domain; } + this.bridgeMsgType = message.type; this.hasMessage = MessageType.GovernanceMessage; return true; } catch(e) { @@ -341,6 +398,7 @@ export class NomadMessage { number, number, number, + number, string | undefined, string | undefined, string | undefined, @@ -349,6 +407,10 @@ export class NomadMessage { number | undefined, string | undefined, string | undefined, + string, + string, + number, + string|undefined, ] { return [ this.hash, @@ -362,6 +424,7 @@ export class NomadMessage { this.timings.dispatchedAt, this.timings.updatedAt, this.timings.relayedAt, + this.timings.receivedAt, this.timings.processedAt, this.bridgeMsgType, this.bridgeMsgTo, @@ -371,6 +434,10 @@ export class NomadMessage { this.bridgeMsgTokenDomain, this.bridgeMsgTokenId, this.sender, + this.raw, + this.leafIndex.toString(), + this.block, + this.evm, ]; } } @@ -407,7 +474,8 @@ class SenderLostAndFound { if (some) { const [_, msg] = some; // console.log(`Set sender to ${brSend.eventData.from!} for message: ${msg.hash}`) - msg.sender = brSend.eventData.from!; + msg.updateSender(brSend.eventData.from!); + msg.evm = brSend.eventData.evmHash!; this.dispatchEventsWithMessages.splice(index, 1); return msg.hash; } @@ -433,6 +501,7 @@ class SenderLostAndFound { const brSend = this.bridgeRouterSendEvents.at(index); if (brSend) { m.sender = brSend.eventData.from!; + m.evm = brSend.eventData.evmHash!; } this.bridgeRouterSendEvents.splice(index, 1); return true; @@ -460,11 +529,11 @@ export class Processor extends Consumer { consumed: number; // for debug domains: number[]; syncQueue: string[]; - db: DBDriver; + db: DB; logger: Logger; senderRegistry: SenderLostAndFound; - constructor(db: DBDriver, logger: Logger) { + constructor(db: DB, logger: Logger) { super(); this.messages = []; this.msgToIndex = new Map(); @@ -547,9 +616,10 @@ export class Processor extends Consumer { e.eventData.committedRoot!, e.eventData.messageHash!, e.eventData.leafIndex!, - e.eventData.destinationAndNonce!, + // e.eventData.destinationAndNonce!, e.eventData.message!, e.ts, + e.block, ); this.senderRegistry.dispatch(e, m); diff --git a/typescript/nomad-monitor/src/indexer/db.ts b/typescript/nomad-monitor/src/indexer/core/db.ts similarity index 53% rename from typescript/nomad-monitor/src/indexer/db.ts rename to typescript/nomad-monitor/src/indexer/core/db.ts index f8d08ab02..3589c509e 100644 --- a/typescript/nomad-monitor/src/indexer/db.ts +++ b/typescript/nomad-monitor/src/indexer/core/db.ts @@ -16,7 +16,7 @@ function expand(rowCount: number, columnCount: number, startAt = 1) { .join(', '); } -export class DBDriver { +export class DB { pool: Pool; syncedOnce: boolean; @@ -35,11 +35,55 @@ export class DBDriver { return !value; } + async getMessageByEvm(tx: string): Promise { + const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, dispatched_at, updated_at, relayed_at, received_at, processed_at, hash FROM messages where evm = $1;`; + const result = await this.pool.query(query, [tx.toLowerCase()]); + const entry = result.rows[0]; + return NomadMessage.fromDB(entry.origin, + entry.destination, + entry.nonce, + entry.root, + entry.hash, + entry.leaf_index, + entry.raw, + entry.block, + entry.dispatched_at, + entry.updated_at, + entry.relayed_at, + entry.received_at, + entry.processed_at, + entry.sender, + tx, + ) + } + + async getMessageByHash(hash: string): Promise { + const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages where hash = $1;`; + const result = await this.pool.query(query, [hash.toLowerCase()]); + const entry = result.rows[0]; + return NomadMessage.fromDB(entry.origin, + entry.destination, + entry.nonce, + entry.root, + hash, + entry.leaf_index, + entry.raw, + entry.block, + entry.dispatched_at, + entry.updated_at, + entry.relayed_at, + entry.received_at, + entry.processed_at, + entry.sender, + entry.evm, + ) + } + async insertMessage(messages: NomadMessage[]) { const rows = messages.length; if (!rows) return; - const columns = 20; - const query = `INSERT INTO messages (hash, origin, destination, nonce, nomad_sender, nomad_recipient, root, state, dispatched_at, updated_at, relayed_at, processed_at, bridge_msg_type, recipient, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id, sender) VALUES ${expand( + const columns = 25; + const query = `INSERT INTO messages (hash, origin, destination, nonce, nomad_sender, nomad_recipient, root, state, dispatched_at, updated_at, relayed_at, received_at, processed_at, bridge_msg_type, recipient, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id, sender, raw, leaf_index, block, evm) VALUES ${expand( rows, columns, )};`; @@ -62,15 +106,20 @@ export class DBDriver { dispatched_at = $9, updated_at = $10, relayed_at = $11, - processed_at = $12, - bridge_msg_type = $13, - recipient = $14, - bridge_msg_amount = $15, - bridge_msg_allow_fast = $16, - bridge_msg_details_hash = $17, - bridge_msg_token_domain = $18, - bridge_msg_token_id = $19, - sender = $20 + received_at = $12, + processed_at = $13, + bridge_msg_type = $14, + recipient = $15, + bridge_msg_amount = $16, + bridge_msg_allow_fast = $17, + bridge_msg_details_hash = $18, + bridge_msg_token_domain = $19, + bridge_msg_token_id = $20, + sender = $21, + raw = $22, + leaf_index = $23, + block = $24, + evm = $25 WHERE hash = $1 `; return this.pool.query(query, m.intoDB()); diff --git a/typescript/nomad-monitor/src/indexer/event.ts b/typescript/nomad-monitor/src/indexer/core/event.ts similarity index 99% rename from typescript/nomad-monitor/src/indexer/event.ts rename to typescript/nomad-monitor/src/indexer/core/event.ts index 18d1f599c..6726e7693 100644 --- a/typescript/nomad-monitor/src/indexer/event.ts +++ b/typescript/nomad-monitor/src/indexer/core/event.ts @@ -42,6 +42,7 @@ export type EventData = { originAndNonce?: ethers.BigNumber; recipient?: string; liquidityProvider?: string; + evmHash?: string; }; export class NomadEvent { diff --git a/typescript/nomad-monitor/src/indexer/core/index.ts b/typescript/nomad-monitor/src/indexer/core/index.ts new file mode 100644 index 000000000..6e39e0735 --- /dev/null +++ b/typescript/nomad-monitor/src/indexer/core/index.ts @@ -0,0 +1,49 @@ +import { mainnet } from '@nomad-xyz/sdk/src'; +import { Processor } from './consumer'; +import { Orchestrator } from './orchestrator'; +import * as dotenv from 'dotenv'; +import { ethers } from 'ethers'; +import { IndexerCollector } from './metrics'; +import Logger from 'bunyan'; +import { DB } from './db'; +dotenv.config({}); + +const infuraKey = process.env.INFURA_KEY!; +const moonbeamRPC = process.env.MOONBEAM_RPC!; +const environment = process.env.ENVIRONMENT!; + +export async function run(db: DB) { + const ctx = mainnet; + + const ethereumId = ctx.mustGetDomain('ethereum').id; + const infura = new ethers.providers.InfuraProvider('homestead', infuraKey); + ctx.registerProvider(ethereumId, infura); + + const moonbeamId = ctx.mustGetDomain('moonbeam').id; + ctx.registerRpcProvider(moonbeamId, moonbeamRPC); + const logger = createLogger('indexer', environment); + + const c = new Processor(db, logger); + const m = new IndexerCollector(environment, logger); + + const o = new Orchestrator( + mainnet, + c, + mainnet.domainNumbers[0], + m, + logger, + db, + ); + await o.init(); + + await o.startConsuming(); +} + +function createLogger(name: string, environment: string) { + return Logger.createLogger({ + name, + serializers: Logger.stdSerializers, + level: 'debug', + environment: environment, + }); +} diff --git a/typescript/nomad-monitor/src/indexer/indexer.ts b/typescript/nomad-monitor/src/indexer/core/indexer.ts similarity index 86% rename from typescript/nomad-monitor/src/indexer/indexer.ts rename to typescript/nomad-monitor/src/indexer/core/indexer.ts index 21a5516f4..b1f96a6a4 100644 --- a/typescript/nomad-monitor/src/indexer/indexer.ts +++ b/typescript/nomad-monitor/src/indexer/core/indexer.ts @@ -12,7 +12,7 @@ export class Indexer { sdk: NomadContext; orchestrator: Orchestrator; persistance: Persistance; - block2timeCache: KVCache; + blockCache: KVCache; eventCallback: undefined | ((event: NomadEvent) => void); constructor(domain: number, sdk: NomadContext, orchestrator: Orchestrator) { @@ -22,21 +22,28 @@ export class Indexer { this.persistance = new RamPersistance( `/tmp/persistance_${this.domain}.json`, ); - this.block2timeCache = new KVCache( + this.blockCache = new KVCache( String(this.domain), this.orchestrator.db, ); + } get provider(): ethers.providers.Provider { return this.sdk.getProvider(this.domain)!; } - async getBlockTimestamp(blockNumber: number): Promise { - const possibleTime = this.block2timeCache.get(String(blockNumber)); - if (possibleTime) return parseInt(possibleTime); + async getBlockInfo(blockNumber: number): Promise<[number, Map]> { + const possibleBlock = this.blockCache.get(String(blockNumber)); + if (possibleBlock) { + const [ts, txs] = possibleBlock.split('.'); + const x: string[] = txs.split(','); + const senders2hashes: Map = new Map(x.map(tx => tx.split(':') as [string, string])); + return [parseInt(ts), senders2hashes]; + } + const [block, error] = await retry( - async () => await this.provider.getBlock(blockNumber), + async () => await this.provider.getBlockWithTransactions(blockNumber), 6, ); if (!block) { @@ -48,12 +55,15 @@ export class Indexer { ); } const time = block.timestamp * 1000; - await this.block2timeCache.set(String(blockNumber), String(time)); - return time; + const senders2hashes: Map = new Map(block.transactions.map(tx => [tx.from, tx.hash])); + const senders2hashesStr = Array.from(senders2hashes.entries()).map(([from, hash]) => `${from}:${hash}`).join(',') + await this.blockCache.set(String(blockNumber), `${time}.${senders2hashesStr}`); + // await this.block2timeCache.set(String(blockNumber), String(block.transactions.map(tx => tx.from).join(','))); + return [time, senders2hashes]; } async init() { - await this.block2timeCache.init(); + await this.blockCache.init(); await this.persistance.init(); } @@ -205,23 +215,27 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( async (event) => - new NomadEvent( - this.domain, - EventType.BridgeRouterSend, - ContractType.BridgeRouter, - 0, - await this.getBlockTimestamp(event.blockNumber), - { - token: event.args[0], - from: event.args[1], - toDomain: event.args[2], - toId: event.args[3], - amount: event.args[4], - fastLiquidityEnabled: event.args[5], - }, - event.blockNumber, - EventSource.Fetch, - ), + { + const [ts, senders2hashes] = await this.getBlockInfo(event.blockNumber); + return new NomadEvent( + this.domain, + EventType.BridgeRouterSend, + ContractType.BridgeRouter, + 0, + ts, + { + token: event.args[0], + from: event.args[1], + toDomain: event.args[2], + toId: event.args[3], + amount: event.args[4], + fastLiquidityEnabled: event.args[5], + evmHash: senders2hashes.get(event.args[1])!, + }, + event.blockNumber, + EventSource.Fetch, + ) + } ), ); allEvents.push(...parsedEvents) @@ -237,7 +251,7 @@ export class Indexer { EventType.BridgeRouterReceive, ContractType.BridgeRouter, 0, - await this.getBlockTimestamp(event.blockNumber), + (await this.getBlockInfo(event.blockNumber))[0], { originAndNonce: event.args[0], token: event.args[1], @@ -271,7 +285,7 @@ export class Indexer { EventType.HomeDispatch, ContractType.Home, 0, - await this.getBlockTimestamp(event.blockNumber), + (await this.getBlockInfo(event.blockNumber))[0], { messageHash: event.args[0], leafIndex: event.args[1], @@ -297,7 +311,7 @@ export class Indexer { EventType.HomeUpdate, ContractType.Home, 0, - await this.getBlockTimestamp(event.blockNumber), + (await this.getBlockInfo(event.blockNumber))[0], { homeDomain: event.args[0], oldRoot: event.args[1], @@ -333,7 +347,7 @@ export class Indexer { EventType.ReplicaUpdate, ContractType.Replica, domain, - await this.getBlockTimestamp(event.blockNumber), + (await this.getBlockInfo(event.blockNumber))[0], { homeDomain: event.args[0], oldRoot: event.args[1], @@ -362,7 +376,7 @@ export class Indexer { EventType.ReplicaProcess, ContractType.Replica, domain, - await this.getBlockTimestamp(event.blockNumber), + (await this.getBlockInfo(event.blockNumber))[0], { messageHash: event.args[0], success: event.args[1], diff --git a/typescript/nomad-monitor/src/indexer/metrics.ts b/typescript/nomad-monitor/src/indexer/core/metrics.ts similarity index 98% rename from typescript/nomad-monitor/src/indexer/metrics.ts rename to typescript/nomad-monitor/src/indexer/core/metrics.ts index e5c33a8d9..00cb3b65d 100644 --- a/typescript/nomad-monitor/src/indexer/metrics.ts +++ b/typescript/nomad-monitor/src/indexer/core/metrics.ts @@ -1,4 +1,4 @@ -import { MetricsCollector } from '../metrics'; +import { MetricsCollector } from '../../metrics'; import { Gauge } from 'prom-client'; import Logger from 'bunyan'; diff --git a/typescript/nomad-monitor/src/indexer/orchestrator.ts b/typescript/nomad-monitor/src/indexer/core/orchestrator.ts similarity index 97% rename from typescript/nomad-monitor/src/indexer/orchestrator.ts rename to typescript/nomad-monitor/src/indexer/core/orchestrator.ts index 95037b7ca..486ed5770 100644 --- a/typescript/nomad-monitor/src/indexer/orchestrator.ts +++ b/typescript/nomad-monitor/src/indexer/core/orchestrator.ts @@ -1,7 +1,7 @@ import { NomadContext } from '@nomad-xyz/sdk/src'; import Logger from 'bunyan'; import { Consumer } from './consumer'; -import { DBDriver } from './db'; +import { DB } from './db'; import { Indexer } from './indexer'; import { IndexerCollector } from './metrics'; import { Statistics } from './types'; @@ -16,7 +16,7 @@ export class Orchestrator { freshStart: boolean; metrics: IndexerCollector; logger: Logger; - db: DBDriver; + db: DB; constructor( sdk: NomadContext, @@ -24,7 +24,7 @@ export class Orchestrator { gov: number, metrics: IndexerCollector, logger: Logger, - db: DBDriver, + db: DB, ) { this.sdk = sdk; this.consumer = c; diff --git a/typescript/nomad-monitor/src/indexer/types.ts b/typescript/nomad-monitor/src/indexer/core/types.ts similarity index 100% rename from typescript/nomad-monitor/src/indexer/types.ts rename to typescript/nomad-monitor/src/indexer/core/types.ts diff --git a/typescript/nomad-monitor/src/indexer/utils.ts b/typescript/nomad-monitor/src/indexer/core/utils.ts similarity index 96% rename from typescript/nomad-monitor/src/indexer/utils.ts rename to typescript/nomad-monitor/src/indexer/core/utils.ts index 250e03f89..cfc8b6179 100644 --- a/typescript/nomad-monitor/src/indexer/utils.ts +++ b/typescript/nomad-monitor/src/indexer/core/utils.ts @@ -2,7 +2,7 @@ import { ethers } from 'ethers'; import { NomadEvent } from './event'; import fs from 'fs'; import { Mean } from './types'; -import { DBDriver } from './db'; +import { DB } from './db'; export function sleep(ms: number) { return new Promise((resolve) => { @@ -68,9 +68,9 @@ export function reviver(key: any, value: any): any { export class KVCache { m: Map; name: string; - db: DBDriver; + db: DB; - constructor(name: string, db: DBDriver) { + constructor(name: string, db: DB) { this.db = db; this.m = new Map(); this.name = name; diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts index 950ecbc37..a5882b877 100644 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ b/typescript/nomad-monitor/src/indexer/main.ts @@ -1,52 +1,17 @@ -import { mainnet } from '@nomad-xyz/sdk/src'; -import { Processor } from './consumer'; -import { Orchestrator } from './orchestrator'; -import * as dotenv from 'dotenv'; -import { ethers } from 'ethers'; -import { IndexerCollector } from './metrics'; -import Logger from 'bunyan'; -import { DBDriver } from './db'; +import * as core from './core'; +import * as api from './api'; +import { DB } from './core/db'; -dotenv.config({}); - -const infuraKey = process.env.INFURA_KEY!; -const moonbeamRPC = process.env.MOONBEAM_RPC!; -const environment = process.env.ENVIRONMENT!; +console.log(process.argv[2]); (async () => { - const ctx = mainnet; - const ethereumId = ctx.mustGetDomain('ethereum').id; - const moonbeamId = ctx.mustGetDomain('moonbeam').id; - const infura = new ethers.providers.InfuraProvider('homestead', infuraKey); - - ctx.registerProvider(ethereumId, infura); - - ctx.registerRpcProvider(moonbeamId, moonbeamRPC); - const logger = createLogger('indexer', environment); - - const db = new DBDriver(); - - const c = new Processor(db, logger); - const m = new IndexerCollector(environment, logger); - - const o = new Orchestrator( - mainnet, - c, - mainnet.domainNumbers[0], - m, - logger, - db, - ); - await o.init(); - - await o.startConsuming(); -})(); - -function createLogger(name: string, environment: string) { - return Logger.createLogger({ - name, - serializers: Logger.stdSerializers, - level: 'debug', - environment: environment, - }); -} + const db = new DB(); + await db.connect(); + + if (process.argv[2] === 'api') { + await api.run(db) + } else { + await core.run(db); + } + +})() \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js index 7438588bb..8b0ea3667 100644 --- a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js +++ b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js @@ -13,9 +13,11 @@ exports.up = pgm => { nomad_recipient: {type: 'varchar(42)', notNull: true}, // Nomad recipient (bridge router) root: {type: 'varchar(66)', notNull: true}, state: {type: 'integer', notNull: true}, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) + block: {type: 'integer', notNull: true}, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) dispatched_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state dispatched updated_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state updated relayed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state relayed + received_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state received processed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state processed // Bridge message internals sender: {type: 'varchar(42)', notNull: false}, // sender @@ -26,6 +28,9 @@ exports.up = pgm => { bridge_msg_details_hash: {type: 'varchar(66)', notNull: false}, // Details hash - don't know what it is (need to do homework) bridge_msg_token_domain: {type: 'integer', notNull: false}, // Token domain bridge_msg_token_id: {type: 'varchar(42)', notNull: false}, // Token id (address) + raw: {type: 'varchar', notNull: true}, + leaf_index: {type: 'varchar(256)', notNull: true}, + evm: {type: 'varchar(66)', notNull: false}, createdAt: { type: 'timestamp', notNull: true, From d8e5a5c4215aff9132da4debb9c7f9be2299eed6 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 25 Jan 2022 16:23:30 +0100 Subject: [PATCH 18/63] feature: db request --- .../nomad-monitor/src/indexer/api/index.ts | 19 ++++- .../src/indexer/core/consumer.ts | 2 +- .../nomad-monitor/src/indexer/core/db.ts | 71 ++++++++++++++++++- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/typescript/nomad-monitor/src/indexer/api/index.ts b/typescript/nomad-monitor/src/indexer/api/index.ts index 9d91454f8..0b0d68e19 100644 --- a/typescript/nomad-monitor/src/indexer/api/index.ts +++ b/typescript/nomad-monitor/src/indexer/api/index.ts @@ -1,9 +1,14 @@ import express from 'express'; -import { DB } from '../core/db'; +import { DB, MsgRequest } from '../core/db'; import * as dotenv from 'dotenv'; dotenv.config({}); +function fail(res: any, code: number, reason: string) { + return res.status(code).json({error: reason}); +} + + const PORT = process.env.PORT; export async function run(db: DB) { @@ -21,6 +26,18 @@ export async function run(db: DB) { return res.json(message.toObject()) }); + app.get('/tx', async (req: express.Request<{}, {}, {}, MsgRequest>,res) => { + + const {size, } = req.query; // page, destination, origin, receiver, sender + + if (size && size > 30) return fail(res, 403, 'maximum size is 30') + + const messages = await db.getMessages(req.query); + + // const message = await db.getMessageByHash(req.params.hash); + return res.json(messages) + }); + app.listen(PORT, () => { console.log(`⚡️[server]: Server is running at https://localhost:${PORT}`); }); diff --git a/typescript/nomad-monitor/src/indexer/core/consumer.ts b/typescript/nomad-monitor/src/indexer/core/consumer.ts index 2ef52ef7c..1dd933076 100644 --- a/typescript/nomad-monitor/src/indexer/core/consumer.ts +++ b/typescript/nomad-monitor/src/indexer/core/consumer.ts @@ -500,7 +500,7 @@ class SenderLostAndFound { if (index >= 0) { const brSend = this.bridgeRouterSendEvents.at(index); if (brSend) { - m.sender = brSend.eventData.from!; + m.updateSender(brSend.eventData.from!); m.evm = brSend.eventData.evmHash!; } this.bridgeRouterSendEvents.splice(index, 1); diff --git a/typescript/nomad-monitor/src/indexer/core/db.ts b/typescript/nomad-monitor/src/indexer/core/db.ts index 3589c509e..c2455fe33 100644 --- a/typescript/nomad-monitor/src/indexer/core/db.ts +++ b/typescript/nomad-monitor/src/indexer/core/db.ts @@ -16,6 +16,36 @@ function expand(rowCount: number, columnCount: number, startAt = 1) { .join(', '); } +export interface MsgRequest { + size?: number, page?: number, destination?: number, origin?: number, recipient?: string, sender?: string +} + +class Contr { + args: string[]; + offset: number; + constructor(offset: number) { + this.args = []; + this.offset = offset; + } + + add(arg: string) { + this.args.push(arg); + } + + static fromReq(r: MsgRequest, offset=0): string { + const c = new Contr(offset); + if (r.sender) c.add('sender'); + if (r.recipient) c.add('recipient'); + if (r.origin) c.add('origin'); + if (r.destination) c.add('destination'); + return c.construct(); + } + + construct(): string { + return this.args.length ? 'where ' + this.args.map((a, i) => `${a} = $${i+1 + this.offset}`).join(' and ') : '' + } +} + export class DB { pool: Pool; syncedOnce: boolean; @@ -36,7 +66,7 @@ export class DB { } async getMessageByEvm(tx: string): Promise { - const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, dispatched_at, updated_at, relayed_at, received_at, processed_at, hash FROM messages where evm = $1;`; + const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, dispatched_at, updated_at, relayed_at, received_at, processed_at, hash FROM messages where evm = $1 order by dispatched_at desc;`; const result = await this.pool.query(query, [tx.toLowerCase()]); const entry = result.rows[0]; return NomadMessage.fromDB(entry.origin, @@ -58,7 +88,7 @@ export class DB { } async getMessageByHash(hash: string): Promise { - const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages where hash = $1;`; + const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages where hash = $1 order by dispatched_at desc;`; const result = await this.pool.query(query, [hash.toLowerCase()]); const entry = result.rows[0]; return NomadMessage.fromDB(entry.origin, @@ -79,6 +109,43 @@ export class DB { ) } + async getMessages(req: MsgRequest): Promise { + const limit = req.size || 15; + const page = req.page || 1; + const offset = (page || -1) * limit; + const args: any[] = [limit, offset]; + + const c = new Contr(args.length) + + if (req.sender) {c.add('sender'); args.push(req.sender)}; + if (req.recipient) {c.add('recipient'); args.push(req.recipient)}; + if (req.origin) {c.add('origin'); args.push(req.origin)}; + if (req.destination) {c.add('destination'); args.push(req.destination)}; + + const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, hash, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages ${c.construct()} order by dispatched_at desc limit $1 offset $2;`; + console.log(`Query`, query) + const result = await this.pool.query(query, args); + + return result.rows.map(entry => { + return NomadMessage.fromDB(entry.origin, + entry.destination, + entry.nonce, + entry.root, + entry.hash, + entry.leaf_index, + entry.raw, + entry.block, + entry.dispatched_at, + entry.updated_at, + entry.relayed_at, + entry.received_at, + entry.processed_at, + entry.sender, + entry.evm, + ) + }) + } + async insertMessage(messages: NomadMessage[]) { const rows = messages.length; if (!rows) return; From ebc7d8f71fdb3b44826720e4beb6c6df1aa8608b Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 25 Jan 2022 21:25:22 +0100 Subject: [PATCH 19/63] feature: docker --- tools/indexer/Dockerfile | 21 + tools/indexer/api/index.ts | 56 + .../src => tools}/indexer/core/consumer.ts | 167 +- .../src => tools}/indexer/core/db.ts | 91 +- .../src => tools}/indexer/core/event.ts | 49 +- tools/indexer/core/index.ts | 38 + .../src => tools}/indexer/core/indexer.ts | 177 +- tools/indexer/core/metrics.ts | 162 + .../indexer/core/orchestrator.ts | 24 +- .../src => tools}/indexer/core/types.ts | 6 +- .../src => tools}/indexer/core/utils.ts | 51 +- tools/indexer/main.ts | 20 + .../1642713407510_my-first-migration.js | 63 + tools/indexer/package-lock.json | 9575 +++++++++++++++++ tools/indexer/package.json | 37 + tools/indexer/run.sh | 3 + .../nomad-monitor/src/indexer/api/index.ts | 44 - .../nomad-monitor/src/indexer/core/index.ts | 49 - .../nomad-monitor/src/indexer/core/metrics.ts | 128 - typescript/nomad-monitor/src/indexer/main.ts | 17 - .../1642713407510_my-first-migration.js | 63 - 21 files changed, 10292 insertions(+), 549 deletions(-) create mode 100644 tools/indexer/Dockerfile create mode 100644 tools/indexer/api/index.ts rename {typescript/nomad-monitor/src => tools}/indexer/core/consumer.ts (85%) rename {typescript/nomad-monitor/src => tools}/indexer/core/db.ts (79%) rename {typescript/nomad-monitor/src => tools}/indexer/core/event.ts (79%) create mode 100644 tools/indexer/core/index.ts rename {typescript/nomad-monitor/src => tools}/indexer/core/indexer.ts (81%) create mode 100644 tools/indexer/core/metrics.ts rename {typescript/nomad-monitor/src => tools}/indexer/core/orchestrator.ts (88%) rename {typescript/nomad-monitor/src => tools}/indexer/core/types.ts (95%) rename {typescript/nomad-monitor/src => tools}/indexer/core/utils.ts (68%) create mode 100644 tools/indexer/main.ts create mode 100644 tools/indexer/migrations/1642713407510_my-first-migration.js create mode 100644 tools/indexer/package-lock.json create mode 100644 tools/indexer/package.json create mode 100644 tools/indexer/run.sh delete mode 100644 typescript/nomad-monitor/src/indexer/api/index.ts delete mode 100644 typescript/nomad-monitor/src/indexer/core/index.ts delete mode 100644 typescript/nomad-monitor/src/indexer/core/metrics.ts delete mode 100644 typescript/nomad-monitor/src/indexer/main.ts delete mode 100644 typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js diff --git a/tools/indexer/Dockerfile b/tools/indexer/Dockerfile new file mode 100644 index 000000000..076e8ce7c --- /dev/null +++ b/tools/indexer/Dockerfile @@ -0,0 +1,21 @@ +FROM node:16 +ENV NODE_ENV=production + +WORKDIR /app + +COPY package.json ./package.json +COPY package-lock.json ./package-lock.json +COPY nomad-xyz-sdk-1.2.4.tgz ./nomad-xyz-sdk-1.2.4.tgz +RUN npm i + + +RUN npm i ./nomad-xyz-sdk-1.2.4.tgz + +# RUN rm ./nomad-xyz-sdk-1.2.4.tgz + +COPY api ./api +COPY core ./core +COPY migrations ./migrations +COPY main.ts ./main.ts + +CMD [ "npm", "run", "start" ] \ No newline at end of file diff --git a/tools/indexer/api/index.ts b/tools/indexer/api/index.ts new file mode 100644 index 000000000..e54cb7a8a --- /dev/null +++ b/tools/indexer/api/index.ts @@ -0,0 +1,56 @@ +import express from "express"; +import { DB, MsgRequest } from "../core/db"; +import * as dotenv from "dotenv"; +import Logger from "bunyan"; +dotenv.config({}); + +function fail(res: any, code: number, reason: string) { + return res.status(code).json({ error: reason }); +} + +const PORT = process.env.PORT; + +export async function run(db: DB, logger: Logger) { + const app = express(); + + const log = ( + req: express.Request, + res: express.Response, + next: express.NextFunction + ) => { + logger.info(`request to ${req.url}`); + next(); + }; + + app.get("/healthcheck", log, (req, res) => { + res.send("OK!"); + }); + + app.get("/tx/:tx", log, async (req, res) => { + const message = await db.getMessageByEvm(req.params.tx); + return res.json(message.toObject()); + }); + + app.get("/hash/:hash", log, async (req, res) => { + const message = await db.getMessageByHash(req.params.hash); + return res.json(message.toObject()); + }); + + app.get( + "/tx", + log, + async (req: express.Request<{}, {}, {}, MsgRequest>, res) => { + const { size } = req.query; + + if (size && size > 30) return fail(res, 403, "maximum page size is 30"); + + const messages = await db.getMessages(req.query); + + return res.json(messages); + } + ); + + app.listen(PORT, () => { + logger.info(`Server is running at https://localhost:${PORT}`); + }); +} diff --git a/typescript/nomad-monitor/src/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts similarity index 85% rename from typescript/nomad-monitor/src/indexer/core/consumer.ts rename to tools/indexer/core/consumer.ts index 1dd933076..43524b8f4 100644 --- a/typescript/nomad-monitor/src/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -1,12 +1,11 @@ -import { parseMessage } from '@nomad-xyz/sdk/src/nomad/messages/NomadMessage'; -import { ethers } from 'ethers'; -import { EventType, NomadEvent } from './event'; -import { Statistics } from './types'; -import { parseBody } from '@nomad-xyz/sdk/src/nomad/messages/BridgeMessage'; -import { parseAction } from '@nomad-xyz/sdk/src/nomad/messages/GovernanceMessage'; -import { DB } from './db'; -import Logger from 'bunyan'; - +import { parseMessage } from "@nomad-xyz/sdk/dist/nomad/messages/NomadMessage"; +import { ethers } from "ethers"; +import { EventType, NomadEvent } from "./event"; +import { Statistics } from "./types"; +import { parseBody } from "@nomad-xyz/sdk/dist/nomad/messages/BridgeMessage"; +import { parseAction } from "@nomad-xyz/sdk/dist/nomad/messages/GovernanceMessage"; +import { DB } from "./db"; +import Logger from "bunyan"; class StatisticsCollector { s: Statistics; @@ -63,7 +62,9 @@ class StatisticsCollector { const inReceiveStat = m.timings.inReceived(); if (inReceiveStat) { this.s.timings.total.meanReceive.add(inReceiveStat); - this.s.timings.domainStatistics.get(m.origin)!.meanReceive.add(inReceiveStat); + this.s.timings.domainStatistics + .get(m.origin)! + .meanReceive.add(inReceiveStat); } } @@ -202,7 +203,10 @@ class Timings { if (this.processedAt) { return ( this.processedAt - - (this.receivedAt || this.relayedAt || this.updatedAt || this.dispatchedAt) + (this.receivedAt || + this.relayedAt || + this.updatedAt || + this.dispatchedAt) ); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available } return undefined; @@ -212,7 +216,10 @@ class Timings { if (this.processedAt) { return ( this.processedAt - - (this.dispatchedAt || this.updatedAt || this.relayedAt || this.receivedAt) + (this.dispatchedAt || + this.updatedAt || + this.relayedAt || + this.receivedAt) ); // same as for .inRelayed() and .inProcessed() but opposit order } return undefined; @@ -220,15 +227,14 @@ class Timings { } function bytes32ToAddress(s: string) { - return '0x' + s.slice(26); + return "0x" + s.slice(26); } - enum MessageType { NoMessage, TransferMessage, GovernanceMessage, -}; +} export class NomadMessage { origin: number; @@ -253,7 +259,6 @@ export class NomadMessage { timings: Timings; block: number; evm?: string; - constructor( origin: number, @@ -265,13 +270,13 @@ export class NomadMessage { // destinationAndNonce: ethers.BigNumber, message: string, createdAt: number, - block: number, + block: number ) { this.origin = origin; this.destination = destination; this.nonce = nonce; - this.root = root; - this.hash = hash; + this.root = root.toLowerCase(); + this.hash = hash.toLowerCase(); this.leafIndex = leafIndex; // this.destinationAndNonce = destinationAndNonce; this.raw = message; @@ -309,7 +314,7 @@ export class NomadMessage { state: this.state, timings: this.timings, tx: this.evm, - } + }; } static fromDB( @@ -327,9 +332,19 @@ export class NomadMessage { processedAt: number, block: number, sender: string, - evm: string, + evm: string ): NomadMessage { - const m = new NomadMessage(origin, destination, nonce, root, hash, leafIndex, message, createdAt, block); + const m = new NomadMessage( + origin, + destination, + nonce, + root, + hash, + leafIndex, + message, + createdAt, + block + ); m.timings.updated(updatedAt); m.timings.relayed(relayedAt); m.timings.received(receivedAt); @@ -340,22 +355,26 @@ export class NomadMessage { } tryParseMessage(body: string) { - this.tryParseTransferMessage(body) || this.tryParseGovernanceMessage(body) + this.tryParseTransferMessage(body) || this.tryParseGovernanceMessage(body); } tryParseTransferMessage(body: string): boolean { try { const bridgeMessage = parseBody(body); this.bridgeMsgType = bridgeMessage.action.type as string; - this.bridgeMsgTo = bytes32ToAddress(bridgeMessage.action.to); + this.bridgeMsgTo = bytes32ToAddress( + bridgeMessage.action.to + ).toLowerCase(); this.bridgeMsgAmount = bridgeMessage.action.amount; this.bridgeMsgAllowFast = bridgeMessage.action.allowFast; this.bridgeMsgDetailsHash = bridgeMessage.action.detailsHash; this.bridgeMsgTokenDomain = bridgeMessage.token.domain as number; - this.bridgeMsgTokenId = bytes32ToAddress(bridgeMessage.token.id as string); + this.bridgeMsgTokenId = bytes32ToAddress( + bridgeMessage.token.id as string + ).toLowerCase(); this.hasMessage = MessageType.TransferMessage; return true; - } catch(e) { + } catch (e) { return false; } } @@ -364,7 +383,7 @@ export class NomadMessage { try { const message = parseAction(body); if (message.type == "batch") { - message.batchHash + message.batchHash; } else { message.address; message.domain; @@ -372,7 +391,7 @@ export class NomadMessage { this.bridgeMsgType = message.type; this.hasMessage = MessageType.GovernanceMessage; return true; - } catch(e) { + } catch (e) { return false; } } @@ -410,34 +429,34 @@ export class NomadMessage { string, string, number, - string|undefined, + string | undefined ] { return [ - this.hash, + this.hash.toLowerCase(), this.origin, this.destination, this.nonce, - this.nomadSender, - this.nomadRecipient, - this.root, + this.nomadSender.toLowerCase(), + this.nomadRecipient.toLowerCase(), + this.root.toLowerCase(), this.state, this.timings.dispatchedAt, this.timings.updatedAt, this.timings.relayedAt, this.timings.receivedAt, this.timings.processedAt, - this.bridgeMsgType, - this.bridgeMsgTo, + this.bridgeMsgType?.toLowerCase(), + this.bridgeMsgTo?.toLowerCase(), this.bridgeMsgAmount?.toString(), this.bridgeMsgAllowFast, - this.bridgeMsgDetailsHash, + this.bridgeMsgDetailsHash?.toLowerCase(), this.bridgeMsgTokenDomain, - this.bridgeMsgTokenId, - this.sender, + this.bridgeMsgTokenId?.toLowerCase(), + this.sender?.toLowerCase(), this.raw, this.leafIndex.toString(), this.block, - this.evm, + this.evm?.toLowerCase(), ]; } } @@ -448,27 +467,28 @@ class SenderLostAndFound { bridgeRouterSendEvents: NomadEvent[]; constructor(p: Processor) { this.p = p; - this.dispatchEventsWithMessages = [] - this.bridgeRouterSendEvents = [] + this.dispatchEventsWithMessages = []; + this.bridgeRouterSendEvents = []; } bridgeRouterSend(e: NomadEvent): string | undefined { // check if we have dispatch events with block >= current && block <= current + 4; const hash = this.findMatchingDispatchAndUpdateAndRemove(e); if (hash) { - return hash + return hash; } else { //add event for further fixing from dispatch side this.bridgeRouterSendEvents.push(e); return undefined; } - } - findMatchingDispatchAndUpdateAndRemove(brSend: NomadEvent): string|undefined { - const index = this.dispatchEventsWithMessages.findIndex( - ([dispatch, m]) => this.match(dispatch, brSend, m) + findMatchingDispatchAndUpdateAndRemove( + brSend: NomadEvent + ): string | undefined { + const index = this.dispatchEventsWithMessages.findIndex(([dispatch, m]) => + this.match(dispatch, brSend, m) ); - + if (index >= 0) { const some = this.dispatchEventsWithMessages.at(index); if (some) { @@ -479,23 +499,26 @@ class SenderLostAndFound { this.dispatchEventsWithMessages.splice(index, 1); return msg.hash; } - } return undefined; } match(dispatch: NomadEvent, brSend: NomadEvent, m: NomadMessage): boolean { - return brSend.eventData.toDomain! === m.destination && //brSend.eventData.token?.toLowerCase() === m.bridgeMsgTokenId?.toLowerCase() && - bytes32ToAddress(brSend.eventData.toId!).toLowerCase() === m.bridgeMsgTo?.toLowerCase() && - brSend.eventData.amount!.eq(m.bridgeMsgAmount!) && - ( + return ( + brSend.eventData.toDomain! === m.destination && //brSend.eventData.token?.toLowerCase() === m.bridgeMsgTokenId?.toLowerCase() && + bytes32ToAddress(brSend.eventData.toId!).toLowerCase() === + m.bridgeMsgTo?.toLowerCase() && + brSend.eventData.amount!.eq(m.bridgeMsgAmount!) && brSend.block === dispatch.block //&& // (dispatch.block - brSend.block <= 2 || brSend.block - dispatch.block <= 30) - ) + ); } - findMatchingBRSendUpdateAndRemove(dispatch: NomadEvent, m: NomadMessage): boolean { - const index = this.bridgeRouterSendEvents.findIndex( - (brSend) => this.match(dispatch, brSend, m) + findMatchingBRSendUpdateAndRemove( + dispatch: NomadEvent, + m: NomadMessage + ): boolean { + const index = this.bridgeRouterSendEvents.findIndex((brSend) => + this.match(dispatch, brSend, m) ); if (index >= 0) { const brSend = this.bridgeRouterSendEvents.at(index); @@ -515,10 +538,9 @@ class SenderLostAndFound { if (this.findMatchingBRSendUpdateAndRemove(e, m)) { return true; } else { - this.dispatchEventsWithMessages.push([e,m]); + this.dispatchEventsWithMessages.push([e, m]); return false; } - } } @@ -572,7 +594,9 @@ export class Processor extends Consumer { async sync() { const [inserts, updates] = await this.getMsgForSync(); - this.logger.info(`Inserting ${inserts.length} messages and updating ${updates.length}`); + this.logger.info( + `Inserting ${inserts.length} messages and updating ${updates.length}` + ); await Promise.all([ this.db.insertMessage(inserts), @@ -607,9 +631,7 @@ export class Processor extends Consumer { return hashes.map((hash) => this.getMsg(hash)!).filter((m) => !!m); } - dispatched(e: NomadEvent) { - const m = new NomadMessage( e.domain, ...e.destinationAndNonce(), @@ -619,7 +641,7 @@ export class Processor extends Consumer { // e.eventData.destinationAndNonce!, e.eventData.message!, e.ts, - e.block, + e.block ); this.senderRegistry.dispatch(e, m); @@ -630,8 +652,6 @@ export class Processor extends Consumer { if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } - - homeUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); if (ms.length) @@ -647,7 +667,7 @@ export class Processor extends Consumer { replicaUpdate(e: NomadEvent) { const ms = this.getMsgsByOriginAndRoot( e.replicaOrigin, - e.eventData.oldRoot!, + e.eventData.oldRoot! ); if (ms.length) ms.forEach((m) => { @@ -670,20 +690,16 @@ export class Processor extends Consumer { } } - - bridgeRouterSend(e: NomadEvent) { - const hash = this.senderRegistry.bridgeRouterSend(e); if (hash) { // console.log(`Setting bridgeRouterSend for `, this.getMsg(hash)?.sender) - this.addToSyncQueue(hash) + this.addToSyncQueue(hash); } - } bridgeRouterReceive(e: NomadEvent) { - const m = this.getMsgsByOriginAndNonce(...e.originAndNonce()) + const m = this.getMsgsByOriginAndNonce(...e.originAndNonce()); if (m) { if (m.state < MsgState.Received) { @@ -708,7 +724,7 @@ export class Processor extends Consumer { } getMsg(id: string | number): NomadMessage | undefined { - if (typeof id === 'string') { + if (typeof id === "string") { const msgIndex = this.msgToIndex.get(id); if (msgIndex) return this.messages[msgIndex]; } else { @@ -724,8 +740,11 @@ export class Processor extends Consumer { return []; } - getMsgsByOriginAndNonce(origin: number, nonce: number): NomadMessage | undefined { - return this.messages.find((m) => m.nonce === nonce && m.origin === origin) + getMsgsByOriginAndNonce( + origin: number, + nonce: number + ): NomadMessage | undefined { + return this.messages.find((m) => m.nonce === nonce && m.origin === origin); } stats(): Statistics { diff --git a/typescript/nomad-monitor/src/indexer/core/db.ts b/tools/indexer/core/db.ts similarity index 79% rename from typescript/nomad-monitor/src/indexer/core/db.ts rename to tools/indexer/core/db.ts index c2455fe33..47a3be416 100644 --- a/typescript/nomad-monitor/src/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -1,5 +1,5 @@ -import { NomadMessage } from './consumer'; -import { Pool } from 'pg'; +import { NomadMessage } from "./consumer"; +import { Pool } from "pg"; // expand(3, 2) returns "($1, $2), ($3, $4), ($5, $6)" function expand(rowCount: number, columnCount: number, startAt = 1) { @@ -11,13 +11,18 @@ function expand(rowCount: number, columnCount: number, startAt = 1) { `(${Array(columnCount) .fill(0) .map((v) => `$${index++}`) - .join(', ')})`, + .join(", ")})` ) - .join(', '); + .join(", "); } export interface MsgRequest { - size?: number, page?: number, destination?: number, origin?: number, recipient?: string, sender?: string + size?: number; + page?: number; + destination?: number; + origin?: number; + recipient?: string; + sender?: string; } class Contr { @@ -29,20 +34,26 @@ class Contr { } add(arg: string) { + if (!arg.match(/[a-z]+/)) throw new Error(`Can add only a-z characters`); this.args.push(arg); } - - static fromReq(r: MsgRequest, offset=0): string { + + static fromReq(r: MsgRequest, offset = 0): string { const c = new Contr(offset); - if (r.sender) c.add('sender'); - if (r.recipient) c.add('recipient'); - if (r.origin) c.add('origin'); - if (r.destination) c.add('destination'); + if (r.sender) c.add("sender"); + if (r.recipient) c.add("recipient"); + if (r.origin) c.add("origin"); + if (r.destination) c.add("destination"); return c.construct(); } construct(): string { - return this.args.length ? 'where ' + this.args.map((a, i) => `${a} = $${i+1 + this.offset}`).join(' and ') : '' + return this.args.length + ? "where " + + this.args + .map((a, i) => `${a} = $${i + 1 + this.offset}`) + .join(" and ") + : ""; } } @@ -69,7 +80,8 @@ export class DB { const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, dispatched_at, updated_at, relayed_at, received_at, processed_at, hash FROM messages where evm = $1 order by dispatched_at desc;`; const result = await this.pool.query(query, [tx.toLowerCase()]); const entry = result.rows[0]; - return NomadMessage.fromDB(entry.origin, + return NomadMessage.fromDB( + entry.origin, entry.destination, entry.nonce, entry.root, @@ -83,15 +95,16 @@ export class DB { entry.received_at, entry.processed_at, entry.sender, - tx, - ) + tx + ); } async getMessageByHash(hash: string): Promise { const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages where hash = $1 order by dispatched_at desc;`; const result = await this.pool.query(query, [hash.toLowerCase()]); const entry = result.rows[0]; - return NomadMessage.fromDB(entry.origin, + return NomadMessage.fromDB( + entry.origin, entry.destination, entry.nonce, entry.root, @@ -105,8 +118,8 @@ export class DB { entry.received_at, entry.processed_at, entry.sender, - entry.evm, - ) + entry.evm + ); } async getMessages(req: MsgRequest): Promise { @@ -115,19 +128,31 @@ export class DB { const offset = (page || -1) * limit; const args: any[] = [limit, offset]; - const c = new Contr(args.length) - - if (req.sender) {c.add('sender'); args.push(req.sender)}; - if (req.recipient) {c.add('recipient'); args.push(req.recipient)}; - if (req.origin) {c.add('origin'); args.push(req.origin)}; - if (req.destination) {c.add('destination'); args.push(req.destination)}; + const c = new Contr(args.length); + + if (req.sender) { + c.add("sender"); + args.push(req.sender.toLowerCase()); + } + if (req.recipient) { + c.add("recipient"); + args.push(req.recipient.toLowerCase()); + } + if (req.origin) { + c.add("origin"); + args.push(req.origin); + } + if (req.destination) { + c.add("destination"); + args.push(req.destination); + } const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, hash, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages ${c.construct()} order by dispatched_at desc limit $1 offset $2;`; - console.log(`Query`, query) const result = await this.pool.query(query, args); - return result.rows.map(entry => { - return NomadMessage.fromDB(entry.origin, + return result.rows.map((entry) => { + return NomadMessage.fromDB( + entry.origin, entry.destination, entry.nonce, entry.root, @@ -141,9 +166,9 @@ export class DB { entry.received_at, entry.processed_at, entry.sender, - entry.evm, - ) - }) + entry.evm + ); + }); } async insertMessage(messages: NomadMessage[]) { @@ -152,7 +177,7 @@ export class DB { const columns = 25; const query = `INSERT INTO messages (hash, origin, destination, nonce, nomad_sender, nomad_recipient, root, state, dispatched_at, updated_at, relayed_at, received_at, processed_at, bridge_msg_type, recipient, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id, sender, raw, leaf_index, block, evm) VALUES ${expand( rows, - columns, + columns )};`; const values = messages.map((m) => m.intoDB()).flat(); return await this.pool.query(query, values); @@ -203,7 +228,7 @@ export class DB { async getAllKeyPair(namespace: string): Promise> { const res = await this.pool.query( `select key, value from kv_storage where namespace = $1;`, - [namespace], + [namespace] ); return new Map(res.rows.map((r) => [r.key, r.value])); } @@ -215,7 +240,7 @@ export class DB { ON CONFLICT (namespace, key) DO UPDATE SET value = $3;`, - [namespace, k, v], + [namespace, k, v] ); } } diff --git a/typescript/nomad-monitor/src/indexer/core/event.ts b/tools/indexer/core/event.ts similarity index 79% rename from typescript/nomad-monitor/src/indexer/core/event.ts rename to tools/indexer/core/event.ts index 6726e7693..d982f5347 100644 --- a/typescript/nomad-monitor/src/indexer/core/event.ts +++ b/tools/indexer/core/event.ts @@ -1,23 +1,23 @@ -import { ethers } from 'ethers'; +import { ethers } from "ethers"; export enum ContractType { - Home = 'home', - Replica = 'replica', - BridgeRouter = 'bridgeRouter', + Home = "home", + Replica = "replica", + BridgeRouter = "bridgeRouter", } export enum EventType { - HomeDispatch = 'homeDispatch', - HomeUpdate = 'homeUpdate', - ReplicaUpdate = 'replicaUpdate', - ReplicaProcess = 'replicaProcess', - BridgeRouterSend = 'bridgeRouterSend', - BridgeRouterReceive = 'bridgeRouterReceive', + HomeDispatch = "homeDispatch", + HomeUpdate = "homeUpdate", + ReplicaUpdate = "replicaUpdate", + ReplicaProcess = "replicaProcess", + BridgeRouterSend = "bridgeRouterSend", + BridgeRouterReceive = "bridgeRouterReceive", } export enum EventSource { - Fetch = 'fetch', - Storage = 'storage', + Fetch = "fetch", + Storage = "storage", } export type EventData = { @@ -42,7 +42,7 @@ export type EventData = { originAndNonce?: ethers.BigNumber; recipient?: string; liquidityProvider?: string; - evmHash?: string; + evmHash?: string; }; export class NomadEvent { @@ -63,14 +63,15 @@ export class NomadEvent { ts: number, eventData: EventData, block: number, - source: EventSource, + source: EventSource ) { this.domain = domain; this.eventType = eventType; this.contractType = contractType; this.replicaOrigin = replicaOrigin; this.ts = - /*source === EventSource.Fetch && */ (contractType == ContractType.Home || contractType == ContractType.BridgeRouter) + /*source === EventSource.Fetch && */ contractType == ContractType.Home || + contractType == ContractType.BridgeRouter ? ts - 45000 : ts; // if the event was fetched from RPC for past (we asked RPC when event happened) happened on another chain we want to make sure that event at chain of origin happened before it was relayed to destination this.eventData = eventData; @@ -81,11 +82,11 @@ export class NomadEvent { destinationAndNonce(): [number, number] { if (this.eventType !== EventType.HomeDispatch) { throw new Error( - `Destination method is not availiable for non home-dispatch`, + `Destination method is not availiable for non home-dispatch` ); } const [destination, nonce] = parseDestinationAndNonce( - this.eventData.destinationAndNonce!, + this.eventData.destinationAndNonce! ); return [destination, nonce]; } @@ -93,11 +94,11 @@ export class NomadEvent { originAndNonce(): [number, number] { if (this.eventType !== EventType.BridgeRouterReceive) { throw new Error( - `Destination method is not availiable for non BridgeRouterReceive`, + `Destination method is not availiable for non BridgeRouterReceive` ); } const [origin, nonce] = parseDestinationAndNonce( - this.eventData.originAndNonce!, + this.eventData.originAndNonce! ); return [origin, nonce]; } @@ -133,15 +134,15 @@ export class NomadEvent { e.ts, e.eventData, e.block, - EventSource.Storage, + EventSource.Storage ); } } function parseDestinationAndNonce( - h: ethers.BigNumber | { hex?: string; _hex?: string }, + h: ethers.BigNumber | { hex?: string; _hex?: string } ): [number, number] { - let hexString = ''; + let hexString = ""; if (h instanceof ethers.BigNumber) { hexString = h.toHexString(); } else { @@ -153,10 +154,10 @@ function parseDestinationAndNonce( const without0x = hexString.slice(2); const destinationLength = without0x.length - 8; const destinationHex = ethers.BigNumber.from( - '0x' + without0x.slice(0, destinationLength), + "0x" + without0x.slice(0, destinationLength) ); const nonceHex = ethers.BigNumber.from( - '0x' + without0x.slice(destinationLength), + "0x" + without0x.slice(destinationLength) ); return [destinationHex.toNumber(), nonceHex.toNumber()]; } diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts new file mode 100644 index 000000000..ffacebdaa --- /dev/null +++ b/tools/indexer/core/index.ts @@ -0,0 +1,38 @@ +import { mainnet } from "@nomad-xyz/sdk/dist"; +import { Processor } from "./consumer"; +import { Orchestrator } from "./orchestrator"; +import * as dotenv from "dotenv"; +import { ethers } from "ethers"; +import { IndexerCollector } from "./metrics"; +import { DB } from "./db"; +import Logger from "bunyan"; +dotenv.config({}); + +const infuraKey = process.env.INFURA_KEY!; +const moonbeamRPC = process.env.MOONBEAM_RPC!; + +export async function run(db: DB, environment: string, logger: Logger) { + const ctx = mainnet; + + const ethereumId = ctx.mustGetDomain("ethereum").id; + const infura = new ethers.providers.InfuraProvider("homestead", infuraKey); + ctx.registerProvider(ethereumId, infura); + + const moonbeamId = ctx.mustGetDomain("moonbeam").id; + ctx.registerRpcProvider(moonbeamId, moonbeamRPC); + + const c = new Processor(db, logger); + const m = new IndexerCollector(environment, logger); + + const o = new Orchestrator( + mainnet, + c, + mainnet.domainNumbers[0], + m, + logger, + db + ); + await o.init(); + + await o.startConsuming(); +} diff --git a/typescript/nomad-monitor/src/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts similarity index 81% rename from typescript/nomad-monitor/src/indexer/core/indexer.ts rename to tools/indexer/core/indexer.ts index b1f96a6a4..b232952c9 100644 --- a/typescript/nomad-monitor/src/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -1,11 +1,11 @@ -import { Orchestrator } from './orchestrator'; -import { NomadContext } from '@nomad-xyz/sdk/src'; -import fs from 'fs'; -import { ContractType, EventType, NomadEvent, EventSource } from './event'; -import { Home, Replica } from '@nomad-xyz/contract-interfaces/core'; -import { ethers } from 'ethers'; -import { KVCache, replacer, retry, reviver } from './utils'; -import { BridgeRouter } from '@nomad-xyz/contract-interfaces/bridge'; +import { Orchestrator } from "./orchestrator"; +import { NomadContext } from "@nomad-xyz/sdk/dist"; +import fs from "fs"; +import { ContractType, EventType, NomadEvent, EventSource } from "./event"; +import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; +import { ethers } from "ethers"; +import { KVCache, replacer, retry, reviver } from "./utils"; +import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; export class Indexer { domain: number; @@ -20,44 +20,51 @@ export class Indexer { this.sdk = sdk; this.orchestrator = orchestrator; this.persistance = new RamPersistance( - `/tmp/persistance_${this.domain}.json`, + `/tmp/persistance_${this.domain}.json` ); - this.blockCache = new KVCache( - String(this.domain), - this.orchestrator.db, - ); - + this.blockCache = new KVCache(String(this.domain), this.orchestrator.db); } get provider(): ethers.providers.Provider { return this.sdk.getProvider(this.domain)!; } - async getBlockInfo(blockNumber: number): Promise<[number, Map]> { + async getBlockInfo( + blockNumber: number + ): Promise<[number, Map]> { const possibleBlock = this.blockCache.get(String(blockNumber)); if (possibleBlock) { - const [ts, txs] = possibleBlock.split('.'); - const x: string[] = txs.split(','); - const senders2hashes: Map = new Map(x.map(tx => tx.split(':') as [string, string])); + const [ts, txs] = possibleBlock.split("."); + const x: string[] = txs.split(","); + const senders2hashes: Map = new Map( + x.map((tx) => tx.split(":") as [string, string]) + ); return [parseInt(ts), senders2hashes]; } const [block, error] = await retry( async () => await this.provider.getBlockWithTransactions(blockNumber), - 6, + 6 ); if (!block) { throw ( error || new Error( - `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}`, + `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}` ) ); } const time = block.timestamp * 1000; - const senders2hashes: Map = new Map(block.transactions.map(tx => [tx.from, tx.hash])); - const senders2hashesStr = Array.from(senders2hashes.entries()).map(([from, hash]) => `${from}:${hash}`).join(',') - await this.blockCache.set(String(blockNumber), `${time}.${senders2hashesStr}`); + const senders2hashes: Map = new Map( + block.transactions.map((tx) => [tx.from, tx.hash]) + ); + const senders2hashesStr = Array.from(senders2hashes.entries()) + .map(([from, hash]) => `${from}:${hash}`) + .join(","); + await this.blockCache.set( + String(blockNumber), + `${time}.${senders2hashesStr}` + ); // await this.block2timeCache.set(String(blockNumber), String(block.transactions.map(tx => tx.from).join(','))); return [time, senders2hashes]; } @@ -90,12 +97,12 @@ export class Indexer { async updateAll(replicas: number[]) { let from = Math.max( this.persistance.height + 1, - this.sdk.getDomain(this.domain)?.paginate?.from || 0, + this.sdk.getDomain(this.domain)?.paginate?.from || 0 ); const to = await this.provider.getBlockNumber(); this.orchestrator.logger.info( - `Fetching events for domain ${this.domain} from: ${from}, to: ${to}`, + `Fetching events for domain ${this.domain} from: ${from}, to: ${to}` ); const fetchEvents = async (from: number, to: number) => { @@ -128,7 +135,7 @@ export class Indexer { batchTo = Math.min(to, batchFrom + batchSize); } - if (!allEvents) throw new Error('kek'); + if (!allEvents) throw new Error("kek"); allEvents.sort((a, b) => a.ts - b.ts); this.persistance.store(...allEvents); @@ -144,9 +151,9 @@ export class Indexer { const h = new Map(); const r = new Map(); - let h1 = ''; + let h1 = ""; let ht = Number.MAX_VALUE; - let r1 = ''; + let r1 = ""; let rt = Number.MAX_VALUE; let rtotal = 0; let htotal = 0; @@ -213,32 +220,31 @@ export class Indexer { { const events = await br.queryFilter(br.filters.Send(), from, to); const parsedEvents = await Promise.all( - events.map( - async (event) => + events.map(async (event) => { + const [ts, senders2hashes] = await this.getBlockInfo( + event.blockNumber + ); + return new NomadEvent( + this.domain, + EventType.BridgeRouterSend, + ContractType.BridgeRouter, + 0, + ts, { - const [ts, senders2hashes] = await this.getBlockInfo(event.blockNumber); - return new NomadEvent( - this.domain, - EventType.BridgeRouterSend, - ContractType.BridgeRouter, - 0, - ts, - { - token: event.args[0], - from: event.args[1], - toDomain: event.args[2], - toId: event.args[3], - amount: event.args[4], - fastLiquidityEnabled: event.args[5], - evmHash: senders2hashes.get(event.args[1])!, - }, - event.blockNumber, - EventSource.Fetch, - ) - } - ), + token: event.args[0], + from: event.args[1], + toDomain: event.args[2], + toId: event.args[3], + amount: event.args[4], + fastLiquidityEnabled: event.args[5], + evmHash: senders2hashes.get(event.args[1])!, + }, + event.blockNumber, + EventSource.Fetch + ); + }) ); - allEvents.push(...parsedEvents) + allEvents.push(...parsedEvents); } { @@ -251,7 +257,9 @@ export class Indexer { EventType.BridgeRouterReceive, ContractType.BridgeRouter, 0, - (await this.getBlockInfo(event.blockNumber))[0], + ( + await this.getBlockInfo(event.blockNumber) + )[0], { originAndNonce: event.args[0], token: event.args[1], @@ -260,15 +268,14 @@ export class Indexer { amount: event.args[4], }, event.blockNumber, - EventSource.Fetch, - ), - ), + EventSource.Fetch + ) + ) ); - allEvents.push(...parsedEvents) + allEvents.push(...parsedEvents); } - return allEvents - + return allEvents; } async fetchHome(from: number, to: number) { @@ -285,7 +292,9 @@ export class Indexer { EventType.HomeDispatch, ContractType.Home, 0, - (await this.getBlockInfo(event.blockNumber))[0], + ( + await this.getBlockInfo(event.blockNumber) + )[0], { messageHash: event.args[0], leafIndex: event.args[1], @@ -294,9 +303,9 @@ export class Indexer { message: event.args[4], }, event.blockNumber, - EventSource.Fetch, - ), - ), + EventSource.Fetch + ) + ) ); fetchedEvents.push(...parsedEvents); } @@ -311,7 +320,9 @@ export class Indexer { EventType.HomeUpdate, ContractType.Home, 0, - (await this.getBlockInfo(event.blockNumber))[0], + ( + await this.getBlockInfo(event.blockNumber) + )[0], { homeDomain: event.args[0], oldRoot: event.args[1], @@ -319,9 +330,9 @@ export class Indexer { signature: event.args[3], }, event.blockNumber, - EventSource.Fetch, - ), - ), + EventSource.Fetch + ) + ) ); fetchedEvents.push(...parsedEvents); } @@ -337,7 +348,7 @@ export class Indexer { const events = await replica.queryFilter( replica.filters.Update(), from, - to, + to ); const parsedEvents = await Promise.all( events.map( @@ -347,7 +358,9 @@ export class Indexer { EventType.ReplicaUpdate, ContractType.Replica, domain, - (await this.getBlockInfo(event.blockNumber))[0], + ( + await this.getBlockInfo(event.blockNumber) + )[0], { homeDomain: event.args[0], oldRoot: event.args[1], @@ -355,9 +368,9 @@ export class Indexer { signature: event.args[3], }, event.blockNumber, - EventSource.Fetch, - ), - ), + EventSource.Fetch + ) + ) ); fetchedEvents.push(...parsedEvents); } @@ -366,7 +379,7 @@ export class Indexer { const events = await replica.queryFilter( replica.filters.Process(), from, - to, + to ); const parsedEvents = await Promise.all( events.map( @@ -376,16 +389,18 @@ export class Indexer { EventType.ReplicaProcess, ContractType.Replica, domain, - (await this.getBlockInfo(event.blockNumber))[0], + ( + await this.getBlockInfo(event.blockNumber) + )[0], { messageHash: event.args[0], success: event.args[1], returnData: event.args[2], }, event.blockNumber, - EventSource.Fetch, - ), - ), + EventSource.Fetch + ) + ) ); fetchedEvents.push(...parsedEvents); } @@ -469,8 +484,8 @@ export class RamPersistance extends Persistance { height: this.height, storePath: this.storePath, }, - replacer, - ), + replacer + ) ); } @@ -487,8 +502,8 @@ export class RamPersistance extends Persistance { loadFromFile() { const object = JSON.parse( - fs.readFileSync(this.storePath, 'utf8'), - reviver, + fs.readFileSync(this.storePath, "utf8"), + reviver ) as { block2events: Map; blocks: number[]; diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts new file mode 100644 index 000000000..db36e4d65 --- /dev/null +++ b/tools/indexer/core/metrics.ts @@ -0,0 +1,162 @@ +import { Gauge } from "prom-client"; +import Logger from "bunyan"; + +import { register } from "prom-client"; +import express, { Response } from "express"; + +export class MetricsCollector { + readonly environment: string; + private readonly logger: Logger; + + constructor(environment: string, logger: Logger) { + this.environment = environment; + this.logger = logger; + } + + /** + * Starts a server that exposes metrics in the prometheus format + */ + startServer(port: number) { + if (!Number.isInteger(port) || port <= 0 || port > 65535) { + throw Error(`Invalid PrometheusPort value: ${port}`); + } + const server = express(); + server.get("/metrics", async (_, res: Response) => { + res.set("Content-Type", register.contentType); + res.end(await register.metrics()); + }); + + this.logger.info( + { + endpoint: `http://0.0.0.0:${port}/metrics`, + }, + "Prometheus metrics exposed" + ); + server.listen(port); + } +} + +const prefix = `fancy_monitor`; + +export class IndexerCollector extends MetricsCollector { + private numDispatchedGauge: Gauge; + private numUpdatedGauge: Gauge; + private numRelayedGauge: Gauge; + private numProcessedGauge: Gauge; + + private meanUpdateTimeGauge: Gauge; + private meanRelayTimeGauge: Gauge; + private meanProcessTimeGauge: Gauge; + private meanEndToEndTimeGauge: Gauge; + + constructor(environment: string, logger: Logger) { + super(environment, logger); + + // Count + + this.numDispatchedGauge = new Gauge({ + name: prefix + "_number_messages_dispatched", + help: "Gauge that indicates how many messages have been dispatched for a network.", + labelNames: ["network", "environment"], + }); + + this.numUpdatedGauge = new Gauge({ + name: prefix + "_number_messages_updated", + help: "Gauge that indicates how many messages have been updated for a network.", + labelNames: ["network", "environment"], + }); + + this.numRelayedGauge = new Gauge({ + name: prefix + "_number_messages_relayed", + help: "Gauge that indicates how many messages have been relayed for a network.", + labelNames: ["network", "environment"], + }); + + this.numProcessedGauge = new Gauge({ + name: prefix + "_number_messages_processed", + help: "Gauge that indicates how many messages have been processed for a network.", + labelNames: ["network", "environment"], + }); + + // Time + + this.meanUpdateTimeGauge = new Gauge({ + name: prefix + "_mean_update_time", + help: "Gauge that indicates how long does it take to move from dispatched to updated.", + labelNames: ["network", "environment"], + }); + + this.meanRelayTimeGauge = new Gauge({ + name: prefix + "_mean_relay_time", + help: "Gauge that indicates how long does it take to move from updated to relayed.", + labelNames: ["network", "environment"], + }); + + this.meanProcessTimeGauge = new Gauge({ + name: prefix + "_mean_process_time", + help: "Gauge that indicates how long does it take to move from relayed to processed.", + labelNames: ["network", "environment"], + }); + + this.meanEndToEndTimeGauge = new Gauge({ + name: prefix + "_mean_e2e_time", + help: "Gauge that indicates how long does it take to move from dispatched to processed.", + labelNames: ["network", "environment"], + }); + } + + /** + * Sets the state for a bridge. + */ + setNetworkState( + network: string, + dispatched: number, + updated: number, + relayed: number, + processed: number, + updateTime: number, + relayTime: number, + processTime: number, + e2eTime: number + ) { + this.numDispatchedGauge.set( + { network, environment: this.environment }, + dispatched + ); + + this.numUpdatedGauge.set( + { network, environment: this.environment }, + updated + ); + + this.numRelayedGauge.set( + { network, environment: this.environment }, + relayed + ); + + this.numProcessedGauge.set( + { network, environment: this.environment }, + processed + ); + + this.meanUpdateTimeGauge.set( + { network, environment: this.environment }, + updateTime + ); + + this.meanRelayTimeGauge.set( + { network, environment: this.environment }, + relayTime + ); + + this.meanProcessTimeGauge.set( + { network, environment: this.environment }, + processTime + ); + + this.meanEndToEndTimeGauge.set( + { network, environment: this.environment }, + e2eTime + ); + } +} diff --git a/typescript/nomad-monitor/src/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts similarity index 88% rename from typescript/nomad-monitor/src/indexer/core/orchestrator.ts rename to tools/indexer/core/orchestrator.ts index 486ed5770..1387d7d44 100644 --- a/typescript/nomad-monitor/src/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -1,11 +1,11 @@ -import { NomadContext } from '@nomad-xyz/sdk/src'; -import Logger from 'bunyan'; -import { Consumer } from './consumer'; -import { DB } from './db'; -import { Indexer } from './indexer'; -import { IndexerCollector } from './metrics'; -import { Statistics } from './types'; -import { replacer, sleep } from './utils'; +import { NomadContext } from "@nomad-xyz/sdk/dist"; +import Logger from "bunyan"; +import { Consumer } from "./consumer"; +import { DB } from "./db"; +import { Indexer } from "./indexer"; +import { IndexerCollector } from "./metrics"; +import { Statistics } from "./types"; +import { replacer, sleep } from "./utils"; export class Orchestrator { sdk: NomadContext; @@ -24,7 +24,7 @@ export class Orchestrator { gov: number, metrics: IndexerCollector, logger: Logger, - db: DB, + db: DB ) { this.sdk = sdk; this.consumer = c; @@ -45,7 +45,7 @@ export class Orchestrator { async indexAll() { const events = ( await Promise.all( - this.sdk.domainNumbers.map((domain: number) => this.index(domain)), + this.sdk.domainNumbers.map((domain: number) => this.index(domain)) ) ).flat(); events.sort((a, b) => a.ts - b.ts); @@ -90,7 +90,7 @@ export class Orchestrator { this.logger.info( `Finished reindexing after ${ (new Date().valueOf() - start) / 1000 - } seconds`, + } seconds` ); const stats = this.consumer.stats(); @@ -122,7 +122,7 @@ export class Orchestrator { meanUpdate, meanRelay, meanProcess, - meanE2E, + meanE2E ); } diff --git a/typescript/nomad-monitor/src/indexer/core/types.ts b/tools/indexer/core/types.ts similarity index 95% rename from typescript/nomad-monitor/src/indexer/core/types.ts rename to tools/indexer/core/types.ts index 88b88208b..a1f62c0aa 100644 --- a/typescript/nomad-monitor/src/indexer/core/types.ts +++ b/tools/indexer/core/types.ts @@ -76,7 +76,7 @@ export class RootTimingStatistic { return { total: this.total.toObject(), domainStatistics: Array.from(this.domainStatistics.entries()).map( - ([d, v]) => [d, v.toObject()], + ([d, v]) => [d, v.toObject()] ), }; } @@ -88,7 +88,7 @@ export class RootCountStagesStatistic { constructor(domains: number[]) { this.total = new BasicCountStages(); this.domainStatistics = new Map( - domains.map((d) => [d, new BasicCountStages()]), + domains.map((d) => [d, new BasicCountStages()]) ); } @@ -96,7 +96,7 @@ export class RootCountStagesStatistic { return { total: this.total.toObject(), domainStatistics: Array.from(this.domainStatistics.entries()).map( - ([d, v]) => [d, v.toObject()], + ([d, v]) => [d, v.toObject()] ), }; } diff --git a/typescript/nomad-monitor/src/indexer/core/utils.ts b/tools/indexer/core/utils.ts similarity index 68% rename from typescript/nomad-monitor/src/indexer/core/utils.ts rename to tools/indexer/core/utils.ts index cfc8b6179..cb69e6da4 100644 --- a/typescript/nomad-monitor/src/indexer/core/utils.ts +++ b/tools/indexer/core/utils.ts @@ -1,8 +1,9 @@ -import { ethers } from 'ethers'; -import { NomadEvent } from './event'; -import fs from 'fs'; -import { Mean } from './types'; -import { DB } from './db'; +import { ethers } from "ethers"; +import { NomadEvent } from "./event"; +import fs from "fs"; +import { Mean } from "./types"; +import { DB } from "./db"; +import Logger from "bunyan"; export function sleep(ms: number) { return new Promise((resolve) => { @@ -12,7 +13,7 @@ export function sleep(ms: number) { export async function retry( callback: () => Promise, - tries: number, + tries: number ): Promise<[T | undefined, any]> { let timeout = 5000; let lastError: any = undefined; @@ -30,17 +31,17 @@ export async function retry( export function replacer(key: any, value: any): any { if (value instanceof Map) { return { - dataType: 'Map', + dataType: "Map", value: Array.from(value.entries()), // or with spread: value: [...value] }; } else if (value instanceof NomadEvent) { return { - dataType: 'NomadEvent', + dataType: "NomadEvent", value: value.toObject(), // or with spread: value: [...value] }; } else if (value instanceof ethers.BigNumber) { return { - dataType: 'BigNumber', + dataType: "BigNumber", value: value.toHexString(), // or with spread: value: [...value] }; } else if (value instanceof Mean) { @@ -51,14 +52,14 @@ export function replacer(key: any, value: any): any { } export function reviver(key: any, value: any): any { - if (typeof value === 'object' && value !== null) { - if (value.dataType === 'Map') { + if (typeof value === "object" && value !== null) { + if (value.dataType === "Map") { return new Map(value.value); - } else if (value.dataType === 'NomadEvent') { + } else if (value.dataType === "NomadEvent") { return NomadEvent.fromObject(value.value); - } else if (value.dataType === 'BigNumber') { + } else if (value.dataType === "BigNumber") { return ethers.BigNumber.from(value.value); - } else if (value.type === 'BigNumber') { + } else if (value.type === "BigNumber") { return ethers.BigNumber.from(value.hex); } } @@ -97,14 +98,22 @@ export class KVCache { } export function logToFile(s: string) { - fs.appendFileSync('/tmp/log.log', s + '\n'); + fs.appendFileSync("/tmp/log.log", s + "\n"); } -import crypto from 'crypto'; +import crypto from "crypto"; export function hash(...vals: string[]): string { - const hash = crypto.createHash('md5'); - vals.forEach(v => hash.update(v)) - return hash.digest('hex') - -} \ No newline at end of file + const hash = crypto.createHash("md5"); + vals.forEach((v) => hash.update(v)); + return hash.digest("hex"); +} + +export function createLogger(name: string, environment: string) { + return Logger.createLogger({ + name, + serializers: Logger.stdSerializers, + level: "debug", + environment: environment, + }); +} diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts new file mode 100644 index 000000000..417870614 --- /dev/null +++ b/tools/indexer/main.ts @@ -0,0 +1,20 @@ +import * as core from "./core"; +import * as api from "./api"; +import { DB } from "./core/db"; +import { createLogger } from "./core/utils"; + +const environment = process.env.ENVIRONMENT!; +const program = process.env.PROGRAM!; + +(async () => { + const db = new DB(); + await db.connect(); + + const logger = createLogger("indexer", environment); + + if (program === "api") { + await api.run(db, logger); + } else if (program === "core") { + await core.run(db, environment, logger); + } +})(); diff --git a/tools/indexer/migrations/1642713407510_my-first-migration.js b/tools/indexer/migrations/1642713407510_my-first-migration.js new file mode 100644 index 000000000..112dea592 --- /dev/null +++ b/tools/indexer/migrations/1642713407510_my-first-migration.js @@ -0,0 +1,63 @@ +/* eslint-disable camelcase */ + +exports.shorthands = undefined; + +exports.up = (pgm) => { + pgm.createTable("messages", { + id: "id", + hash: { type: "varchar(66)", notNull: true, unique: true }, // Nomad TX id + origin: { type: "integer", notNull: true }, + destination: { type: "integer", notNull: true }, + nonce: { type: "integer", notNull: true }, + nomad_sender: { type: "varchar(42)", notNull: true }, // Nomad sender (bridge router) + nomad_recipient: { type: "varchar(42)", notNull: true }, // Nomad recipient (bridge router) + root: { type: "varchar(66)", notNull: true }, + state: { type: "integer", notNull: true }, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) + block: { type: "integer", notNull: true }, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) + dispatched_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state dispatched + updated_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state updated + relayed_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state relayed + received_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state received + processed_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state processed + // Bridge message internals + sender: { type: "varchar(42)", notNull: false }, // sender + bridge_msg_type: { type: "varchar(14)", notNull: false }, // Mostly 'transaction' + recipient: { type: "varchar(42)", notNull: false }, // Real recipient on destination domain + bridge_msg_amount: { type: "varchar(256)", notNull: false }, // Amount of Token + bridge_msg_allow_fast: { type: "boolean", notNull: false }, // Allow fast? + bridge_msg_details_hash: { type: "varchar(66)", notNull: false }, // Details hash - don't know what it is (need to do homework) + bridge_msg_token_domain: { type: "integer", notNull: false }, // Token domain + bridge_msg_token_id: { type: "varchar(42)", notNull: false }, // Token id (address) + raw: { type: "varchar", notNull: true }, + leaf_index: { type: "varchar(256)", notNull: true }, + evm: { type: "varchar(66)", notNull: false }, + createdAt: { + type: "timestamp", + notNull: true, + default: pgm.func("current_timestamp"), + }, + }); + pgm.createIndex("messages", "id"); + + pgm.createTable("kv_storage", { + id: "id", + namespace: { type: "varchar", notNull: true }, + key: { type: "varchar", notNull: true }, + value: { type: "varchar", notNull: true }, + createdAt: { + type: "timestamp", + notNull: true, + default: pgm.func("current_timestamp"), + }, + }); + pgm.createIndex("kv_storage", "id"); + pgm.addConstraint("kv_storage", "unique_namespace_key", { + unique: ["namespace", "key"], + }); +}; + +exports.down = (pgm) => { + pgm.dropTable("messages"); + pgm.dropConstraint("kv_storage", "unique_namespace_key"); + pgm.dropTable("kv_storage"); +}; diff --git a/tools/indexer/package-lock.json b/tools/indexer/package-lock.json new file mode 100644 index 000000000..ec3c3478d --- /dev/null +++ b/tools/indexer/package-lock.json @@ -0,0 +1,9575 @@ +{ + "name": "indexer", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "indexer", + "version": "0.1.0", + "license": "Apache 2.0", + "dependencies": { + "@nomad-xyz/contract-interfaces": "1.1.1", + "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.2.4.tgz", + "@types/bunyan": "^1.8.7", + "@types/express": "^4.17.13", + "@types/node-pg-migrate": "^2.3.1", + "@types/pg": "^8.6.4", + "bunyan": "^1.8.15", + "dotenv": "^10.0.0", + "ethers": "^5.4.6", + "express": "^4.17.2", + "got": "^11.8.2", + "moment": "^2.29.1", + "node-pg-migrate": "^6.2.1", + "pg": "^8.7.1", + "prom-client": "^14.0.0", + "request": "^2.88.2", + "ts-node": "^10.4.0", + "typescript": "^4.4.3" + }, + "devDependencies": { + "prettier": "^2.4.1" + } + }, + "../../typescript/nomad-sdk": { + "name": "@nomad-xyz/sdk", + "version": "1.2.4", + "extraneous": true, + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@gnosis.pm/safe-core-sdk": "^1.3.0", + "@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.7", + "@nomad-xyz/contract-interfaces": "1.1.1", + "ethers": "^5.4.6", + "web3": "^1.6.1" + }, + "devDependencies": { + "@types/node": "^16.9.1", + "@typescript-eslint/eslint-plugin": "^4.33.0", + "@typescript-eslint/parser": "^4.33.0", + "dotenv": "^10.0.0", + "eslint": "^7.32.0", + "eslint-config-prettier": "^8.3.0", + "fs": "0.0.1-security", + "prettier": "^2.4.1", + "typescript": "^4.4.3" + } + }, + "../nomad-sdk": { + "extraneous": true + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ethereumjs/common": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.0.tgz", + "integrity": "sha512-Cq2qS0FTu6O2VU1sgg+WyU9Ps0M6j/BEMHN+hRaECXCV/r0aI78u4N6p52QW/BDVhwWZpCdrvG8X7NJdzlpNUA==", + "dependencies": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.3" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.4.0.tgz", + "integrity": "sha512-WWUwg1PdjHKZZxPPo274ZuPsJCWV3SqATrEKQP1n2DrVYVP1aZIYpo/mFaA0BDoE0tIQmBeimRCEA0Lgil+yYw==", + "dependencies": { + "@ethereumjs/common": "^2.6.0", + "ethereumjs-util": "^7.1.3" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", + "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", + "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.5.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", + "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "^5.5.0", + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0" + } + }, + "node_modules/@ethersproject/experimental": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/experimental/-/experimental-5.5.0.tgz", + "integrity": "sha512-Q62IjbhlgVmeFHRI6JSU/mZbtVGd8mNgkuIU0IxyTVszUzNblocVctIngAiWm8gGqr/ytj7hN1FRadCRMY4zIA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/web": "^5.5.0", + "ethers": "^5.5.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", + "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", + "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.5.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", + "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/sha2": "^5.5.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.5.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.2.tgz", + "integrity": "sha512-hkbx7x/MKcRjyrO4StKXCzCpWer6s97xnm34xkfPiarhtEUVAN4TBBpamM+z66WcTt7H5B53YwbRj1n7i8pZoQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/random": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.1.tgz", + "integrity": "sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", + "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", + "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", + "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", + "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/json-wallets": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", + "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "node_modules/@gnosis.pm/safe-core-sdk": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-core-sdk/-/safe-core-sdk-1.3.0.tgz", + "integrity": "sha512-laKkyJUv0llPPG5ep2+18v/anIEGi+KjarNoeVAutYzIeAAkSvvVbY8qyfczh5bWQkqvQ3K1l/QLUJ8Kx4hm3g==", + "dependencies": { + "@gnosis.pm/safe-core-sdk-types": "^0.1.1", + "@gnosis.pm/safe-deployments": "^1.7.0", + "ethereumjs-util": "^7.1.3", + "semver": "^7.3.5" + } + }, + "node_modules/@gnosis.pm/safe-core-sdk-types": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-core-sdk-types/-/safe-core-sdk-types-0.1.1.tgz", + "integrity": "sha512-PghXGDaI5Foq37nZGmI90U2OKMeGtxh5KqkDqou9aFHwGVa/nf9HRQPxG9/XUzcyfe9OlKttDlJnR3XnC3dSDw==" + }, + "node_modules/@gnosis.pm/safe-deployments": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-deployments/-/safe-deployments-1.8.0.tgz", + "integrity": "sha512-xK2ZZXxCEGOw+6UZAeUmvqE/4C/XTpYmv1a8KzKUgSOxcGkHsIDqcjdKjqif7gOdnwHl4+XXJUtDQEuSLT4Scg==" + }, + "node_modules/@gnosis.pm/safe-ethers-adapters": { + "version": "0.1.0-alpha.7", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-ethers-adapters/-/safe-ethers-adapters-0.1.0-alpha.7.tgz", + "integrity": "sha512-NtxP71cplKB7AvILF4siC/eICcIc+qCzxzrQe8lqL7mwrnWgJ8VxLcVRO6944A+mLT7xG/fPpupF9rcuNyphkw==", + "dependencies": { + "@gnosis.pm/safe-core-sdk": "^1.0.0", + "@gnosis.pm/safe-core-sdk-types": "^0.1.1", + "@gnosis.pm/safe-deployments": "^1.0.0", + "axios": "^0.21.1" + }, + "peerDependencies": { + "@ethersproject/abstract-provider": "^5.0.0", + "@ethersproject/abstract-signer": "^5.0.0", + "@ethersproject/bignumber": "^5.0.0", + "@ethersproject/properties": "^5.0.0" + } + }, + "node_modules/@nomad-xyz/contract-interfaces": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@nomad-xyz/contract-interfaces/-/contract-interfaces-1.1.1.tgz", + "integrity": "sha512-bF0NYdck/LItN9s9D5ZQDzquiXv6sYCCi78lyHMiUNYaCGG3KLKeI5LkhW4fUlhgsNWBcQ9cdHt71S58m8KQrA==", + "dependencies": { + "@ethersproject/experimental": "^5.3.0", + "@types/node": "^16.10.2", + "typescript": "^4.4.3" + } + }, + "node_modules/@nomad-xyz/contract-interfaces/node_modules/@types/node": { + "version": "16.11.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.21.tgz", + "integrity": "sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A==" + }, + "node_modules/@nomad-xyz/sdk": { + "version": "1.2.4", + "resolved": "file:nomad-xyz-sdk-1.2.4.tgz", + "integrity": "sha512-sE4PqrAIFpQq0OOhnwyuD1soVHKGVV9LyqWLbN2XbacwOTZhxS2iTd8SyQCPU1cb+6fQj5diFysEKjoaFrFzQA==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "@gnosis.pm/safe-core-sdk": "^1.3.0", + "@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.7", + "@nomad-xyz/contract-interfaces": "1.1.1", + "ethers": "^5.4.6", + "web3": "^1.6.1" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", + "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + }, + "node_modules/@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bunyan": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.8.tgz", + "integrity": "sha512-Cblq+Yydg3u+sGiz2mjHjC5MPmdjY+No4qvHrF+BUhblsmSfMvsHLbOG62tPbonsqBj6sbWv1LHcsoe5Jw+/Ow==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "node_modules/@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "node_modules/@types/node": { + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.12.tgz", + "integrity": "sha512-4YpbAsnJXWYK/fpTVFlMIcUIho2AYCi4wg5aNPrG1ng7fn/1/RZfCIpRCiBX+12RVa34RluilnvCqD+g3KiSiA==" + }, + "node_modules/@types/node-pg-migrate": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/node-pg-migrate/-/node-pg-migrate-2.3.1.tgz", + "integrity": "sha512-ccKzTzDu8b6IRttXh1Ch6JlEQvVM4jrRUU9xyEp+Pq5yd3vBCg84pJeAUzycyts5Tg+eyiLqmlIW3g/SrRlqEQ==", + "deprecated": "This is a stub types definition for node-pg-migrate (https://github.com/theoephraim/node-pg-migrate#readme). node-pg-migrate provides its own type definitions, so you don\\'t need @types/node-pg-migrate installed!" + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pg": { + "version": "8.6.4", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.4.tgz", + "integrity": "sha512-uYA7UMVzDFpJobCrqwW/iWkFmvizy6knIUgr0Quaw7K1Le3ZnF7hI3bKqFoxPZ+fju1Sc7zdTvOl9YfFZPcmeA==", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "optional": true + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "node_modules/bignumber.js": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", + "engines": { + "node": "*" + } + }, + "node_modules/bintrees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" + }, + "node_modules/blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "dependencies": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-rsa/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "node_modules/bufferutil": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", + "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "engines": [ + "node >=0.10.0" + ], + "bin": { + "bunyan": "bin/bunyan" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "node_modules/bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, + "node_modules/cids/node_modules/multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "dependencies": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crc-32": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.1.tgz", + "integrity": "sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w==", + "dependencies": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.3.1" + }, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dependencies": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "dependencies": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + } + }, + "node_modules/eth-ens-namehash/node_modules/js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "node_modules/eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "dependencies": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "node_modules/eth-lib/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/eth-lib/node_modules/ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dependencies": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "dependencies": { + "js-sha3": "^0.8.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz", + "integrity": "sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethereumjs-util/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/ethers": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.3.tgz", + "integrity": "sha512-fTT4WT8/hTe/BLwRUtl7I5zlpF3XC3P/Xwqxc5AIP2HGlH15qpmjs0Ou78az93b1rLITzXLFxoNX63B8ZbUd7g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.5.0", + "@ethersproject/abstract-provider": "5.5.1", + "@ethersproject/abstract-signer": "5.5.0", + "@ethersproject/address": "5.5.0", + "@ethersproject/base64": "5.5.0", + "@ethersproject/basex": "5.5.0", + "@ethersproject/bignumber": "5.5.0", + "@ethersproject/bytes": "5.5.0", + "@ethersproject/constants": "5.5.0", + "@ethersproject/contracts": "5.5.0", + "@ethersproject/hash": "5.5.0", + "@ethersproject/hdnode": "5.5.0", + "@ethersproject/json-wallets": "5.5.0", + "@ethersproject/keccak256": "5.5.0", + "@ethersproject/logger": "5.5.0", + "@ethersproject/networks": "5.5.2", + "@ethersproject/pbkdf2": "5.5.0", + "@ethersproject/properties": "5.5.0", + "@ethersproject/providers": "5.5.2", + "@ethersproject/random": "5.5.1", + "@ethersproject/rlp": "5.5.0", + "@ethersproject/sha2": "5.5.0", + "@ethersproject/signing-key": "5.5.0", + "@ethersproject/solidity": "5.5.0", + "@ethersproject/strings": "5.5.0", + "@ethersproject/transactions": "5.5.0", + "@ethersproject/units": "5.5.0", + "@ethersproject/wallet": "5.5.0", + "@ethersproject/web": "5.5.1", + "@ethersproject/wordlists": "5.5.0" + } + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "node_modules/eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/got": { + "version": "11.8.3", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", + "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "engines": { + "node": "*" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "dependencies": { + "has-symbol-support-x": "^1.4.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "dependencies": { + "punycode": "2.1.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/idna-uts46-hx/node_modules/punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", + "engines": { + "node": ">=6" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "node_modules/isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dependencies": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keyv": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "deprecated": "This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that.", + "dependencies": { + "mkdirp": "*" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mock-fs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", + "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "varint": "^5.0.0" + } + }, + "node_modules/multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dependencies": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + } + }, + "node_modules/multihashes/node_modules/multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "node_modules/nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-pg-migrate": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-6.2.1.tgz", + "integrity": "sha512-EsIOAWFBSBa/2g4BjA1tjRtPOEjOiZ/gqpjjxtsUyQRau0O2s1SVwyRW1HRA2qXZDMT/sA8GxcwLWqkK6BaGxA==", + "dependencies": { + "@types/pg": "^8.0.0", + "decamelize": "^5.0.0", + "mkdirp": "~1.0.0", + "yargs": "~17.3.0" + }, + "bin": { + "node-pg-migrate": "bin/node-pg-migrate" + }, + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "pg": ">=4.3.0 <9.0.0" + } + }, + "node_modules/node-pg-migrate/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oboe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", + "integrity": "sha1-VVQoTFQ6ImbXo48X4HOCH73jk80=", + "dependencies": { + "http-https": "^1.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-headers": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", + "integrity": "sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "node_modules/pg": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", + "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.4.1", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=2.0.0" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", + "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/printj": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz", + "integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==", + "bin": { + "printj": "bin/printj.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/prom-client": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.0.1.tgz", + "integrity": "sha512-HxTArb6fkOntQHoRGvv4qd/BkorjliiuO2uSWC2KC17MUTKYttWdDoXX/vxOhQdkoECEM9BBH0pj2l8G8kev6w==", + "dependencies": { + "tdigest": "^0.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "dependencies": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/rlp/node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "dependencies": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "dependencies": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/swarm-js": { + "version": "0.1.40", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz", + "integrity": "sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA==", + "dependencies": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request": "^1.0.1" + } + }, + "node_modules/swarm-js/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swarm-js/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "engines": { + "node": ">=4" + } + }, + "node_modules/swarm-js/node_modules/got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "dependencies": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swarm-js/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/swarm-js/node_modules/p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/tdigest": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", + "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", + "dependencies": { + "bintrees": "1.0.1" + } + }, + "node_modules/timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dependencies": { + "prepend-http": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "node_modules/url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "engines": { + "node": ">= 4" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.8.tgz", + "integrity": "sha512-k4dW/Qja1BYDl2qD4tOMB9PFVha/UJtxTc1cXYOe3WwA/2m0Yn4qB7wLMpJyLJ/7DR0XnTut3HsCSzDT4ZvKgA==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/web3": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.7.0.tgz", + "integrity": "sha512-n39O7QQNkpsjhiHMJ/6JY6TaLbdX+2FT5iGs8tb3HbIWOhPm4+a7UDbr5Lkm+gLa9aRKWesZs5D5hWyEvg4aJA==", + "hasInstallScript": true, + "dependencies": { + "web3-bzz": "1.7.0", + "web3-core": "1.7.0", + "web3-eth": "1.7.0", + "web3-eth-personal": "1.7.0", + "web3-net": "1.7.0", + "web3-shh": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-bzz": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.7.0.tgz", + "integrity": "sha512-XPhTWUnZa8gnARfiqaag3jJ9+6+a66Li8OikgBUJoMUqPuQTCJPncTbGYqOJIfRFGavEAdlMnfYXx9lvgv2ZPw==", + "hasInstallScript": true, + "dependencies": { + "@types/node": "^12.12.6", + "got": "9.6.0", + "swarm-js": "^0.1.40" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-bzz/node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/web3-bzz/node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/web3-bzz/node_modules/@types/node": { + "version": "12.20.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", + "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + }, + "node_modules/web3-bzz/node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/web3-bzz/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/web3-bzz/node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "node_modules/web3-bzz/node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/web3-bzz/node_modules/got/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/web3-bzz/node_modules/got/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/web3-bzz/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "node_modules/web3-bzz/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/web3-bzz/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/web3-bzz/node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/web3-bzz/node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "engines": { + "node": ">=4" + } + }, + "node_modules/web3-bzz/node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/web3-bzz/node_modules/responselike/node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/web3-bzz/node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/web3-core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.7.0.tgz", + "integrity": "sha512-U/CRL53h3T5KHl8L3njzCBT7fCaHkbE6BGJe3McazvFldRbfTDEHXkUJCyM30ZD0RoLi3aDfTVeFIusmEyCctA==", + "dependencies": { + "@types/bn.js": "^4.11.5", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-requestmanager": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-helpers": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.7.0.tgz", + "integrity": "sha512-kFiqsZFHJliKF8VKZNjt2JvKu3gu7h3N1/ke3EPhdp9Li/rLmiyzFVr6ApryZ1FSjbSx6vyOkibG3m6xQ5EHJA==", + "dependencies": { + "web3-eth-iban": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-method": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.7.0.tgz", + "integrity": "sha512-43Om+kZX8wU5u1pJ28TltF9e9pSTRph6b8wrOb6wgXAfPHqMulq6UTBJWjXXIRVN46Eiqv0nflw35hp9bbgnbA==", + "dependencies": { + "@ethersproject/transactions": "^5.0.0-beta.135", + "web3-core-helpers": "1.7.0", + "web3-core-promievent": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-promievent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.7.0.tgz", + "integrity": "sha512-xPH66XeC0K0k29GoRd0vyPQ07yxERPRd4yVPrbMzGAz/e9E4M3XN//XK6+PdfGvGw3fx8VojS+tNIMiw+PujbQ==", + "dependencies": { + "eventemitter3": "4.0.4" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-requestmanager": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.7.0.tgz", + "integrity": "sha512-rA3dBTBPrt+eIfTAQ2/oYNTN/2wbZaYNR3pFZGqG8+2oCK03+7oQyz4sWISKy/nYQhURh4GK01rs9sN4o/Tq9w==", + "dependencies": { + "util": "^0.12.0", + "web3-core-helpers": "1.7.0", + "web3-providers-http": "1.7.0", + "web3-providers-ipc": "1.7.0", + "web3-providers-ws": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-subscriptions": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.7.0.tgz", + "integrity": "sha512-6giF8pyJrPmWrRpc2WLoVCvQdMMADp20ZpAusEW72axauZCNlW1XfTjs0i4QHQBfdd2lFp65qad9IuATPhuzrQ==", + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/web3-core/node_modules/@types/node": { + "version": "12.20.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", + "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + }, + "node_modules/web3-eth": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.7.0.tgz", + "integrity": "sha512-3uYwjMjn/MZjKIzXCt4YL9ja/k9X5shfa4lKparZhZE6uesmu+xmSmrEFXA/e9qcveF50jkV7frjkT8H+cLYtw==", + "dependencies": { + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-eth-abi": "1.7.0", + "web3-eth-accounts": "1.7.0", + "web3-eth-contract": "1.7.0", + "web3-eth-ens": "1.7.0", + "web3-eth-iban": "1.7.0", + "web3-eth-personal": "1.7.0", + "web3-net": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-abi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.7.0.tgz", + "integrity": "sha512-heqR0bWxgCJwjWIhq2sGyNj9bwun5+Xox/LdZKe+WMyTSy0cXDXEAgv3XKNkXC4JqdDt/ZlbTEx4TWak4TRMSg==", + "dependencies": { + "@ethersproject/abi": "5.0.7", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-abi/node_modules/@ethersproject/abi": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.7.tgz", + "integrity": "sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==", + "dependencies": { + "@ethersproject/address": "^5.0.4", + "@ethersproject/bignumber": "^5.0.7", + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/constants": "^5.0.4", + "@ethersproject/hash": "^5.0.4", + "@ethersproject/keccak256": "^5.0.3", + "@ethersproject/logger": "^5.0.5", + "@ethersproject/properties": "^5.0.3", + "@ethersproject/strings": "^5.0.4" + } + }, + "node_modules/web3-eth-accounts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.7.0.tgz", + "integrity": "sha512-Zwm7TlQXdXGRuS6+ib1YsR5fQwpfnFyL6UAZg1zERdrUrs3IkCZSL3yCP/8ZYbAjdTEwWljoott2iSqXNH09ug==", + "dependencies": { + "@ethereumjs/common": "^2.5.0", + "@ethereumjs/tx": "^3.3.2", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "ethereumjs-util": "^7.0.10", + "scrypt-js": "^3.0.1", + "uuid": "3.3.2", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-accounts/node_modules/eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "dependencies": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "node_modules/web3-eth-accounts/node_modules/uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/web3-eth-contract": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.7.0.tgz", + "integrity": "sha512-2LY1Xwxu5rx468nqHuhvupQAIpytxIUj3mGL9uexszkhrQf05THVe3i4OnUCzkeN6B2cDztNOqLT3j9SSnVQDg==", + "dependencies": { + "@types/bn.js": "^4.11.5", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-promievent": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-eth-abi": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-contract/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/web3-eth-ens": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.7.0.tgz", + "integrity": "sha512-I1bikYJJWQ/FJZIAvwsGOvzAgcRIkosWG4s1L6veRoXaU8OEJFeh4s00KcfHDxg7GWZZGbUSbdbzKpwRbWnvkg==", + "dependencies": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-promievent": "1.7.0", + "web3-eth-abi": "1.7.0", + "web3-eth-contract": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-iban": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.7.0.tgz", + "integrity": "sha512-1PFE/Og+sPZaug+M9TqVUtjOtq0HecE+SjDcsOOysXSzslNC2CItBGkcRwbvUcS+LbIkA7MFsuqYxOL0IV/gyA==", + "dependencies": { + "bn.js": "^4.11.9", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-personal": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.7.0.tgz", + "integrity": "sha512-Dr9RZTNOR80PhrPKGdktDUXpOgExEcCcosBj080lKCJFU1paSPj9Zfnth3u6BtIOXyKsVFTrpqekqUDyAwXnNw==", + "dependencies": { + "@types/node": "^12.12.6", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-net": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-personal/node_modules/@types/node": { + "version": "12.20.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", + "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + }, + "node_modules/web3-net": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.7.0.tgz", + "integrity": "sha512-8pmfU1Se7DmG40Pu8nOCKlhuI12VsVzCtdFDnLAai0zGVAOUuuOCK71B2aKm6u9amWBJjtOlyrCwvsG+QEd6dw==", + "dependencies": { + "web3-core": "1.7.0", + "web3-core-method": "1.7.0", + "web3-utils": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-http": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.7.0.tgz", + "integrity": "sha512-Y9reeEiApfvQKLUUtrU4Z0c+H6b7BMWcsxjgoXndI1C5NB297mIUfltXxfXsh5C/jk5qn4Q3sJp3SwQTyVjH7Q==", + "dependencies": { + "web3-core-helpers": "1.7.0", + "xhr2-cookies": "1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-ipc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.7.0.tgz", + "integrity": "sha512-U5YLXgu6fvAK4nnMYqo9eoml3WywgTym0dgCdVX/n1UegLIQ4nctTubBAuWQEJzmAzwh+a6ValGcE7ZApTRI7Q==", + "dependencies": { + "oboe": "2.1.5", + "web3-core-helpers": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-ws": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.7.0.tgz", + "integrity": "sha512-0a8+lVV3JBf+eYnGOsdzOpftK1kis5X7s35QAdoaG5SDapnEylXFlR4xDSSSU88ZwMwvse8hvng2xW6A7oeWxw==", + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.7.0", + "websocket": "^1.0.32" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-shh": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.7.0.tgz", + "integrity": "sha512-RZhxcevALIPK178VZCpwMBvQeW+IoWtRJ4EMdegpbnETeZaC3aRUcs6vKnrf0jXJjm4J/E2Dt438Y1Ord/1IMw==", + "hasInstallScript": true, + "dependencies": { + "web3-core": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-net": "1.7.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.7.0.tgz", + "integrity": "sha512-O8Tl4Ky40Sp6pe89Olk2FsaUkgHyb5QAXuaKo38ms3CxZZ4d3rPGfjP9DNKGm5+IUgAZBNpF1VmlSmNCqfDI1w==", + "dependencies": { + "bn.js": "^4.11.9", + "ethereum-bloom-filters": "^1.0.6", + "ethereumjs-util": "^7.1.0", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "dependencies": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "node_modules/xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "dependencies": { + "xhr-request": "^1.1.0" + } + }, + "node_modules/xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "dependencies": { + "cookiejar": "^2.1.1" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", + "engines": { + "node": ">=0.10.32" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==" + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@ethereumjs/common": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.0.tgz", + "integrity": "sha512-Cq2qS0FTu6O2VU1sgg+WyU9Ps0M6j/BEMHN+hRaECXCV/r0aI78u4N6p52QW/BDVhwWZpCdrvG8X7NJdzlpNUA==", + "requires": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.3" + } + }, + "@ethereumjs/tx": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.4.0.tgz", + "integrity": "sha512-WWUwg1PdjHKZZxPPo274ZuPsJCWV3SqATrEKQP1n2DrVYVP1aZIYpo/mFaA0BDoE0tIQmBeimRCEA0Lgil+yYw==", + "requires": { + "@ethereumjs/common": "^2.6.0", + "ethereumjs-util": "^7.1.3" + } + }, + "@ethersproject/abi": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", + "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/basex": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", + "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/contracts": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", + "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", + "requires": { + "@ethersproject/abi": "^5.5.0", + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0" + } + }, + "@ethersproject/experimental": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/experimental/-/experimental-5.5.0.tgz", + "integrity": "sha512-Q62IjbhlgVmeFHRI6JSU/mZbtVGd8mNgkuIU0IxyTVszUzNblocVctIngAiWm8gGqr/ytj7hN1FRadCRMY4zIA==", + "requires": { + "@ethersproject/web": "^5.5.0", + "ethers": "^5.5.0", + "scrypt-js": "3.0.1" + } + }, + "@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/hdnode": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", + "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + } + }, + "@ethersproject/json-wallets": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", + "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", + "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/pbkdf2": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", + "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/sha2": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/providers": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.2.tgz", + "integrity": "sha512-hkbx7x/MKcRjyrO4StKXCzCpWer6s97xnm34xkfPiarhtEUVAN4TBBpamM+z66WcTt7H5B53YwbRj1n7i8pZoQ==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "@ethersproject/random": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.1.tgz", + "integrity": "sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/sha2": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", + "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "hash.js": "1.1.7" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/solidity": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", + "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/units": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", + "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/wallet": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", + "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/json-wallets": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/wordlists": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", + "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@gnosis.pm/safe-core-sdk": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-core-sdk/-/safe-core-sdk-1.3.0.tgz", + "integrity": "sha512-laKkyJUv0llPPG5ep2+18v/anIEGi+KjarNoeVAutYzIeAAkSvvVbY8qyfczh5bWQkqvQ3K1l/QLUJ8Kx4hm3g==", + "requires": { + "@gnosis.pm/safe-core-sdk-types": "^0.1.1", + "@gnosis.pm/safe-deployments": "^1.7.0", + "ethereumjs-util": "^7.1.3", + "semver": "^7.3.5" + } + }, + "@gnosis.pm/safe-core-sdk-types": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-core-sdk-types/-/safe-core-sdk-types-0.1.1.tgz", + "integrity": "sha512-PghXGDaI5Foq37nZGmI90U2OKMeGtxh5KqkDqou9aFHwGVa/nf9HRQPxG9/XUzcyfe9OlKttDlJnR3XnC3dSDw==" + }, + "@gnosis.pm/safe-deployments": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-deployments/-/safe-deployments-1.8.0.tgz", + "integrity": "sha512-xK2ZZXxCEGOw+6UZAeUmvqE/4C/XTpYmv1a8KzKUgSOxcGkHsIDqcjdKjqif7gOdnwHl4+XXJUtDQEuSLT4Scg==" + }, + "@gnosis.pm/safe-ethers-adapters": { + "version": "0.1.0-alpha.7", + "resolved": "https://registry.npmjs.org/@gnosis.pm/safe-ethers-adapters/-/safe-ethers-adapters-0.1.0-alpha.7.tgz", + "integrity": "sha512-NtxP71cplKB7AvILF4siC/eICcIc+qCzxzrQe8lqL7mwrnWgJ8VxLcVRO6944A+mLT7xG/fPpupF9rcuNyphkw==", + "requires": { + "@gnosis.pm/safe-core-sdk": "^1.0.0", + "@gnosis.pm/safe-core-sdk-types": "^0.1.1", + "@gnosis.pm/safe-deployments": "^1.0.0", + "axios": "^0.21.1" + } + }, + "@nomad-xyz/contract-interfaces": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@nomad-xyz/contract-interfaces/-/contract-interfaces-1.1.1.tgz", + "integrity": "sha512-bF0NYdck/LItN9s9D5ZQDzquiXv6sYCCi78lyHMiUNYaCGG3KLKeI5LkhW4fUlhgsNWBcQ9cdHt71S58m8KQrA==", + "requires": { + "@ethersproject/experimental": "^5.3.0", + "@types/node": "^16.10.2", + "typescript": "^4.4.3" + }, + "dependencies": { + "@types/node": { + "version": "16.11.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.21.tgz", + "integrity": "sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A==" + } + } + }, + "@nomad-xyz/sdk": { + "version": "file:nomad-xyz-sdk-1.2.4.tgz", + "integrity": "sha512-sE4PqrAIFpQq0OOhnwyuD1soVHKGVV9LyqWLbN2XbacwOTZhxS2iTd8SyQCPU1cb+6fQj5diFysEKjoaFrFzQA==", + "requires": { + "@gnosis.pm/safe-core-sdk": "^1.3.0", + "@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.7", + "@nomad-xyz/contract-interfaces": "1.1.1", + "ethers": "^5.4.6", + "web3": "^1.6.1" + } + }, + "@sindresorhus/is": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", + "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==" + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + }, + "@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bunyan": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.8.tgz", + "integrity": "sha512-Cblq+Yydg3u+sGiz2mjHjC5MPmdjY+No4qvHrF+BUhblsmSfMvsHLbOG62tPbonsqBj6sbWv1LHcsoe5Jw+/Ow==", + "requires": { + "@types/node": "*" + } + }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "@types/keyv": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", + "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", + "requires": { + "@types/node": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "@types/node": { + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.12.tgz", + "integrity": "sha512-4YpbAsnJXWYK/fpTVFlMIcUIho2AYCi4wg5aNPrG1ng7fn/1/RZfCIpRCiBX+12RVa34RluilnvCqD+g3KiSiA==" + }, + "@types/node-pg-migrate": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/node-pg-migrate/-/node-pg-migrate-2.3.1.tgz", + "integrity": "sha512-ccKzTzDu8b6IRttXh1Ch6JlEQvVM4jrRUU9xyEp+Pq5yd3vBCg84pJeAUzycyts5Tg+eyiLqmlIW3g/SrRlqEQ==" + }, + "@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/pg": { + "version": "8.6.4", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.4.tgz", + "integrity": "sha512-uYA7UMVzDFpJobCrqwW/iWkFmvizy6knIUgr0Quaw7K1Le3ZnF7hI3bKqFoxPZ+fju1Sc7zdTvOl9YfFZPcmeA==", + "requires": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "requires": { + "@types/node": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "optional": true + }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "bignumber.js": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==" + }, + "bintrees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" + }, + "blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "requires": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + } + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "bufferutil": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", + "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "requires": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "requires": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "crc-32": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.1.tgz", + "integrity": "sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w==", + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.3.1" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + } + } + }, + "eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "requires": { + "js-sha3": "^0.8.0" + } + }, + "ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereumjs-util": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz", + "integrity": "sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw==", + "requires": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "dependencies": { + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + } + } + }, + "ethers": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.3.tgz", + "integrity": "sha512-fTT4WT8/hTe/BLwRUtl7I5zlpF3XC3P/Xwqxc5AIP2HGlH15qpmjs0Ou78az93b1rLITzXLFxoNX63B8ZbUd7g==", + "requires": { + "@ethersproject/abi": "5.5.0", + "@ethersproject/abstract-provider": "5.5.1", + "@ethersproject/abstract-signer": "5.5.0", + "@ethersproject/address": "5.5.0", + "@ethersproject/base64": "5.5.0", + "@ethersproject/basex": "5.5.0", + "@ethersproject/bignumber": "5.5.0", + "@ethersproject/bytes": "5.5.0", + "@ethersproject/constants": "5.5.0", + "@ethersproject/contracts": "5.5.0", + "@ethersproject/hash": "5.5.0", + "@ethersproject/hdnode": "5.5.0", + "@ethersproject/json-wallets": "5.5.0", + "@ethersproject/keccak256": "5.5.0", + "@ethersproject/logger": "5.5.0", + "@ethersproject/networks": "5.5.2", + "@ethersproject/pbkdf2": "5.5.0", + "@ethersproject/properties": "5.5.0", + "@ethersproject/providers": "5.5.2", + "@ethersproject/random": "5.5.1", + "@ethersproject/rlp": "5.5.0", + "@ethersproject/sha2": "5.5.0", + "@ethersproject/signing-key": "5.5.0", + "@ethersproject/solidity": "5.5.0", + "@ethersproject/strings": "5.5.0", + "@ethersproject/transactions": "5.5.0", + "@ethersproject/units": "5.5.0", + "@ethersproject/wallet": "5.5.0", + "@ethersproject/web": "5.5.1", + "@ethersproject/wordlists": "5.5.0" + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" + }, + "express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "requires": { + "type": "^2.5.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "follow-redirects": { + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "got": { + "version": "11.8.3", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", + "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + } + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + } + }, + "keyv": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", + "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + }, + "mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "requires": { + "mime-db": "1.51.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "*" + } + }, + "mock-fs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", + "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "requires": { + "varint": "^5.0.0" + } + }, + "multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "requires": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + }, + "dependencies": { + "multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + } + } + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "optional": true + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + }, + "node-pg-migrate": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-6.2.1.tgz", + "integrity": "sha512-EsIOAWFBSBa/2g4BjA1tjRtPOEjOiZ/gqpjjxtsUyQRau0O2s1SVwyRW1HRA2qXZDMT/sA8GxcwLWqkK6BaGxA==", + "requires": { + "@types/pg": "^8.0.0", + "decamelize": "^5.0.0", + "mkdirp": "~1.0.0", + "yargs": "~17.3.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "oboe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", + "integrity": "sha1-VVQoTFQ6ImbXo48X4HOCH73jk80=", + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-headers": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", + "integrity": "sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "optional": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pg": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", + "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.4.1", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", + "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", + "requires": {} + }, + "pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true + }, + "printj": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz", + "integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "prom-client": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.0.1.tgz", + "integrity": "sha512-HxTArb6fkOntQHoRGvv4qd/BkorjliiuO2uSWC2KC17MUTKYttWdDoXX/vxOhQdkoECEM9BBH0pj2l8G8kev6w==", + "requires": { + "tdigest": "^0.1.1" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "requires": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "requires": { + "bn.js": "^5.2.0" + }, + "dependencies": { + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "requires": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + } + } + }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "swarm-js": { + "version": "0.1.40", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz", + "integrity": "sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA==", + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request": "^1.0.1" + }, + "dependencies": { + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + } + } + }, + "tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "tdigest": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", + "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", + "requires": { + "bintrees": "1.0.1" + } + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "ts-node": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", + "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==" + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, + "utf-8-validate": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.8.tgz", + "integrity": "sha512-k4dW/Qja1BYDl2qD4tOMB9PFVha/UJtxTc1cXYOe3WwA/2m0Yn4qB7wLMpJyLJ/7DR0XnTut3HsCSzDT4ZvKgA==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "web3": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.7.0.tgz", + "integrity": "sha512-n39O7QQNkpsjhiHMJ/6JY6TaLbdX+2FT5iGs8tb3HbIWOhPm4+a7UDbr5Lkm+gLa9aRKWesZs5D5hWyEvg4aJA==", + "requires": { + "web3-bzz": "1.7.0", + "web3-core": "1.7.0", + "web3-eth": "1.7.0", + "web3-eth-personal": "1.7.0", + "web3-net": "1.7.0", + "web3-shh": "1.7.0", + "web3-utils": "1.7.0" + } + }, + "web3-bzz": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.7.0.tgz", + "integrity": "sha512-XPhTWUnZa8gnARfiqaag3jJ9+6+a66Li8OikgBUJoMUqPuQTCJPncTbGYqOJIfRFGavEAdlMnfYXx9lvgv2ZPw==", + "requires": { + "@types/node": "^12.12.6", + "got": "9.6.0", + "swarm-js": "^0.1.40" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/node": { + "version": "12.20.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", + "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + } + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + } + } + }, + "web3-core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.7.0.tgz", + "integrity": "sha512-U/CRL53h3T5KHl8L3njzCBT7fCaHkbE6BGJe3McazvFldRbfTDEHXkUJCyM30ZD0RoLi3aDfTVeFIusmEyCctA==", + "requires": { + "@types/bn.js": "^4.11.5", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-requestmanager": "1.7.0", + "web3-utils": "1.7.0" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "12.20.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", + "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + } + } + }, + "web3-core-helpers": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.7.0.tgz", + "integrity": "sha512-kFiqsZFHJliKF8VKZNjt2JvKu3gu7h3N1/ke3EPhdp9Li/rLmiyzFVr6ApryZ1FSjbSx6vyOkibG3m6xQ5EHJA==", + "requires": { + "web3-eth-iban": "1.7.0", + "web3-utils": "1.7.0" + } + }, + "web3-core-method": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.7.0.tgz", + "integrity": "sha512-43Om+kZX8wU5u1pJ28TltF9e9pSTRph6b8wrOb6wgXAfPHqMulq6UTBJWjXXIRVN46Eiqv0nflw35hp9bbgnbA==", + "requires": { + "@ethersproject/transactions": "^5.0.0-beta.135", + "web3-core-helpers": "1.7.0", + "web3-core-promievent": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-utils": "1.7.0" + } + }, + "web3-core-promievent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.7.0.tgz", + "integrity": "sha512-xPH66XeC0K0k29GoRd0vyPQ07yxERPRd4yVPrbMzGAz/e9E4M3XN//XK6+PdfGvGw3fx8VojS+tNIMiw+PujbQ==", + "requires": { + "eventemitter3": "4.0.4" + } + }, + "web3-core-requestmanager": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.7.0.tgz", + "integrity": "sha512-rA3dBTBPrt+eIfTAQ2/oYNTN/2wbZaYNR3pFZGqG8+2oCK03+7oQyz4sWISKy/nYQhURh4GK01rs9sN4o/Tq9w==", + "requires": { + "util": "^0.12.0", + "web3-core-helpers": "1.7.0", + "web3-providers-http": "1.7.0", + "web3-providers-ipc": "1.7.0", + "web3-providers-ws": "1.7.0" + } + }, + "web3-core-subscriptions": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.7.0.tgz", + "integrity": "sha512-6giF8pyJrPmWrRpc2WLoVCvQdMMADp20ZpAusEW72axauZCNlW1XfTjs0i4QHQBfdd2lFp65qad9IuATPhuzrQ==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.7.0" + } + }, + "web3-eth": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.7.0.tgz", + "integrity": "sha512-3uYwjMjn/MZjKIzXCt4YL9ja/k9X5shfa4lKparZhZE6uesmu+xmSmrEFXA/e9qcveF50jkV7frjkT8H+cLYtw==", + "requires": { + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-eth-abi": "1.7.0", + "web3-eth-accounts": "1.7.0", + "web3-eth-contract": "1.7.0", + "web3-eth-ens": "1.7.0", + "web3-eth-iban": "1.7.0", + "web3-eth-personal": "1.7.0", + "web3-net": "1.7.0", + "web3-utils": "1.7.0" + } + }, + "web3-eth-abi": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.7.0.tgz", + "integrity": "sha512-heqR0bWxgCJwjWIhq2sGyNj9bwun5+Xox/LdZKe+WMyTSy0cXDXEAgv3XKNkXC4JqdDt/ZlbTEx4TWak4TRMSg==", + "requires": { + "@ethersproject/abi": "5.0.7", + "web3-utils": "1.7.0" + }, + "dependencies": { + "@ethersproject/abi": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.7.tgz", + "integrity": "sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==", + "requires": { + "@ethersproject/address": "^5.0.4", + "@ethersproject/bignumber": "^5.0.7", + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/constants": "^5.0.4", + "@ethersproject/hash": "^5.0.4", + "@ethersproject/keccak256": "^5.0.3", + "@ethersproject/logger": "^5.0.5", + "@ethersproject/properties": "^5.0.3", + "@ethersproject/strings": "^5.0.4" + } + } + } + }, + "web3-eth-accounts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.7.0.tgz", + "integrity": "sha512-Zwm7TlQXdXGRuS6+ib1YsR5fQwpfnFyL6UAZg1zERdrUrs3IkCZSL3yCP/8ZYbAjdTEwWljoott2iSqXNH09ug==", + "requires": { + "@ethereumjs/common": "^2.5.0", + "@ethereumjs/tx": "^3.3.2", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "ethereumjs-util": "^7.0.10", + "scrypt-js": "^3.0.1", + "uuid": "3.3.2", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-utils": "1.7.0" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.7.0.tgz", + "integrity": "sha512-2LY1Xwxu5rx468nqHuhvupQAIpytxIUj3mGL9uexszkhrQf05THVe3i4OnUCzkeN6B2cDztNOqLT3j9SSnVQDg==", + "requires": { + "@types/bn.js": "^4.11.5", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-promievent": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-eth-abi": "1.7.0", + "web3-utils": "1.7.0" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + } + } + }, + "web3-eth-ens": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.7.0.tgz", + "integrity": "sha512-I1bikYJJWQ/FJZIAvwsGOvzAgcRIkosWG4s1L6veRoXaU8OEJFeh4s00KcfHDxg7GWZZGbUSbdbzKpwRbWnvkg==", + "requires": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-promievent": "1.7.0", + "web3-eth-abi": "1.7.0", + "web3-eth-contract": "1.7.0", + "web3-utils": "1.7.0" + } + }, + "web3-eth-iban": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.7.0.tgz", + "integrity": "sha512-1PFE/Og+sPZaug+M9TqVUtjOtq0HecE+SjDcsOOysXSzslNC2CItBGkcRwbvUcS+LbIkA7MFsuqYxOL0IV/gyA==", + "requires": { + "bn.js": "^4.11.9", + "web3-utils": "1.7.0" + } + }, + "web3-eth-personal": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.7.0.tgz", + "integrity": "sha512-Dr9RZTNOR80PhrPKGdktDUXpOgExEcCcosBj080lKCJFU1paSPj9Zfnth3u6BtIOXyKsVFTrpqekqUDyAwXnNw==", + "requires": { + "@types/node": "^12.12.6", + "web3-core": "1.7.0", + "web3-core-helpers": "1.7.0", + "web3-core-method": "1.7.0", + "web3-net": "1.7.0", + "web3-utils": "1.7.0" + }, + "dependencies": { + "@types/node": { + "version": "12.20.42", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", + "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + } + } + }, + "web3-net": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.7.0.tgz", + "integrity": "sha512-8pmfU1Se7DmG40Pu8nOCKlhuI12VsVzCtdFDnLAai0zGVAOUuuOCK71B2aKm6u9amWBJjtOlyrCwvsG+QEd6dw==", + "requires": { + "web3-core": "1.7.0", + "web3-core-method": "1.7.0", + "web3-utils": "1.7.0" + } + }, + "web3-providers-http": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.7.0.tgz", + "integrity": "sha512-Y9reeEiApfvQKLUUtrU4Z0c+H6b7BMWcsxjgoXndI1C5NB297mIUfltXxfXsh5C/jk5qn4Q3sJp3SwQTyVjH7Q==", + "requires": { + "web3-core-helpers": "1.7.0", + "xhr2-cookies": "1.1.0" + } + }, + "web3-providers-ipc": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.7.0.tgz", + "integrity": "sha512-U5YLXgu6fvAK4nnMYqo9eoml3WywgTym0dgCdVX/n1UegLIQ4nctTubBAuWQEJzmAzwh+a6ValGcE7ZApTRI7Q==", + "requires": { + "oboe": "2.1.5", + "web3-core-helpers": "1.7.0" + } + }, + "web3-providers-ws": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.7.0.tgz", + "integrity": "sha512-0a8+lVV3JBf+eYnGOsdzOpftK1kis5X7s35QAdoaG5SDapnEylXFlR4xDSSSU88ZwMwvse8hvng2xW6A7oeWxw==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.7.0", + "websocket": "^1.0.32" + } + }, + "web3-shh": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.7.0.tgz", + "integrity": "sha512-RZhxcevALIPK178VZCpwMBvQeW+IoWtRJ4EMdegpbnETeZaC3aRUcs6vKnrf0jXJjm4J/E2Dt438Y1Ord/1IMw==", + "requires": { + "web3-core": "1.7.0", + "web3-core-method": "1.7.0", + "web3-core-subscriptions": "1.7.0", + "web3-net": "1.7.0" + } + }, + "web3-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.7.0.tgz", + "integrity": "sha512-O8Tl4Ky40Sp6pe89Olk2FsaUkgHyb5QAXuaKo38ms3CxZZ4d3rPGfjP9DNKGm5+IUgAZBNpF1VmlSmNCqfDI1w==", + "requires": { + "bn.js": "^4.11.9", + "ethereum-bloom-filters": "^1.0.6", + "ethereumjs-util": "^7.1.0", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + } + }, + "websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "requires": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "requires": {} + }, + "xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "requires": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "requires": { + "xhr-request": "^1.1.0" + } + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + } + } +} diff --git a/tools/indexer/package.json b/tools/indexer/package.json new file mode 100644 index 000000000..21e6f4722 --- /dev/null +++ b/tools/indexer/package.json @@ -0,0 +1,37 @@ +{ + "author": "Illusory Systems Inc.", + "license": "Apache 2.0", + "name": "indexer", + "version": "0.1.0", + "description": "Indexer", + "scripts": { + "core": "PROGRAMM=core ts-node main", + "api": "PROGRAMM=api ts-node main", + "any": "ts-node main", + "start": "npm run migrate && npm run any", + "migrate": "node-pg-migrate up" + }, + "dependencies": { + "@nomad-xyz/contract-interfaces": "1.1.1", + "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.2.4.tgz", + "@types/bunyan": "^1.8.7", + "@types/express": "^4.17.13", + "@types/node-pg-migrate": "^2.3.1", + "@types/pg": "^8.6.4", + "bunyan": "^1.8.15", + "dotenv": "^10.0.0", + "ethers": "^5.4.6", + "express": "^4.17.2", + "got": "^11.8.2", + "moment": "^2.29.1", + "node-pg-migrate": "^6.2.1", + "pg": "^8.7.1", + "prom-client": "^14.0.0", + "request": "^2.88.2", + "ts-node": "^10.4.0", + "typescript": "^4.4.3" + }, + "devDependencies": { + "prettier": "^2.4.1" + } +} diff --git a/tools/indexer/run.sh b/tools/indexer/run.sh new file mode 100644 index 000000000..85e12828f --- /dev/null +++ b/tools/indexer/run.sh @@ -0,0 +1,3 @@ +docker run --name pg1 -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres +docker run --name core --rm --env-file .env --link pg1 -e PROGRAM=core -d indexer +docker run --name api --rm -p 3000:3000 --env-file .env -e PROGRAM=api -d indexer \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/api/index.ts b/typescript/nomad-monitor/src/indexer/api/index.ts deleted file mode 100644 index 0b0d68e19..000000000 --- a/typescript/nomad-monitor/src/indexer/api/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import express from 'express'; -import { DB, MsgRequest } from '../core/db'; -import * as dotenv from 'dotenv'; -dotenv.config({}); - - -function fail(res: any, code: number, reason: string) { - return res.status(code).json({error: reason}); -} - - -const PORT = process.env.PORT; - -export async function run(db: DB) { - const app = express(); - - app.get('/healthcheck', (req,res) => res.send('OK!')); - - app.get('/tx/:tx', async (req,res) => { - const message = await db.getMessageByEvm(req.params.tx); - return res.json(message.toObject()) - }); - - app.get('/hash/:hash', async (req,res) => { - const message = await db.getMessageByHash(req.params.hash); - return res.json(message.toObject()) - }); - - app.get('/tx', async (req: express.Request<{}, {}, {}, MsgRequest>,res) => { - - const {size, } = req.query; // page, destination, origin, receiver, sender - - if (size && size > 30) return fail(res, 403, 'maximum size is 30') - - const messages = await db.getMessages(req.query); - - // const message = await db.getMessageByHash(req.params.hash); - return res.json(messages) - }); - - app.listen(PORT, () => { - console.log(`⚡️[server]: Server is running at https://localhost:${PORT}`); - }); -} \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/core/index.ts b/typescript/nomad-monitor/src/indexer/core/index.ts deleted file mode 100644 index 6e39e0735..000000000 --- a/typescript/nomad-monitor/src/indexer/core/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { mainnet } from '@nomad-xyz/sdk/src'; -import { Processor } from './consumer'; -import { Orchestrator } from './orchestrator'; -import * as dotenv from 'dotenv'; -import { ethers } from 'ethers'; -import { IndexerCollector } from './metrics'; -import Logger from 'bunyan'; -import { DB } from './db'; -dotenv.config({}); - -const infuraKey = process.env.INFURA_KEY!; -const moonbeamRPC = process.env.MOONBEAM_RPC!; -const environment = process.env.ENVIRONMENT!; - -export async function run(db: DB) { - const ctx = mainnet; - - const ethereumId = ctx.mustGetDomain('ethereum').id; - const infura = new ethers.providers.InfuraProvider('homestead', infuraKey); - ctx.registerProvider(ethereumId, infura); - - const moonbeamId = ctx.mustGetDomain('moonbeam').id; - ctx.registerRpcProvider(moonbeamId, moonbeamRPC); - const logger = createLogger('indexer', environment); - - const c = new Processor(db, logger); - const m = new IndexerCollector(environment, logger); - - const o = new Orchestrator( - mainnet, - c, - mainnet.domainNumbers[0], - m, - logger, - db, - ); - await o.init(); - - await o.startConsuming(); -} - -function createLogger(name: string, environment: string) { - return Logger.createLogger({ - name, - serializers: Logger.stdSerializers, - level: 'debug', - environment: environment, - }); -} diff --git a/typescript/nomad-monitor/src/indexer/core/metrics.ts b/typescript/nomad-monitor/src/indexer/core/metrics.ts deleted file mode 100644 index 00cb3b65d..000000000 --- a/typescript/nomad-monitor/src/indexer/core/metrics.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { MetricsCollector } from '../../metrics'; -import { Gauge } from 'prom-client'; -import Logger from 'bunyan'; - -const prefix = `fancy_monitor`; - -export class IndexerCollector extends MetricsCollector { - private numDispatchedGauge: Gauge; - private numUpdatedGauge: Gauge; - private numRelayedGauge: Gauge; - private numProcessedGauge: Gauge; - - private meanUpdateTimeGauge: Gauge; - private meanRelayTimeGauge: Gauge; - private meanProcessTimeGauge: Gauge; - private meanEndToEndTimeGauge: Gauge; - - constructor(environment: string, logger: Logger) { - super(environment, logger); - - // Count - - this.numDispatchedGauge = new Gauge({ - name: prefix + '_number_messages_dispatched', - help: 'Gauge that indicates how many messages have been dispatched for a network.', - labelNames: ['network', 'environment'], - }); - - this.numUpdatedGauge = new Gauge({ - name: prefix + '_number_messages_updated', - help: 'Gauge that indicates how many messages have been updated for a network.', - labelNames: ['network', 'environment'], - }); - - this.numRelayedGauge = new Gauge({ - name: prefix + '_number_messages_relayed', - help: 'Gauge that indicates how many messages have been relayed for a network.', - labelNames: ['network', 'environment'], - }); - - this.numProcessedGauge = new Gauge({ - name: prefix + '_number_messages_processed', - help: 'Gauge that indicates how many messages have been processed for a network.', - labelNames: ['network', 'environment'], - }); - - // Time - - this.meanUpdateTimeGauge = new Gauge({ - name: prefix + '_mean_update_time', - help: 'Gauge that indicates how long does it take to move from dispatched to updated.', - labelNames: ['network', 'environment'], - }); - - this.meanRelayTimeGauge = new Gauge({ - name: prefix + '_mean_relay_time', - help: 'Gauge that indicates how long does it take to move from updated to relayed.', - labelNames: ['network', 'environment'], - }); - - this.meanProcessTimeGauge = new Gauge({ - name: prefix + '_mean_process_time', - help: 'Gauge that indicates how long does it take to move from relayed to processed.', - labelNames: ['network', 'environment'], - }); - - this.meanEndToEndTimeGauge = new Gauge({ - name: prefix + '_mean_e2e_time', - help: 'Gauge that indicates how long does it take to move from dispatched to processed.', - labelNames: ['network', 'environment'], - }); - } - - /** - * Sets the state for a bridge. - */ - setNetworkState( - network: string, - dispatched: number, - updated: number, - relayed: number, - processed: number, - updateTime: number, - relayTime: number, - processTime: number, - e2eTime: number, - ) { - this.numDispatchedGauge.set( - { network, environment: this.environment }, - dispatched, - ); - - this.numUpdatedGauge.set( - { network, environment: this.environment }, - updated, - ); - - this.numRelayedGauge.set( - { network, environment: this.environment }, - relayed, - ); - - this.numProcessedGauge.set( - { network, environment: this.environment }, - processed, - ); - - this.meanUpdateTimeGauge.set( - { network, environment: this.environment }, - updateTime, - ); - - this.meanRelayTimeGauge.set( - { network, environment: this.environment }, - relayTime, - ); - - this.meanProcessTimeGauge.set( - { network, environment: this.environment }, - processTime, - ); - - this.meanEndToEndTimeGauge.set( - { network, environment: this.environment }, - e2eTime, - ); - } -} diff --git a/typescript/nomad-monitor/src/indexer/main.ts b/typescript/nomad-monitor/src/indexer/main.ts deleted file mode 100644 index a5882b877..000000000 --- a/typescript/nomad-monitor/src/indexer/main.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as core from './core'; -import * as api from './api'; -import { DB } from './core/db'; - -console.log(process.argv[2]); - -(async () => { - const db = new DB(); - await db.connect(); - - if (process.argv[2] === 'api') { - await api.run(db) - } else { - await core.run(db); - } - -})() \ No newline at end of file diff --git a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js b/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js deleted file mode 100644 index 8b0ea3667..000000000 --- a/typescript/nomad-monitor/src/indexer/migrations/1642713407510_my-first-migration.js +++ /dev/null @@ -1,63 +0,0 @@ -/* eslint-disable camelcase */ - -exports.shorthands = undefined; - -exports.up = pgm => { - pgm.createTable('messages', { - id: 'id', - hash: {type: 'varchar(66)', notNull: true, unique: true}, // Nomad TX id - origin: {type: 'integer', notNull: true}, - destination: {type: 'integer', notNull: true}, - nonce: {type: 'integer', notNull: true}, - nomad_sender: {type: 'varchar(42)', notNull: true}, // Nomad sender (bridge router) - nomad_recipient: {type: 'varchar(42)', notNull: true}, // Nomad recipient (bridge router) - root: {type: 'varchar(66)', notNull: true}, - state: {type: 'integer', notNull: true}, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) - block: {type: 'integer', notNull: true}, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) - dispatched_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state dispatched - updated_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state updated - relayed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state relayed - received_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state received - processed_at: {type: 'bigint', notNull: true}, // TS at which the transaction got to state processed - // Bridge message internals - sender: {type: 'varchar(42)', notNull: false}, // sender - bridge_msg_type: {type: 'varchar(14)', notNull: false}, // Mostly 'transaction' - recipient: {type: 'varchar(42)', notNull: false}, // Real recipient on destination domain - bridge_msg_amount: {type: 'varchar(256)', notNull: false}, // Amount of Token - bridge_msg_allow_fast: {type: 'boolean', notNull: false}, // Allow fast? - bridge_msg_details_hash: {type: 'varchar(66)', notNull: false}, // Details hash - don't know what it is (need to do homework) - bridge_msg_token_domain: {type: 'integer', notNull: false}, // Token domain - bridge_msg_token_id: {type: 'varchar(42)', notNull: false}, // Token id (address) - raw: {type: 'varchar', notNull: true}, - leaf_index: {type: 'varchar(256)', notNull: true}, - evm: {type: 'varchar(66)', notNull: false}, - createdAt: { - type: 'timestamp', - notNull: true, - default: pgm.func('current_timestamp'), - }, - }) - pgm.createIndex('messages', 'id'); - - pgm.createTable('kv_storage', { - id: 'id', - namespace: {type: 'varchar', notNull: true}, - key: {type: 'varchar', notNull: true}, - value: {type: 'varchar', notNull: true}, - createdAt: { - type: 'timestamp', - notNull: true, - default: pgm.func('current_timestamp'), - }, - }) - pgm.createIndex('kv_storage', 'id'); - pgm.addConstraint('kv_storage', 'unique_namespace_key', { - unique: ['namespace', 'key'] - }) -}; - -exports.down = pgm => { - pgm.dropTable('messages'); - pgm.dropConstraint('kv_storage', 'unique_namespace_key') - pgm.dropTable('kv_storage'); -}; From 237ffce54f498ae8d249a1290ebbc098386e0db5 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 25 Jan 2022 21:31:39 +0100 Subject: [PATCH 20/63] chore: ethereup rpc provider --- tools/indexer/.env.example | 11 +++++++++++ tools/indexer/core/index.ts | 5 ++--- tools/indexer/{run.sh => example_run.sh} | 2 +- tools/indexer/package.json | 3 ++- 4 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tools/indexer/.env.example rename tools/indexer/{run.sh => example_run.sh} (63%) diff --git a/tools/indexer/.env.example b/tools/indexer/.env.example new file mode 100644 index 000000000..12d72cb51 --- /dev/null +++ b/tools/indexer/.env.example @@ -0,0 +1,11 @@ +ETHEREUM_RPC=https://eth-mainnet.alchemyapi.io/v2/xxx +MOONBEAM_RPC=https://moonbeam.api.onfinality.io/public +ENVIRONMENT=development + +PORT=3000 + +PGHOST=pg1 +PGUSER=postgres +PGDATABASE=postgres +PGPASSWORD=postgres +PGPORT=5432 \ No newline at end of file diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index ffacebdaa..042988db6 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -8,15 +8,14 @@ import { DB } from "./db"; import Logger from "bunyan"; dotenv.config({}); -const infuraKey = process.env.INFURA_KEY!; +const ethereumRPC = process.env.ETHEREUM_RPC!; const moonbeamRPC = process.env.MOONBEAM_RPC!; export async function run(db: DB, environment: string, logger: Logger) { const ctx = mainnet; const ethereumId = ctx.mustGetDomain("ethereum").id; - const infura = new ethers.providers.InfuraProvider("homestead", infuraKey); - ctx.registerProvider(ethereumId, infura); + ctx.registerRpcProvider(ethereumId, ethereumRPC); const moonbeamId = ctx.mustGetDomain("moonbeam").id; ctx.registerRpcProvider(moonbeamId, moonbeamRPC); diff --git a/tools/indexer/run.sh b/tools/indexer/example_run.sh similarity index 63% rename from tools/indexer/run.sh rename to tools/indexer/example_run.sh index 85e12828f..9e92bb888 100644 --- a/tools/indexer/run.sh +++ b/tools/indexer/example_run.sh @@ -1,3 +1,3 @@ docker run --name pg1 -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres docker run --name core --rm --env-file .env --link pg1 -e PROGRAM=core -d indexer -docker run --name api --rm -p 3000:3000 --env-file .env -e PROGRAM=api -d indexer \ No newline at end of file +docker run --name api --rm -p 3000:3000 --env-file .env --link pg1 -e PROGRAM=api -d indexer \ No newline at end of file diff --git a/tools/indexer/package.json b/tools/indexer/package.json index 21e6f4722..dcb582120 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -9,7 +9,8 @@ "api": "PROGRAMM=api ts-node main", "any": "ts-node main", "start": "npm run migrate && npm run any", - "migrate": "node-pg-migrate up" + "migrate": "node-pg-migrate up", + "get_sdk": "cd ../../typescript/nomad-sdk && npm i && npm run build && npm pack && popd && mv ../../typescript/nomad-sdk/nomad-xyz-sdk-1.2.4.tgz ." }, "dependencies": { "@nomad-xyz/contract-interfaces": "1.1.1", From f7282da4ac82b48b6bc9684d3edc6578a1d41aa7 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 25 Jan 2022 22:09:18 +0100 Subject: [PATCH 21/63] chore: script --- tools/indexer/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/indexer/package.json b/tools/indexer/package.json index dcb582120..25e15d269 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -10,7 +10,7 @@ "any": "ts-node main", "start": "npm run migrate && npm run any", "migrate": "node-pg-migrate up", - "get_sdk": "cd ../../typescript/nomad-sdk && npm i && npm run build && npm pack && popd && mv ../../typescript/nomad-sdk/nomad-xyz-sdk-1.2.4.tgz ." + "get_sdk": "cd ../../typescript/nomad-sdk && npm i && npm run build && npm pack && cd ../../tools/indexer && mv ../../typescript/nomad-sdk/nomad-xyz-sdk-1.2.4.tgz ." }, "dependencies": { "@nomad-xyz/contract-interfaces": "1.1.1", From 9352b21198dd0d94c39112bb09cda5d27175001b Mon Sep 17 00:00:00 2001 From: Conner Swann <2635475+yourbuddyconner@users.noreply.github.com> Date: Tue, 25 Jan 2022 19:40:28 -0800 Subject: [PATCH 22/63] added docker-compose config and turned on indexer metrics --- tools/indexer/core/index.ts | 2 +- tools/indexer/docker-compose.yml | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tools/indexer/docker-compose.yml diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index 042988db6..1602353a5 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -31,7 +31,7 @@ export async function run(db: DB, environment: string, logger: Logger) { logger, db ); + await m.startServer(3000); await o.init(); - await o.startConsuming(); } diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml new file mode 100644 index 000000000..29a986671 --- /dev/null +++ b/tools/indexer/docker-compose.yml @@ -0,0 +1,40 @@ + +version: '3.7' +services: + + indexer: + build: . + restart: always + command: npm run start indexer + env_file: .env + environment: + - PROGRAM=core + ports: + - '9090:3000' + links: + - postgres + + api: + build: . + restart: always + command: npm run start indexer + ports: + - '8080:3000' + links: + - postgres + env_file: .env + environment: + - PROGRAM=api + + postgres: + image: postgres:latest + restart: always + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + logging: + options: + max-size: 10m + max-file: "3" + ports: + - '5438:5432' \ No newline at end of file From da03c8ed86f0a41916f8238df57f18aeb6b5fe17 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 26 Jan 2022 12:03:30 +0100 Subject: [PATCH 23/63] chore: env handling --- tools/indexer/core/index.ts | 28 +++++++++++++++++----------- tools/indexer/main.ts | 7 +++++-- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index 1602353a5..3ad9a6dbd 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -1,24 +1,30 @@ -import { mainnet } from "@nomad-xyz/sdk/dist"; +import { mainnet, staging, dev, NomadContext } from "@nomad-xyz/sdk/dist"; import { Processor } from "./consumer"; import { Orchestrator } from "./orchestrator"; import * as dotenv from "dotenv"; -import { ethers } from "ethers"; import { IndexerCollector } from "./metrics"; import { DB } from "./db"; import Logger from "bunyan"; dotenv.config({}); -const ethereumRPC = process.env.ETHEREUM_RPC!; -const moonbeamRPC = process.env.MOONBEAM_RPC!; - export async function run(db: DB, environment: string, logger: Logger) { - const ctx = mainnet; + let ctx: NomadContext; + if (environment === 'prod') { + ctx = mainnet + } else if (environment === 'staging') { + ctx = staging; + } else { + ctx = dev; + } - const ethereumId = ctx.mustGetDomain("ethereum").id; - ctx.registerRpcProvider(ethereumId, ethereumRPC); + ctx.domainNumbers.forEach(domain => { + const name = ctx.mustGetDomain(domain).name.toUpperCase(); + const rpcEnvKey = `${name}_RPC`; + const rpc = process.env[rpcEnvKey]; + if (!rpc) throw new Error(`RPC url for domain ${domain} is empty. Please provide as '${rpcEnvKey}=http://...' ENV variable`) - const moonbeamId = ctx.mustGetDomain("moonbeam").id; - ctx.registerRpcProvider(moonbeamId, moonbeamRPC); + ctx.registerRpcProvider(domain, rpc); + }) const c = new Processor(db, logger); const m = new IndexerCollector(environment, logger); @@ -31,7 +37,7 @@ export async function run(db: DB, environment: string, logger: Logger) { logger, db ); - await m.startServer(3000); + m.startServer(3000); await o.init(); await o.startConsuming(); } diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts index 417870614..6ed239d91 100644 --- a/tools/indexer/main.ts +++ b/tools/indexer/main.ts @@ -3,8 +3,11 @@ import * as api from "./api"; import { DB } from "./core/db"; import { createLogger } from "./core/utils"; -const environment = process.env.ENVIRONMENT!; -const program = process.env.PROGRAM!; +export type NomadEnvironment = 'dev' | 'staging' | 'prod'; +export type Program = 'api' | 'core'; + +const environment = process.env.ENVIRONMENT! as NomadEnvironment; +const program = process.env.PROGRAM! as Program; (async () => { const db = new DB(); From 8a778ad5e05e7c2f6638dd427dfff1cba61a830e Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 26 Jan 2022 12:13:36 +0100 Subject: [PATCH 24/63] chore: env example --- tools/indexer/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/indexer/.env.example b/tools/indexer/.env.example index 12d72cb51..fdb150ccb 100644 --- a/tools/indexer/.env.example +++ b/tools/indexer/.env.example @@ -1,6 +1,6 @@ ETHEREUM_RPC=https://eth-mainnet.alchemyapi.io/v2/xxx MOONBEAM_RPC=https://moonbeam.api.onfinality.io/public -ENVIRONMENT=development +ENVIRONMENT=dev # or prod or staging PORT=3000 From e7f45f0994a0a393034a7e198af554cd3ee14112 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 26 Jan 2022 12:15:59 +0100 Subject: [PATCH 25/63] chore: allow big tables for postgres --- tools/indexer/docker-compose.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml index 29a986671..29cd449ac 100644 --- a/tools/indexer/docker-compose.yml +++ b/tools/indexer/docker-compose.yml @@ -1,31 +1,30 @@ - version: '3.7' services: - - indexer: + + indexer: build: . restart: always command: npm run start indexer env_file: .env - environment: + environment: - PROGRAM=core - ports: + ports: - '9090:3000' links: - postgres - - api: + + api: build: . restart: always command: npm run start indexer - ports: + ports: - '8080:3000' links: - postgres env_file: .env - environment: + environment: - PROGRAM=api - + postgres: image: postgres:latest restart: always @@ -34,7 +33,7 @@ services: - POSTGRES_PASSWORD=postgres logging: options: - max-size: 10m + max-size: 100m max-file: "3" ports: - - '5438:5432' \ No newline at end of file + - '5438:5432' From 946ff7f84d433a77b725eb800924f67b617906d3 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 26 Jan 2022 18:37:19 +0100 Subject: [PATCH 26/63] fix: correct names --- tools/indexer/.env.example | 2 +- tools/indexer/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/indexer/.env.example b/tools/indexer/.env.example index fdb150ccb..d9263ed67 100644 --- a/tools/indexer/.env.example +++ b/tools/indexer/.env.example @@ -1,6 +1,6 @@ ETHEREUM_RPC=https://eth-mainnet.alchemyapi.io/v2/xxx MOONBEAM_RPC=https://moonbeam.api.onfinality.io/public -ENVIRONMENT=dev # or prod or staging +ENVIRONMENT=development # or production or staging PORT=3000 diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts index 6ed239d91..937b5ae00 100644 --- a/tools/indexer/main.ts +++ b/tools/indexer/main.ts @@ -3,7 +3,7 @@ import * as api from "./api"; import { DB } from "./core/db"; import { createLogger } from "./core/utils"; -export type NomadEnvironment = 'dev' | 'staging' | 'prod'; +export type NomadEnvironment = 'development' | 'staging' | 'prodaction'; export type Program = 'api' | 'core'; const environment = process.env.ENVIRONMENT! as NomadEnvironment; From 0f8035e402a4686a9af989c66703399d8fac3016 Mon Sep 17 00:00:00 2001 From: Conner Swann <2635475+yourbuddyconner@users.noreply.github.com> Date: Wed, 26 Jan 2022 18:31:27 -0800 Subject: [PATCH 27/63] minor nits, almost works --- tools/indexer/core/index.ts | 8 +++++--- tools/indexer/main.ts | 2 +- tools/indexer/package.json | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index 3ad9a6dbd..b89010903 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -9,7 +9,7 @@ dotenv.config({}); export async function run(db: DB, environment: string, logger: Logger) { let ctx: NomadContext; - if (environment === 'prod') { + if (environment === 'production') { ctx = mainnet } else if (environment === 'staging') { ctx = staging; @@ -21,6 +21,7 @@ export async function run(db: DB, environment: string, logger: Logger) { const name = ctx.mustGetDomain(domain).name.toUpperCase(); const rpcEnvKey = `${name}_RPC`; const rpc = process.env[rpcEnvKey]; + if (!rpc) throw new Error(`RPC url for domain ${domain} is empty. Please provide as '${rpcEnvKey}=http://...' ENV variable`) ctx.registerRpcProvider(domain, rpc); @@ -29,10 +30,11 @@ export async function run(db: DB, environment: string, logger: Logger) { const c = new Processor(db, logger); const m = new IndexerCollector(environment, logger); + console.log(ctx) const o = new Orchestrator( - mainnet, + ctx, c, - mainnet.domainNumbers[0], + ctx.domainNumbers[0], m, logger, db diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts index 937b5ae00..9c3eb4195 100644 --- a/tools/indexer/main.ts +++ b/tools/indexer/main.ts @@ -3,7 +3,7 @@ import * as api from "./api"; import { DB } from "./core/db"; import { createLogger } from "./core/utils"; -export type NomadEnvironment = 'development' | 'staging' | 'prodaction'; +export type NomadEnvironment = 'development' | 'staging' | 'production'; export type Program = 'api' | 'core'; const environment = process.env.ENVIRONMENT! as NomadEnvironment; diff --git a/tools/indexer/package.json b/tools/indexer/package.json index 25e15d269..1e554d7f3 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -10,6 +10,7 @@ "any": "ts-node main", "start": "npm run migrate && npm run any", "migrate": "node-pg-migrate up", + "release-docker": "docker buildx build --platform linux/amd64,linux/arm64 --push -t gcr.io/nomad-xyz/nomad-indexer:sha-$(git rev-parse --short HEAD) .", "get_sdk": "cd ../../typescript/nomad-sdk && npm i && npm run build && npm pack && cd ../../tools/indexer && mv ../../typescript/nomad-sdk/nomad-xyz-sdk-1.2.4.tgz ." }, "dependencies": { From bb3b1e1d2add199a769a03a9e0bf1b04d4944d3c Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 27 Jan 2022 11:40:40 +0100 Subject: [PATCH 28/63] fix: nits --- tools/indexer/core/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index b89010903..08314afa3 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -13,8 +13,10 @@ export async function run(db: DB, environment: string, logger: Logger) { ctx = mainnet } else if (environment === 'staging') { ctx = staging; - } else { + } else if (environment === 'development') { ctx = dev; + } else { + throw new Error(`Enviroment '${environment}' is not suppoerted`); } ctx.domainNumbers.forEach(domain => { @@ -30,7 +32,6 @@ export async function run(db: DB, environment: string, logger: Logger) { const c = new Processor(db, logger); const m = new IndexerCollector(environment, logger); - console.log(ctx) const o = new Orchestrator( ctx, c, From afcf9073a392739768bd0752d2d542c02c6b79fb Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 27 Jan 2022 18:14:28 +0100 Subject: [PATCH 29/63] feature: home health --- tools/indexer/core/metrics.ts | 16 +++++++- tools/indexer/core/orchestrator.ts | 59 +++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index db36e4d65..4613b779e 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -49,6 +49,8 @@ export class IndexerCollector extends MetricsCollector { private meanProcessTimeGauge: Gauge; private meanEndToEndTimeGauge: Gauge; + private homeFailedGauge: Gauge; + constructor(environment: string, logger: Logger) { super(environment, logger); @@ -103,6 +105,12 @@ export class IndexerCollector extends MetricsCollector { help: "Gauge that indicates how long does it take to move from dispatched to processed.", labelNames: ["network", "environment"], }); + + this.homeFailedGauge = new Gauge({ + name: 'nomad_monitor_home_failed', + help: 'Gauge that indicates if home of a network is in failed state.', + labelNames: ['network', 'environment'], + }); } /** @@ -117,7 +125,8 @@ export class IndexerCollector extends MetricsCollector { updateTime: number, relayTime: number, processTime: number, - e2eTime: number + e2eTime: number, + homeFailed: boolean, ) { this.numDispatchedGauge.set( { network, environment: this.environment }, @@ -158,5 +167,10 @@ export class IndexerCollector extends MetricsCollector { { network, environment: this.environment }, e2eTime ); + + this.homeFailedGauge.set( + { network, environment: this.environment }, + homeFailed ? 1 : 0, + ); } } diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index 1387d7d44..af62a4798 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -1,3 +1,4 @@ +import { Home } from "@nomad-xyz/contract-interfaces/core"; import { NomadContext } from "@nomad-xyz/sdk/dist"; import Logger from "bunyan"; import { Consumer } from "./consumer"; @@ -7,10 +8,38 @@ import { IndexerCollector } from "./metrics"; import { Statistics } from "./types"; import { replacer, sleep } from "./utils"; +class HomeHealth { + home: Home; + domain: number; + healthy: boolean; + logger: Logger; + + constructor(domain: number, ctx: NomadContext, logger: Logger) { + this.home = ctx.getCore(domain).home; + this.healthy = true; + } + + async updateHealth(): Promise { + try { + const state = await this.home.state(); + if (state !== 1) { + this.healthy = false; + } + } catch(e) { + this.logger.warn(`Couldn't collect home state for ${this.domain} domain. Error: ${e.message}`); + } + } + + get failed(): boolean { + return !this.healthy; + } +} + export class Orchestrator { sdk: NomadContext; consumer: Consumer; indexers: Map; + healthCheckers: Map; gov: number; done: boolean; freshStart: boolean; @@ -29,6 +58,7 @@ export class Orchestrator { this.sdk = sdk; this.consumer = c; this.indexers = new Map(); + this.healthCheckers = new Map(); this.gov = gov; this.done = false; this.freshStart = true; @@ -39,6 +69,7 @@ export class Orchestrator { async init() { await this.initIndexers(); + await this.initHealthCheckers(); await this.initalFeedConsumer(); } @@ -66,6 +97,16 @@ export class Orchestrator { return await indexer.updateAll(replicas); } + async checkAllHealth() { + await Promise.all( + this.sdk.domainNumbers.map((domain: number) => this.checkHealth(domain)) + ) + } + + async checkHealth(domain: number) { + await this.healthCheckers.get(domain)!.updateHealth(); + } + async initalFeedConsumer() { const events = Array.from(this.indexers.values()) .map((indexer) => indexer.persistance.allEvents()) @@ -82,11 +123,22 @@ export class Orchestrator { } } + async initHealthCheckers() { + for (const domain of this.sdk.domainNumbers) { + const checker = new HomeHealth(domain, this.sdk, this.logger); + await checker.updateHealth(); + this.healthCheckers.set(domain, checker); + } + } + async startConsuming() { while (!this.done) { this.logger.info(`Started to reindex`); const start = new Date().valueOf(); - await this.indexAll(); + await Promise.all([ + this.indexAll(), + this.checkAllHealth() + ]) this.logger.info( `Finished reindexing after ${ (new Date().valueOf() - start) / 1000 @@ -113,6 +165,8 @@ export class Orchestrator { counts: { dispatched, updated, relayed, processed }, timings: { meanUpdate, meanRelay, meanProcess, meanE2E }, } = statistics.forDomain(domain); + + const homeFailed = this.healthCheckers.get(domain)!.failed; this.metrics.setNetworkState( this.sdk.getDomain(domain)!.name, dispatched, @@ -122,7 +176,8 @@ export class Orchestrator { meanUpdate, meanRelay, meanProcess, - meanE2E + meanE2E, + homeFailed ); } From f7833201d8ef8aa20be267b3a420fb0c06102342 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 27 Jan 2022 18:17:30 +0100 Subject: [PATCH 30/63] fix: nits TS --- tools/indexer/core/orchestrator.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index af62a4798..74cb7c5b4 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -15,8 +15,10 @@ class HomeHealth { logger: Logger; constructor(domain: number, ctx: NomadContext, logger: Logger) { - this.home = ctx.getCore(domain).home; + this.domain = domain; + this.home = ctx.mustGetCore(domain).home; this.healthy = true; + this.logger = logger; } async updateHealth(): Promise { @@ -25,7 +27,7 @@ class HomeHealth { if (state !== 1) { this.healthy = false; } - } catch(e) { + } catch(e: any) { this.logger.warn(`Couldn't collect home state for ${this.domain} domain. Error: ${e.message}`); } } From 3f71a4a8925e4ac0053b196c400228361879d7fe Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 27 Jan 2022 18:18:47 +0100 Subject: [PATCH 31/63] fix: nits health restore, though might not need --- tools/indexer/core/orchestrator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index 74cb7c5b4..18d28ddba 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -26,6 +26,8 @@ class HomeHealth { const state = await this.home.state(); if (state !== 1) { this.healthy = false; + } else { + this.healthy = true; } } catch(e: any) { this.logger.warn(`Couldn't collect home state for ${this.domain} domain. Error: ${e.message}`); From 3e0f480a0f0ae0863d4ef4ebb99a79c809d2d613 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 27 Jan 2022 18:31:09 +0100 Subject: [PATCH 32/63] fix: error handling --- tools/indexer/core/indexer.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index b232952c9..78a9c7e62 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -48,9 +48,8 @@ export class Indexer { ); if (!block) { throw ( - error || new Error( - `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}` + `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}, error: ${error}` ) ); } @@ -114,9 +113,11 @@ export class Indexer { const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; - }, 5); + }, 7); - if (error) throw error; + if (error) throw new Error( + `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error}` + ); return fetchedEvents; }; From f27a748ec6b5d0fa606f9afd3bec011f3b80eb59 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 27 Jan 2022 18:41:08 +0100 Subject: [PATCH 33/63] feature: retry throws warns --- tools/indexer/core/indexer.ts | 5 +++-- tools/indexer/core/utils.ts | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 78a9c7e62..ad1027fd7 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -44,7 +44,8 @@ export class Indexer { const [block, error] = await retry( async () => await this.provider.getBlockWithTransactions(blockNumber), - 6 + 7, + (error: any) => this.orchestrator.logger.warn(`Some error happened at retrying getting the block ${blockNumber} for ${this.domain}, error: ${error}`) ); if (!block) { throw ( @@ -113,7 +114,7 @@ export class Indexer { const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; - }, 7); + }, 7, (error) => this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error.message}`)); if (error) throw new Error( `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error}` diff --git a/tools/indexer/core/utils.ts b/tools/indexer/core/utils.ts index cb69e6da4..4d69dac90 100644 --- a/tools/indexer/core/utils.ts +++ b/tools/indexer/core/utils.ts @@ -13,7 +13,8 @@ export function sleep(ms: number) { export async function retry( callback: () => Promise, - tries: number + tries: number, + onError: ((e: any) => Promise | void) | undefined, ): Promise<[T | undefined, any]> { let timeout = 5000; let lastError: any = undefined; @@ -21,6 +22,7 @@ export async function retry( try { return [await callback(), undefined]; } catch (e) { + if (onError) await onError(e); lastError = e; await sleep(timeout * 2 ** attempt); } From 181fcf1a66e7f80f4e11bd3256ee545a18de048d Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 27 Jan 2022 18:43:00 +0100 Subject: [PATCH 34/63] chore: prettier --- tools/indexer/core/index.ts | 26 ++++++++--------- tools/indexer/core/indexer.ts | 45 +++++++++++++++++++----------- tools/indexer/core/metrics.ts | 10 +++---- tools/indexer/core/orchestrator.ts | 13 ++++----- tools/indexer/core/utils.ts | 2 +- tools/indexer/docker-compose.yml | 9 +++--- tools/indexer/main.ts | 4 +-- 7 files changed, 57 insertions(+), 52 deletions(-) diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index 08314afa3..c2364f29d 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -9,37 +9,33 @@ dotenv.config({}); export async function run(db: DB, environment: string, logger: Logger) { let ctx: NomadContext; - if (environment === 'production') { - ctx = mainnet - } else if (environment === 'staging') { + if (environment === "production") { + ctx = mainnet; + } else if (environment === "staging") { ctx = staging; - } else if (environment === 'development') { + } else if (environment === "development") { ctx = dev; } else { throw new Error(`Enviroment '${environment}' is not suppoerted`); } - ctx.domainNumbers.forEach(domain => { + ctx.domainNumbers.forEach((domain) => { const name = ctx.mustGetDomain(domain).name.toUpperCase(); const rpcEnvKey = `${name}_RPC`; const rpc = process.env[rpcEnvKey]; - if (!rpc) throw new Error(`RPC url for domain ${domain} is empty. Please provide as '${rpcEnvKey}=http://...' ENV variable`) + if (!rpc) + throw new Error( + `RPC url for domain ${domain} is empty. Please provide as '${rpcEnvKey}=http://...' ENV variable` + ); ctx.registerRpcProvider(domain, rpc); - }) + }); const c = new Processor(db, logger); const m = new IndexerCollector(environment, logger); - const o = new Orchestrator( - ctx, - c, - ctx.domainNumbers[0], - m, - logger, - db - ); + const o = new Orchestrator(ctx, c, ctx.domainNumbers[0], m, logger, db); m.startServer(3000); await o.init(); await o.startConsuming(); diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index ad1027fd7..ec60d696a 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -45,13 +45,14 @@ export class Indexer { const [block, error] = await retry( async () => await this.provider.getBlockWithTransactions(blockNumber), 7, - (error: any) => this.orchestrator.logger.warn(`Some error happened at retrying getting the block ${blockNumber} for ${this.domain}, error: ${error}`) - ); - if (!block) { - throw ( - new Error( + (error: any) => + this.orchestrator.logger.warn( `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}, error: ${error}` ) + ); + if (!block) { + throw new Error( + `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}, error: ${error}` ); } const time = block.timestamp * 1000; @@ -106,19 +107,29 @@ export class Indexer { ); const fetchEvents = async (from: number, to: number) => { - const [fetchedEvents, error] = await retry(async () => { - const homeEvents = await this.fetchHome(from, to); - const replicasEvents = ( - await Promise.all(replicas.map((r) => this.fetchReplica(r, from, to))) - ).flat(); - const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); - - return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; - }, 7, (error) => this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error.message}`)); - - if (error) throw new Error( - `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error}` + const [fetchedEvents, error] = await retry( + async () => { + const homeEvents = await this.fetchHome(from, to); + const replicasEvents = ( + await Promise.all( + replicas.map((r) => this.fetchReplica(r, from, to)) + ) + ).flat(); + const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); + + return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; + }, + 7, + (error) => + this.orchestrator.logger.warn( + `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error.message}` + ) ); + + if (error) + throw new Error( + `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error}` + ); return fetchedEvents; }; diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index 4613b779e..8f8e77ece 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -107,9 +107,9 @@ export class IndexerCollector extends MetricsCollector { }); this.homeFailedGauge = new Gauge({ - name: 'nomad_monitor_home_failed', - help: 'Gauge that indicates if home of a network is in failed state.', - labelNames: ['network', 'environment'], + name: "nomad_monitor_home_failed", + help: "Gauge that indicates if home of a network is in failed state.", + labelNames: ["network", "environment"], }); } @@ -126,7 +126,7 @@ export class IndexerCollector extends MetricsCollector { relayTime: number, processTime: number, e2eTime: number, - homeFailed: boolean, + homeFailed: boolean ) { this.numDispatchedGauge.set( { network, environment: this.environment }, @@ -170,7 +170,7 @@ export class IndexerCollector extends MetricsCollector { this.homeFailedGauge.set( { network, environment: this.environment }, - homeFailed ? 1 : 0, + homeFailed ? 1 : 0 ); } } diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index 18d28ddba..6f1f85252 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -29,8 +29,10 @@ class HomeHealth { } else { this.healthy = true; } - } catch(e: any) { - this.logger.warn(`Couldn't collect home state for ${this.domain} domain. Error: ${e.message}`); + } catch (e: any) { + this.logger.warn( + `Couldn't collect home state for ${this.domain} domain. Error: ${e.message}` + ); } } @@ -104,7 +106,7 @@ export class Orchestrator { async checkAllHealth() { await Promise.all( this.sdk.domainNumbers.map((domain: number) => this.checkHealth(domain)) - ) + ); } async checkHealth(domain: number) { @@ -139,10 +141,7 @@ export class Orchestrator { while (!this.done) { this.logger.info(`Started to reindex`); const start = new Date().valueOf(); - await Promise.all([ - this.indexAll(), - this.checkAllHealth() - ]) + await Promise.all([this.indexAll(), this.checkAllHealth()]); this.logger.info( `Finished reindexing after ${ (new Date().valueOf() - start) / 1000 diff --git a/tools/indexer/core/utils.ts b/tools/indexer/core/utils.ts index 4d69dac90..838f23e2e 100644 --- a/tools/indexer/core/utils.ts +++ b/tools/indexer/core/utils.ts @@ -14,7 +14,7 @@ export function sleep(ms: number) { export async function retry( callback: () => Promise, tries: number, - onError: ((e: any) => Promise | void) | undefined, + onError: ((e: any) => Promise | void) | undefined ): Promise<[T | undefined, any]> { let timeout = 5000; let lastError: any = undefined; diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml index 29cd449ac..4dde5baf2 100644 --- a/tools/indexer/docker-compose.yml +++ b/tools/indexer/docker-compose.yml @@ -1,6 +1,5 @@ -version: '3.7' +version: "3.7" services: - indexer: build: . restart: always @@ -9,7 +8,7 @@ services: environment: - PROGRAM=core ports: - - '9090:3000' + - "9090:3000" links: - postgres @@ -18,7 +17,7 @@ services: restart: always command: npm run start indexer ports: - - '8080:3000' + - "8080:3000" links: - postgres env_file: .env @@ -36,4 +35,4 @@ services: max-size: 100m max-file: "3" ports: - - '5438:5432' + - "5438:5432" diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts index 9c3eb4195..5d96667a0 100644 --- a/tools/indexer/main.ts +++ b/tools/indexer/main.ts @@ -3,8 +3,8 @@ import * as api from "./api"; import { DB } from "./core/db"; import { createLogger } from "./core/utils"; -export type NomadEnvironment = 'development' | 'staging' | 'production'; -export type Program = 'api' | 'core'; +export type NomadEnvironment = "development" | "staging" | "production"; +export type Program = "api" | "core"; const environment = process.env.ENVIRONMENT! as NomadEnvironment; const program = process.env.PROGRAM! as Program; From 44ced3119790efa83e826b415aa2fc59e6bca933 Mon Sep 17 00:00:00 2001 From: Conner Swann <2635475+yourbuddyconner@users.noreply.github.com> Date: Thu, 27 Jan 2022 17:14:00 -0800 Subject: [PATCH 35/63] pagination fix --- tools/indexer/core/indexer.ts | 73 +++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index ec60d696a..73f7ad924 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -1,5 +1,6 @@ import { Orchestrator } from "./orchestrator"; import { NomadContext } from "@nomad-xyz/sdk/dist"; +import { getEvents } from "@nomad-xyz/sdk/dist/nomad/events/fetch" import fs from "fs"; import { ContractType, EventType, NomadEvent, EventSource } from "./event"; import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; @@ -231,7 +232,15 @@ export class Indexer { const br = this.bridgeRouter(); const allEvents = []; { - const events = await br.queryFilter(br.filters.Send(), from, to); + const events = await getEvents( + this.sdk, + this.domain, + br, + br.filters.Send(), + from, + to + ) + //const events = await br.queryFilter(br.filters.Send(), from, to); const parsedEvents = await Promise.all( events.map(async (event) => { const [ts, senders2hashes] = await this.getBlockInfo( @@ -261,7 +270,15 @@ export class Indexer { } { - const events = await br.queryFilter(br.filters.Receive(), from, to); + const events = await getEvents( + this.sdk, + this.domain, + br, + br.filters.Receive(), + from, + to + ) + //const events = await br.queryFilter(br.filters.Receive(), from, to); const parsedEvents = await Promise.all( events.map( async (event) => @@ -296,7 +313,15 @@ export class Indexer { const home = this.home(); { - const events = await home.queryFilter(home.filters.Dispatch(), from, to); + const events = await getEvents( + this.sdk, + this.domain, + home, + home.filters.Dispatch(), + from, + to + ) + //const events = await home.queryFilter(home.filters.Dispatch(), from, to); const parsedEvents = await Promise.all( events.map( async (event) => @@ -324,7 +349,15 @@ export class Indexer { } { - const events = await home.queryFilter(home.filters.Update(), from, to); + const events = await getEvents( + this.sdk, + this.domain, + home, + home.filters.Update(), + from, + to + ) + //const events = await home.queryFilter(home.filters.Update(), from, to); const parsedEvents = await Promise.all( events.map( async (event) => @@ -358,11 +391,19 @@ export class Indexer { const replica = this.replicaForDomain(domain); { - const events = await replica.queryFilter( - replica.filters.Update(), - from, + // const events = await replica.queryFilter( + // replica.filters.Update(), + // from, + // to + // ); + const events = await getEvents( + this.sdk, + domain, + replica, + replica.filters.Update(), + from, to - ); + ) const parsedEvents = await Promise.all( events.map( async (event) => @@ -389,11 +430,19 @@ export class Indexer { } { - const events = await replica.queryFilter( - replica.filters.Process(), - from, + const events = await getEvents( + this.sdk, + domain, + replica, + replica.filters.Process(), + from, to - ); + ) + // const events = await replica.queryFilter( + // replica.filters.Process(), + // from, + // to + // ); const parsedEvents = await Promise.all( events.map( async (event) => From ba23d0884a2cd985bcd8d5fa2cf7cdd82867a0a1 Mon Sep 17 00:00:00 2001 From: Conner Swann <2635475+yourbuddyconner@users.noreply.github.com> Date: Thu, 27 Jan 2022 18:02:26 -0800 Subject: [PATCH 36/63] limit concurrent number of requests to 20 --- tools/indexer/core/indexer.ts | 29 ++++++++++++++---------- tools/indexer/docker-compose.yml | 3 ++- tools/indexer/package-lock.json | 39 ++++++++++++++++++++++++++++++++ tools/indexer/package.json | 1 + 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 73f7ad924..9427e81fb 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -7,6 +7,11 @@ import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; import { ethers } from "ethers"; import { KVCache, replacer, retry, reviver } from "./utils"; import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; +import pLimit from 'p-limit'; + + +const limit = pLimit(20); + export class Indexer { domain: number; @@ -242,7 +247,7 @@ export class Indexer { ) //const events = await br.queryFilter(br.filters.Send(), from, to); const parsedEvents = await Promise.all( - events.map(async (event) => { + events.map(async (event) => limit(async () => { const [ts, senders2hashes] = await this.getBlockInfo( event.blockNumber ); @@ -264,7 +269,7 @@ export class Indexer { event.blockNumber, EventSource.Fetch ); - }) + })) ); allEvents.push(...parsedEvents); } @@ -281,7 +286,7 @@ export class Indexer { //const events = await br.queryFilter(br.filters.Receive(), from, to); const parsedEvents = await Promise.all( events.map( - async (event) => + async (event) => limit(async () => new NomadEvent( this.domain, EventType.BridgeRouterReceive, @@ -299,7 +304,7 @@ export class Indexer { }, event.blockNumber, EventSource.Fetch - ) + )) ) ); allEvents.push(...parsedEvents); @@ -324,7 +329,7 @@ export class Indexer { //const events = await home.queryFilter(home.filters.Dispatch(), from, to); const parsedEvents = await Promise.all( events.map( - async (event) => + async (event) => limit(async () => new NomadEvent( this.domain, EventType.HomeDispatch, @@ -342,7 +347,7 @@ export class Indexer { }, event.blockNumber, EventSource.Fetch - ) + )) ) ); fetchedEvents.push(...parsedEvents); @@ -360,7 +365,7 @@ export class Indexer { //const events = await home.queryFilter(home.filters.Update(), from, to); const parsedEvents = await Promise.all( events.map( - async (event) => + async (event) => limit(async () => new NomadEvent( this.domain, EventType.HomeUpdate, @@ -377,7 +382,7 @@ export class Indexer { }, event.blockNumber, EventSource.Fetch - ) + )) ) ); fetchedEvents.push(...parsedEvents); @@ -406,7 +411,7 @@ export class Indexer { ) const parsedEvents = await Promise.all( events.map( - async (event) => + async (event) => limit(async () => new NomadEvent( this.domain, EventType.ReplicaUpdate, @@ -423,7 +428,7 @@ export class Indexer { }, event.blockNumber, EventSource.Fetch - ) + )) ) ); fetchedEvents.push(...parsedEvents); @@ -445,7 +450,7 @@ export class Indexer { // ); const parsedEvents = await Promise.all( events.map( - async (event) => + async (event) => limit(async () => new NomadEvent( this.domain, EventType.ReplicaProcess, @@ -461,7 +466,7 @@ export class Indexer { }, event.blockNumber, EventSource.Fetch - ) + )) ) ); fetchedEvents.push(...parsedEvents); diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml index 4dde5baf2..fc29a8d7c 100644 --- a/tools/indexer/docker-compose.yml +++ b/tools/indexer/docker-compose.yml @@ -30,9 +30,10 @@ services: environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=nomad-indexer logging: options: max-size: 100m max-file: "3" ports: - - "5438:5432" + - '5432:5432' diff --git a/tools/indexer/package-lock.json b/tools/indexer/package-lock.json index ec3c3478d..d046714a5 100644 --- a/tools/indexer/package-lock.json +++ b/tools/indexer/package-lock.json @@ -22,6 +22,7 @@ "got": "^11.8.2", "moment": "^2.29.1", "node-pg-migrate": "^6.2.1", + "p-limit": "^3.1.0", "pg": "^8.7.1", "prom-client": "^14.0.0", "request": "^2.88.2", @@ -3573,6 +3574,20 @@ "node": ">=4" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-timeout": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", @@ -5460,6 +5475,17 @@ "engines": { "node": ">=6" } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { @@ -8106,6 +8132,14 @@ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, "p-timeout": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", @@ -9570,6 +9604,11 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } } } diff --git a/tools/indexer/package.json b/tools/indexer/package.json index 1e554d7f3..832c50a93 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -27,6 +27,7 @@ "got": "^11.8.2", "moment": "^2.29.1", "node-pg-migrate": "^6.2.1", + "p-limit": "^3.1.0", "pg": "^8.7.1", "prom-client": "^14.0.0", "request": "^2.88.2", From 4e5e6993f3655e1377f51364a87410a0f48bac82 Mon Sep 17 00:00:00 2001 From: Conner Swann <2635475+yourbuddyconner@users.noreply.github.com> Date: Thu, 27 Jan 2022 18:50:16 -0800 Subject: [PATCH 37/63] concurrent requests per-endpoint --- tools/indexer/core/indexer.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 9427e81fb..d52a216c7 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -10,7 +10,6 @@ import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; -const limit = pLimit(20); export class Indexer { @@ -19,6 +18,8 @@ export class Indexer { orchestrator: Orchestrator; persistance: Persistance; blockCache: KVCache; + limit: pLimit.Limit; + eventCallback: undefined | ((event: NomadEvent) => void); constructor(domain: number, sdk: NomadContext, orchestrator: Orchestrator) { @@ -29,6 +30,8 @@ export class Indexer { `/tmp/persistance_${this.domain}.json` ); this.blockCache = new KVCache(String(this.domain), this.orchestrator.db); + // 20 concurrent requests per indexer + this.limit = pLimit(20); } get provider(): ethers.providers.Provider { @@ -247,7 +250,7 @@ export class Indexer { ) //const events = await br.queryFilter(br.filters.Send(), from, to); const parsedEvents = await Promise.all( - events.map(async (event) => limit(async () => { + events.map(async (event) => this.limit(async () => { const [ts, senders2hashes] = await this.getBlockInfo( event.blockNumber ); @@ -286,7 +289,7 @@ export class Indexer { //const events = await br.queryFilter(br.filters.Receive(), from, to); const parsedEvents = await Promise.all( events.map( - async (event) => limit(async () => + async (event) => this.limit(async () => new NomadEvent( this.domain, EventType.BridgeRouterReceive, @@ -329,7 +332,7 @@ export class Indexer { //const events = await home.queryFilter(home.filters.Dispatch(), from, to); const parsedEvents = await Promise.all( events.map( - async (event) => limit(async () => + async (event) => this.limit(async () => new NomadEvent( this.domain, EventType.HomeDispatch, @@ -365,7 +368,7 @@ export class Indexer { //const events = await home.queryFilter(home.filters.Update(), from, to); const parsedEvents = await Promise.all( events.map( - async (event) => limit(async () => + async (event) => this.limit(async () => new NomadEvent( this.domain, EventType.HomeUpdate, @@ -411,7 +414,7 @@ export class Indexer { ) const parsedEvents = await Promise.all( events.map( - async (event) => limit(async () => + async (event) => this.limit(async () => new NomadEvent( this.domain, EventType.ReplicaUpdate, @@ -450,7 +453,7 @@ export class Indexer { // ); const parsedEvents = await Promise.all( events.map( - async (event) => limit(async () => + async (event) => this.limit(async () => new NomadEvent( this.domain, EventType.ReplicaProcess, From f92fcfe0f5c4710dce55f687773c51f35296f248 Mon Sep 17 00:00:00 2001 From: Conner Swann <2635475+yourbuddyconner@users.noreply.github.com> Date: Thu, 27 Jan 2022 21:00:16 -0800 Subject: [PATCH 38/63] error logs --- tools/indexer/core/indexer.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index d52a216c7..1b169abae 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -31,7 +31,7 @@ export class Indexer { ); this.blockCache = new KVCache(String(this.domain), this.orchestrator.db); // 20 concurrent requests per indexer - this.limit = pLimit(20); + this.limit = pLimit(50); } get provider(): ethers.providers.Provider { @@ -56,12 +56,13 @@ export class Indexer { 7, (error: any) => this.orchestrator.logger.warn( - `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}, error: ${error}` + `Retrying after RPC Error... Block: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` + ) ); if (!block) { throw new Error( - `Some error happened at retrying getting the block ${blockNumber} for ${this.domain}, error: ${error}` + `An RPC foo error occured, retried exhausted. Block: ${blockNumber} Domain: ${this.domain}, Error: ${error}` ); } const time = block.timestamp * 1000; From 5010e32404f083cc8c72648fe64cca315b8a8d63 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Fri, 28 Jan 2022 21:22:20 +0100 Subject: [PATCH 39/63] removed extra pagination --- tools/indexer/core/indexer.ts | 187 +++++++++++++++++----------------- 1 file changed, 95 insertions(+), 92 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 1b169abae..d8f5e71d7 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -10,6 +10,7 @@ import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; +const BATCH_SIZE = process.env.BATCH_SIZE ? parseInt(process.env.BATCH_SIZE) : 2000; export class Indexer { @@ -53,7 +54,7 @@ export class Indexer { const [block, error] = await retry( async () => await this.provider.getBlockWithTransactions(blockNumber), - 7, + 50, (error: any) => this.orchestrator.logger.warn( `Retrying after RPC Error... Block: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` @@ -129,7 +130,7 @@ export class Indexer { return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; }, - 7, + 50, (error) => this.orchestrator.logger.warn( `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error.message}` @@ -145,13 +146,15 @@ export class Indexer { const allEvents = []; - const batchSize = 20000; + const batchSize = BATCH_SIZE; let batchFrom = from; let batchTo = from + batchSize; while (true) { const events = await fetchEvents(batchFrom, batchTo); if (!events) throw new Error(`KEk`); + events.sort((a, b) => a.ts - b.ts); + this.persistance.store(...events); allEvents.push(...events); if (batchTo >= to) break; batchFrom = batchTo + 1; @@ -161,7 +164,6 @@ export class Indexer { if (!allEvents) throw new Error("kek"); allEvents.sort((a, b) => a.ts - b.ts); - this.persistance.store(...allEvents); this.dummyTestEventsIntegrity(); this.orchestrator.logger.info(`Fetched all for domain ${this.domain}`); @@ -171,15 +173,15 @@ export class Indexer { dummyTestEventsIntegrity() { // TODO: either drop or make better - const h = new Map(); + const homeRoots = new Map(); const r = new Map(); - let h1 = ""; - let ht = Number.MAX_VALUE; - let r1 = ""; - let rt = Number.MAX_VALUE; - let rtotal = 0; - let htotal = 0; + let initialHomeRoot = ""; + let initialHomeTimestamp = Number.MAX_VALUE; + let initialReplicaRoot = ""; + let initialReplicaTimestamp = Number.MAX_VALUE; + let homeRootsTotal = 0; + let replicaRootsTotal = 0; let allEvents = this.persistance.allEvents(); if (allEvents.length === 0) { @@ -189,48 +191,48 @@ export class Indexer { for (const event of allEvents) { if (event.eventType == EventType.HomeUpdate) { - const e = event.eventData as { oldRoot: string; newRoot: string }; - h.set(e.oldRoot, e.newRoot); - htotal += 1; - if (event.ts < ht) { - ht = event.ts; - h1 = e.oldRoot; + const { oldRoot, newRoot } = event.eventData as { oldRoot: string; newRoot: string }; + homeRoots.set(oldRoot, newRoot); + homeRootsTotal += 1; + if (event.ts < initialHomeTimestamp) { + initialHomeTimestamp = event.ts; + initialHomeRoot = oldRoot; } } else if (event.eventType == EventType.ReplicaUpdate) { - const e = event.eventData as { oldRoot: string; newRoot: string }; - r.set(e.oldRoot, e.newRoot); - rtotal += 1; - if (event.ts < rt) { - rt = event.ts; - r1 = e.oldRoot; + const { oldRoot, newRoot } = event.eventData as { oldRoot: string; newRoot: string }; + r.set(oldRoot, newRoot); + replicaRootsTotal += 1; + if (event.ts < initialReplicaTimestamp) { + initialReplicaTimestamp = event.ts; + initialReplicaRoot = oldRoot; } } } - if (htotal <= 0) throw new Error(`THis is not supposed to be 0`); - if (rtotal <= 0) throw new Error(`THis is not supposed to be 0`); + if (homeRootsTotal <= 0) throw new Error(`This is not supposed to be 0, but is ${homeRootsTotal}`); + if (replicaRootsTotal <= 0) throw new Error(`This is not supposed to be 0, but is ${replicaRootsTotal}`); while (true) { - let newRoot = h.get(h1); + let newRoot = homeRoots.get(initialHomeRoot); if (newRoot) { - h1 = newRoot; - htotal -= 1; + initialHomeRoot = newRoot; + homeRootsTotal -= 1; } else { break; } } while (true) { - let newRoot = r.get(r1); + let newRoot = r.get(initialReplicaRoot); if (newRoot) { - r1 = newRoot; - rtotal -= 1; + initialReplicaRoot = newRoot; + replicaRootsTotal -= 1; } else { break; } } - if (htotal !== 0) throw new Error(`THis supposed to be 0`); - if (rtotal !== 0) throw new Error(`THis supposed to be 0`); + if (homeRootsTotal !== 0) throw new Error(`This supposed to be 0, but is ${homeRootsTotal}`); + if (replicaRootsTotal !== 0) throw new Error(`This supposed to be 0, but is ${replicaRootsTotal}`); } savePersistance() { @@ -241,15 +243,15 @@ export class Indexer { const br = this.bridgeRouter(); const allEvents = []; { - const events = await getEvents( - this.sdk, - this.domain, - br, - br.filters.Send(), - from, - to - ) - //const events = await br.queryFilter(br.filters.Send(), from, to); + // const events = await getEvents( + // this.sdk, + // this.domain, + // br, + // br.filters.Send(), + // from, + // to + // ) + const events = await br.queryFilter(br.filters.Send(), from, to); const parsedEvents = await Promise.all( events.map(async (event) => this.limit(async () => { const [ts, senders2hashes] = await this.getBlockInfo( @@ -279,15 +281,15 @@ export class Indexer { } { - const events = await getEvents( - this.sdk, - this.domain, - br, - br.filters.Receive(), - from, - to - ) - //const events = await br.queryFilter(br.filters.Receive(), from, to); + // const events = await getEvents( + // this.sdk, + // this.domain, + // br, + // br.filters.Receive(), + // from, + // to + // ) + const events = await br.queryFilter(br.filters.Receive(), from, to); const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => @@ -322,15 +324,15 @@ export class Indexer { const home = this.home(); { - const events = await getEvents( - this.sdk, - this.domain, - home, - home.filters.Dispatch(), - from, - to - ) - //const events = await home.queryFilter(home.filters.Dispatch(), from, to); + // const events = await getEvents( + // this.sdk, + // this.domain, + // home, + // home.filters.Dispatch(), + // from, + // to + // ) + const events = await home.queryFilter(home.filters.Dispatch(), from, to); const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => @@ -358,15 +360,15 @@ export class Indexer { } { - const events = await getEvents( - this.sdk, - this.domain, - home, - home.filters.Update(), - from, - to - ) - //const events = await home.queryFilter(home.filters.Update(), from, to); + // const events = await getEvents( + // this.sdk, + // this.domain, + // home, + // home.filters.Update(), + // from, + // to + // ) + const events = await home.queryFilter(home.filters.Update(), from, to); const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => @@ -400,19 +402,20 @@ export class Indexer { const replica = this.replicaForDomain(domain); { - // const events = await replica.queryFilter( - // replica.filters.Update(), - // from, + const events = await replica.queryFilter( + replica.filters.Update(), + from, + to + ); + // const events = await getEvents( + // this.sdk, + // domain, + // replica, + // replica.filters.Update(), + // from, // to // ); - const events = await getEvents( - this.sdk, - domain, - replica, - replica.filters.Update(), - from, - to - ) + const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => @@ -439,19 +442,19 @@ export class Indexer { } { - const events = await getEvents( - this.sdk, - domain, - replica, - replica.filters.Process(), - from, - to - ) - // const events = await replica.queryFilter( - // replica.filters.Process(), - // from, + // const events = await getEvents( + // this.sdk, + // domain, + // replica, + // replica.filters.Process(), + // from, // to - // ); + // ) + const events = await replica.queryFilter( + replica.filters.Process(), + from, + to + ); const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => From 9b4b809e38a26b6b2a71e6177a5b996ba5ea3570 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Sat, 29 Jan 2022 19:38:46 +0100 Subject: [PATCH 40/63] integrity test for multiple replicas --- tools/indexer/core/indexer.ts | 66 ++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index d8f5e71d7..5dddf513c 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -172,22 +172,20 @@ export class Indexer { } dummyTestEventsIntegrity() { - // TODO: either drop or make better - const homeRoots = new Map(); - const r = new Map(); + let allEvents = this.persistance.allEvents(); + if (allEvents.length === 0) { + this.orchestrator.logger.warn(`No events to test integrity!!!`); + return ; + } + const homeRoots = new Map(); let initialHomeRoot = ""; let initialHomeTimestamp = Number.MAX_VALUE; - let initialReplicaRoot = ""; - let initialReplicaTimestamp = Number.MAX_VALUE; let homeRootsTotal = 0; - let replicaRootsTotal = 0; - let allEvents = this.persistance.allEvents(); - if (allEvents.length === 0) { - this.orchestrator.logger.warn(`No events to test integrity!!!`); - return; - } + type ReplicaDomainInfo = {root: string, ts: number, roots: Map, total: number}; + + const initialReplica: Map = new Map(); for (const event of allEvents) { if (event.eventType == EventType.HomeUpdate) { @@ -200,17 +198,25 @@ export class Indexer { } } else if (event.eventType == EventType.ReplicaUpdate) { const { oldRoot, newRoot } = event.eventData as { oldRoot: string; newRoot: string }; - r.set(oldRoot, newRoot); - replicaRootsTotal += 1; - if (event.ts < initialReplicaTimestamp) { - initialReplicaTimestamp = event.ts; - initialReplicaRoot = oldRoot; + const domain = event.replicaOrigin; + if (!initialReplica.has(domain)) { + initialReplica.set(domain, {root: '', ts: Number.MAX_VALUE, roots: new Map(), total: 0}); + } + const replica = initialReplica.get(domain)!; + replica.roots.set(oldRoot, newRoot); + replica.total += 1; + if (event.ts < replica.ts) { + replica.ts = event.ts; + replica.root = oldRoot; } } } - if (homeRootsTotal <= 0) throw new Error(`This is not supposed to be 0, but is ${homeRootsTotal}`); - if (replicaRootsTotal <= 0) throw new Error(`This is not supposed to be 0, but is ${replicaRootsTotal}`); + if (homeRootsTotal <= 0) throw new Error(`${this.domain}: Total for home is not supposed to be 0, but is ${homeRootsTotal}`); + + for (const [domain, replica] of initialReplica) { + if (replica.total <= 0) throw new Error(`${this.domain}: Total for replica ${domain} is not supposed to be 0, but is ${replica.total}`); + } while (true) { let newRoot = homeRoots.get(initialHomeRoot); @@ -221,18 +227,22 @@ export class Indexer { break; } } - while (true) { - let newRoot = r.get(initialReplicaRoot); - if (newRoot) { - initialReplicaRoot = newRoot; - replicaRootsTotal -= 1; - } else { - break; + if (homeRootsTotal !== 0) throw new Error(`${this.domain}: Left roots for home supposed to be 0, but is ${homeRootsTotal}`); + + for (const [domain, replica] of initialReplica) { + let initialReplicaRoot = replica.root; + let replicaRootsTotal = replica.total; + while (true) { + let newRoot = replica.roots.get(initialReplicaRoot); + if (newRoot) { + initialReplicaRoot = newRoot; + replicaRootsTotal -= 1; + } else { + break; + } } + if (replicaRootsTotal !== 0) throw new Error(`${this.domain}: Left roots for replica ${domain} supposed to be 0, but is ${replicaRootsTotal} replica for domain ${domain}`) } - - if (homeRootsTotal !== 0) throw new Error(`This supposed to be 0, but is ${homeRootsTotal}`); - if (replicaRootsTotal !== 0) throw new Error(`This supposed to be 0, but is ${replicaRootsTotal}`); } savePersistance() { From e6891dd0372de6298683b4b94b3380883b3cb9c2 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Sat, 29 Jan 2022 21:12:50 +0100 Subject: [PATCH 41/63] column length --- tools/indexer/core/db.ts | 22 ++++++++++++------- .../1643485834250_increased-column-size.js | 11 ++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 tools/indexer/migrations/1643485834250_increased-column-size.js diff --git a/tools/indexer/core/db.ts b/tools/indexer/core/db.ts index 47a3be416..9dd9bb4c2 100644 --- a/tools/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -172,15 +172,21 @@ export class DB { } async insertMessage(messages: NomadMessage[]) { - const rows = messages.length; - if (!rows) return; + if (!messages.length) return; const columns = 25; - const query = `INSERT INTO messages (hash, origin, destination, nonce, nomad_sender, nomad_recipient, root, state, dispatched_at, updated_at, relayed_at, received_at, processed_at, bridge_msg_type, recipient, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id, sender, raw, leaf_index, block, evm) VALUES ${expand( - rows, - columns - )};`; - const values = messages.map((m) => m.intoDB()).flat(); - return await this.pool.query(query, values); + const batchSize = 2000; + + do { + const batch = messages.splice(0, batchSize); + const query = `INSERT INTO messages (hash, origin, destination, nonce, nomad_sender, nomad_recipient, root, state, dispatched_at, updated_at, relayed_at, received_at, processed_at, bridge_msg_type, recipient, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id, sender, raw, leaf_index, block, evm) VALUES ${expand( + batch.length, + columns + )};`; + const values = batch.map((m) => m.intoDB()).flat(); + await this.pool.query(query, values); + } while (messages.length > 0); + + return } async updateMessage(messages: NomadMessage[]) { diff --git a/tools/indexer/migrations/1643485834250_increased-column-size.js b/tools/indexer/migrations/1643485834250_increased-column-size.js new file mode 100644 index 000000000..3f5fbc6c4 --- /dev/null +++ b/tools/indexer/migrations/1643485834250_increased-column-size.js @@ -0,0 +1,11 @@ +/* eslint-disable camelcase */ + +exports.shorthands = undefined; + +exports.up = pgm => { + pgm.alterColumn('messages', 'bridge_msg_type', {type: 'varchar(20)', notNull: false}); +}; + +exports.down = pgm => { + pgm.alterColumn('messages', 'bridge_msg_type', {type: 'varchar(14)', notNull: false}); +}; From 9a510fb2c117084701d5311f3d446e2c69b6631f Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Sun, 30 Jan 2022 10:37:35 +0100 Subject: [PATCH 42/63] retries everywhere --- tools/indexer/core/indexer.ts | 133 ++++++++++++++++++++++++---------- 1 file changed, 96 insertions(+), 37 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 5dddf513c..29b2970c3 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -11,6 +11,7 @@ import pLimit from 'p-limit'; const BATCH_SIZE = process.env.BATCH_SIZE ? parseInt(process.env.BATCH_SIZE) : 2000; +const RETRIES = 100; export class Indexer { @@ -54,7 +55,7 @@ export class Indexer { const [block, error] = await retry( async () => await this.provider.getBlockWithTransactions(blockNumber), - 50, + RETRIES, (error: any) => this.orchestrator.logger.warn( `Retrying after RPC Error... Block: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` @@ -118,9 +119,7 @@ export class Indexer { ); const fetchEvents = async (from: number, to: number) => { - const [fetchedEvents, error] = await retry( - async () => { - const homeEvents = await this.fetchHome(from, to); + const homeEvents = await this.fetchHome(from, to); const replicasEvents = ( await Promise.all( replicas.map((r) => this.fetchReplica(r, from, to)) @@ -128,20 +127,22 @@ export class Indexer { ).flat(); const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); - return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; - }, - 50, - (error) => - this.orchestrator.logger.warn( - `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error.message}` - ) - ); + // const [fetchedEvents, error] = await retry( + // async () => { + + // }, + // 50, + // (error) => + // this.orchestrator.logger.warn( + // `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error.message}` + // ) + // ); - if (error) - throw new Error( - `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error}` - ); - return fetchedEvents; + // if (error) + // throw new Error( + // `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error}` + // ); + return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; }; const allEvents = []; @@ -230,18 +231,18 @@ export class Indexer { if (homeRootsTotal !== 0) throw new Error(`${this.domain}: Left roots for home supposed to be 0, but is ${homeRootsTotal}`); for (const [domain, replica] of initialReplica) { - let initialReplicaRoot = replica.root; - let replicaRootsTotal = replica.total; + let root = replica.root; + let total = replica.total; while (true) { - let newRoot = replica.roots.get(initialReplicaRoot); + let newRoot = replica.roots.get(root); if (newRoot) { - initialReplicaRoot = newRoot; - replicaRootsTotal -= 1; + root = newRoot; + total -= 1; } else { break; } } - if (replicaRootsTotal !== 0) throw new Error(`${this.domain}: Left roots for replica ${domain} supposed to be 0, but is ${replicaRootsTotal} replica for domain ${domain}`) + if (total !== 0) throw new Error(`${this.domain}: Left roots for replica ${domain} supposed to be 0, but is ${total} replica for domain ${domain}`) } } @@ -261,7 +262,16 @@ export class Indexer { // from, // to // ) - const events = await br.queryFilter(br.filters.Send(), from, to); + const [events, error] = await retry(async () => { + return await br.queryFilter(br.filters.Send(), from, to); + }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + if (error) { + this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + throw error + } + if (events === undefined) { + throw new Error(`There is no error, but events for some reason are still undefined`); + } const parsedEvents = await Promise.all( events.map(async (event) => this.limit(async () => { const [ts, senders2hashes] = await this.getBlockInfo( @@ -299,7 +309,17 @@ export class Indexer { // from, // to // ) - const events = await br.queryFilter(br.filters.Receive(), from, to); + + const [events, error] = await retry(async () => { + return await br.queryFilter(br.filters.Receive(), from, to); + }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + if (error) { + this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + throw error + } + if (events === undefined) { + throw new Error(`There is no error, but events for some reason are still undefined`); + } const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => @@ -342,7 +362,17 @@ export class Indexer { // from, // to // ) - const events = await home.queryFilter(home.filters.Dispatch(), from, to); + const [events, error] = await retry(async () => { + return await home.queryFilter(home.filters.Dispatch(), from, to); + }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + if (error) { + this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + throw error + } + if (events === undefined) { + throw new Error(`There is no error, but events for some reason are still undefined`); + } + const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => @@ -378,7 +408,17 @@ export class Indexer { // from, // to // ) - const events = await home.queryFilter(home.filters.Update(), from, to); + const [events, error] = await retry(async () => { + return await home.queryFilter(home.filters.Update(), from, to); + }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + if (error) { + this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + throw error + } + if (events === undefined) { + throw new Error(`There is no error, but events for some reason are still undefined`); + } + const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => @@ -412,11 +452,20 @@ export class Indexer { const replica = this.replicaForDomain(domain); { - const events = await replica.queryFilter( - replica.filters.Update(), - from, - to - ); + const [events, error] = await retry(async () => { + return await replica.queryFilter( + replica.filters.Update(), + from, + to + ); + }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + if (error) { + this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + throw error + } + if (events === undefined) { + throw new Error(`There is no error, but events for some reason are still undefined`); + } // const events = await getEvents( // this.sdk, // domain, @@ -460,11 +509,21 @@ export class Indexer { // from, // to // ) - const events = await replica.queryFilter( - replica.filters.Process(), - from, - to - ); + const [events, error] = await retry(async () => { + return await replica.queryFilter( + replica.filters.Process(), + from, + to + ); + }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + if (error) { + this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + throw error + } + if (events === undefined) { + throw new Error(`There is no error, but events for some reason are still undefined`); + } + const parsedEvents = await Promise.all( events.map( async (event) => this.limit(async () => From 76e64b35e1a6990424600f11d7304aacd5cecd99 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 31 Jan 2022 07:54:31 +0100 Subject: [PATCH 43/63] retries, timeout, refetch on failed integrity --- tools/indexer/core/consumer.ts | 2 - tools/indexer/core/event.ts | 31 +++++++++++ tools/indexer/core/indexer.ts | 98 +++++++++++++++++----------------- tools/indexer/core/utils.ts | 2 +- 4 files changed, 80 insertions(+), 53 deletions(-) diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index 43524b8f4..cf8ee644a 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -493,7 +493,6 @@ class SenderLostAndFound { const some = this.dispatchEventsWithMessages.at(index); if (some) { const [_, msg] = some; - // console.log(`Set sender to ${brSend.eventData.from!} for message: ${msg.hash}`) msg.updateSender(brSend.eventData.from!); msg.evm = brSend.eventData.evmHash!; this.dispatchEventsWithMessages.splice(index, 1); @@ -693,7 +692,6 @@ export class Processor extends Consumer { bridgeRouterSend(e: NomadEvent) { const hash = this.senderRegistry.bridgeRouterSend(e); if (hash) { - // console.log(`Setting bridgeRouterSend for `, this.getMsg(hash)?.sender) this.addToSyncQueue(hash); } } diff --git a/tools/indexer/core/event.ts b/tools/indexer/core/event.ts index d982f5347..8877f0d8d 100644 --- a/tools/indexer/core/event.ts +++ b/tools/indexer/core/event.ts @@ -1,4 +1,5 @@ import { ethers } from "ethers"; +import { hash } from "./utils"; export enum ContractType { Home = "home", @@ -45,6 +46,32 @@ export type EventData = { evmHash?: string; }; +export function uniqueHash(d: EventData): string { + return hash( + d.messageHash || 'undefined', + d.leafIndex?.toHexString() || 'undefined', + d.destinationAndNonce?.toHexString() || 'undefined', + d.committedRoot || 'undefined', + d.oldRoot || 'undefined', + d.newRoot || 'undefined', + d.success ? 'true' : 'false', + d.returnData?.toString() || 'undefined', + d.message || 'undefined', + d.signature || 'undefined', + d.homeDomain?.toString() || 'undefined', + d.token || 'undefined', + d.from || 'undefined', + d.toDomain?.toString() || 'undefined', + d.toId || 'undefined', + d.amount?.toHexString() || 'undefined', + d.fastLiquidityEnabled ? 'true' : 'false', + d.originAndNonce?.toHexString() || 'undefined', + d.recipient || 'undefined', + d.liquidityProvider || 'undefined', + d.evmHash || 'undefined', + ) +} + export class NomadEvent { domain: number; eventType: EventType; @@ -137,6 +164,10 @@ export class NomadEvent { EventSource.Storage ); } + + uniqueHash(): string { + return hash(this.domain.toString(), this.eventType, this.replicaOrigin.toString(), this.block.toString(), uniqueHash(this.eventData)) + } } function parseDestinationAndNonce( diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 29b2970c3..3576def36 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -33,7 +33,7 @@ export class Indexer { ); this.blockCache = new KVCache(String(this.domain), this.orchestrator.db); // 20 concurrent requests per indexer - this.limit = pLimit(50); + this.limit = pLimit(100); } get provider(): ethers.providers.Provider { @@ -54,12 +54,13 @@ export class Indexer { } const [block, error] = await retry( - async () => await this.provider.getBlockWithTransactions(blockNumber), + async () => { + return await this.limit(() => this.provider.getBlockWithTransactions(blockNumber)) + }, RETRIES, (error: any) => this.orchestrator.logger.warn( `Retrying after RPC Error... Block: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` - ) ); if (!block) { @@ -109,7 +110,7 @@ export class Indexer { async updateAll(replicas: number[]) { let from = Math.max( - this.persistance.height + 1, + this.persistance.height, this.sdk.getDomain(this.domain)?.paginate?.from || 0 ); const to = await this.provider.getBlockNumber(); @@ -118,45 +119,44 @@ export class Indexer { `Fetching events for domain ${this.domain} from: ${from}, to: ${to}` ); - const fetchEvents = async (from: number, to: number) => { + const fetchEvents = async (from: number, to: number): Promise => { const homeEvents = await this.fetchHome(from, to); - const replicasEvents = ( - await Promise.all( - replicas.map((r) => this.fetchReplica(r, from, to)) - ) - ).flat(); - const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); - - // const [fetchedEvents, error] = await retry( - // async () => { - - // }, - // 50, - // (error) => - // this.orchestrator.logger.warn( - // `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error.message}` - // ) - // ); + const replicasEvents = ( + await Promise.all( + replicas.map((r) => this.fetchReplica(r, from, to)) + ) + ).flat(); + const bridgeRouterEvents = await this.fetchBridgeRouter(from, to); - // if (error) - // throw new Error( - // `Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${error}` - // ); return [...homeEvents, ...replicasEvents, ...bridgeRouterEvents]; }; - const allEvents = []; + const allEvents: NomadEvent[] = []; const batchSize = BATCH_SIZE; let batchFrom = from; let batchTo = from + batchSize; while (true) { + this.orchestrator.logger.info( + `Fetching batch of events for domain ${this.domain} from: ${batchFrom}, to: ${batchTo}` + ); const events = await fetchEvents(batchFrom, batchTo); if (!events) throw new Error(`KEk`); events.sort((a, b) => a.ts - b.ts); this.persistance.store(...events); - allEvents.push(...events); + try { + this.dummyTestEventsIntegrity(batchTo); + this.orchestrator.logger.info(`Integrity test PASSED for ${this.domain} between ${batchFrom} and ${batchTo}`); + } catch(e) { + const pastFrom = batchFrom; + const pastTo = batchTo; + batchFrom = batchFrom - batchSize/2; + batchTo = batchFrom + batchSize; + this.orchestrator.logger.warn(`Integrity test not passed for ${this.domain} between ${pastFrom} and ${pastTo}, recollecting between ${batchFrom} and ${batchTo},: ${e}`); + continue; + } + allEvents.push(...events.filter(newEvent => allEvents.every(oldEvent => newEvent.uniqueHash() !== oldEvent.uniqueHash()))); if (batchTo >= to) break; batchFrom = batchTo + 1; batchTo = Math.min(to, batchFrom + batchSize); @@ -172,8 +172,9 @@ export class Indexer { return allEvents; } - dummyTestEventsIntegrity() { + dummyTestEventsIntegrity(blockTo?: number) { let allEvents = this.persistance.allEvents(); + if (blockTo) allEvents = allEvents.filter(e => e.block <= blockTo); if (allEvents.length === 0) { this.orchestrator.logger.warn(`No events to test integrity!!!`); return ; @@ -213,12 +214,6 @@ export class Indexer { } } - if (homeRootsTotal <= 0) throw new Error(`${this.domain}: Total for home is not supposed to be 0, but is ${homeRootsTotal}`); - - for (const [domain, replica] of initialReplica) { - if (replica.total <= 0) throw new Error(`${this.domain}: Total for replica ${domain} is not supposed to be 0, but is ${replica.total}`); - } - while (true) { let newRoot = homeRoots.get(initialHomeRoot); if (newRoot) { @@ -273,7 +268,7 @@ export class Indexer { throw new Error(`There is no error, but events for some reason are still undefined`); } const parsedEvents = await Promise.all( - events.map(async (event) => this.limit(async () => { + events.map(async (event) => { const [ts, senders2hashes] = await this.getBlockInfo( event.blockNumber ); @@ -295,7 +290,7 @@ export class Indexer { event.blockNumber, EventSource.Fetch ); - })) + }) ); allEvents.push(...parsedEvents); } @@ -322,7 +317,7 @@ export class Indexer { } const parsedEvents = await Promise.all( events.map( - async (event) => this.limit(async () => + async (event) => new NomadEvent( this.domain, EventType.BridgeRouterReceive, @@ -342,7 +337,7 @@ export class Indexer { EventSource.Fetch )) ) - ); + ; allEvents.push(...parsedEvents); } @@ -375,7 +370,7 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( - async (event) => this.limit(async () => + async (event) => new NomadEvent( this.domain, EventType.HomeDispatch, @@ -395,7 +390,7 @@ export class Indexer { EventSource.Fetch )) ) - ); + ; fetchedEvents.push(...parsedEvents); } @@ -421,7 +416,7 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( - async (event) => this.limit(async () => + async (event) => new NomadEvent( this.domain, EventType.HomeUpdate, @@ -440,7 +435,7 @@ export class Indexer { EventSource.Fetch )) ) - ); + ; fetchedEvents.push(...parsedEvents); } @@ -477,7 +472,7 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( - async (event) => this.limit(async () => + async (event) => new NomadEvent( this.domain, EventType.ReplicaUpdate, @@ -496,7 +491,7 @@ export class Indexer { EventSource.Fetch )) ) - ); + ; fetchedEvents.push(...parsedEvents); } @@ -526,7 +521,7 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( - async (event) => this.limit(async () => + async (event) => new NomadEvent( this.domain, EventType.ReplicaProcess, @@ -544,7 +539,7 @@ export class Indexer { EventSource.Fetch )) ) - ); + ; fetchedEvents.push(...parsedEvents); } return fetchedEvents; @@ -586,19 +581,22 @@ export class RamPersistance extends Persistance { } store(...events: NomadEvent[]): void { - events.forEach((event) => { - this.updateFromTo(event.block); + for (const event of events) { const block = this.block2events.get(event.block); if (block) { + if (block.some(e => e.uniqueHash() === event.uniqueHash())) { + continue; + } block.push(event); } else { this.block2events.set(event.block, [event]); } + this.updateFromTo(event.block); if (this.blocks.indexOf(event.block) < 0) { this.blocks.push(event.block); this.blocks = this.blocks.sort(); } - }); + }; this.persist(); } async init(): Promise { diff --git a/tools/indexer/core/utils.ts b/tools/indexer/core/utils.ts index 838f23e2e..2a86a0099 100644 --- a/tools/indexer/core/utils.ts +++ b/tools/indexer/core/utils.ts @@ -16,7 +16,7 @@ export async function retry( tries: number, onError: ((e: any) => Promise | void) | undefined ): Promise<[T | undefined, any]> { - let timeout = 5000; + let timeout = 2000; let lastError: any = undefined; for (let attempt = 0; attempt < tries; attempt++) { try { From e7ce901650844183f7b5269f38ca6ff06c48e880 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 31 Jan 2022 08:04:26 +0100 Subject: [PATCH 44/63] debug logs --- tools/indexer/core/indexer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 3576def36..40ead5618 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -138,7 +138,7 @@ export class Indexer { let batchTo = from + batchSize; while (true) { - this.orchestrator.logger.info( + this.orchestrator.logger.debug( `Fetching batch of events for domain ${this.domain} from: ${batchFrom}, to: ${batchTo}` ); const events = await fetchEvents(batchFrom, batchTo); @@ -147,7 +147,7 @@ export class Indexer { this.persistance.store(...events); try { this.dummyTestEventsIntegrity(batchTo); - this.orchestrator.logger.info(`Integrity test PASSED for ${this.domain} between ${batchFrom} and ${batchTo}`); + this.orchestrator.logger.debug(`Integrity test PASSED for ${this.domain} between ${batchFrom} and ${batchTo}`); } catch(e) { const pastFrom = batchFrom; const pastTo = batchTo; From 2a62a4652fe42e2b9526791aaddf0b4b2c8f246b Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 31 Jan 2022 18:25:53 +0100 Subject: [PATCH 45/63] chore: env.exmaple, docker-compose --- tools/indexer/.env.example | 1 + tools/indexer/docker-compose.yml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/tools/indexer/.env.example b/tools/indexer/.env.example index d9263ed67..3dfb77395 100644 --- a/tools/indexer/.env.example +++ b/tools/indexer/.env.example @@ -3,6 +3,7 @@ MOONBEAM_RPC=https://moonbeam.api.onfinality.io/public ENVIRONMENT=development # or production or staging PORT=3000 +BATCH_SIZE=5000 PGHOST=pg1 PGUSER=postgres diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml index fc29a8d7c..353fca319 100644 --- a/tools/indexer/docker-compose.yml +++ b/tools/indexer/docker-compose.yml @@ -9,6 +9,8 @@ services: - PROGRAM=core ports: - "9090:3000" + depends_on: + - "postgres" links: - postgres @@ -18,11 +20,15 @@ services: command: npm run start indexer ports: - "8080:3000" + expose: + - 8080 links: - postgres env_file: .env environment: - PROGRAM=api + depends_on: + - "postgres" postgres: image: postgres:latest @@ -37,3 +43,5 @@ services: max-file: "3" ports: - '5432:5432' + expose: + - 5432 From d12f4cab1e2bc6dfb2b906105b59a74e8ac07903 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 1 Feb 2022 18:11:46 +0100 Subject: [PATCH 46/63] chore: wrapped getBlockNumber retry --- tools/indexer/core/indexer.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 40ead5618..dd17ab99b 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -113,7 +113,21 @@ export class Indexer { this.persistance.height, this.sdk.getDomain(this.domain)?.paginate?.from || 0 ); - const to = await this.provider.getBlockNumber(); + const [to, error] = await retry( + async () => { + return await this.provider.getBlockNumber() + }, + RETRIES, + (error: any) => + this.orchestrator.logger.warn( + `Retrying after RPC Error on .getBlockNumber() method... Error: ${error}` + ) + ); + if (!to) { + throw new Error( + `Retrying .getBlockNumber() method... exhausted maximum retry count. Throwing: ${error}` + ); + } this.orchestrator.logger.info( `Fetching events for domain ${this.domain} from: ${from}, to: ${to}` From 5a0037ba7222c17827f84fcd28ae379559338d0b Mon Sep 17 00:00:00 2001 From: Daniil Naumetc <11177808+kekonen@users.noreply.github.com> Date: Thu, 3 Feb 2022 23:37:30 +0100 Subject: [PATCH 47/63] Daniil/tx monitor prisma (#156) * feature: prisma * feature: cleaned db naming * feature: naming and fixes --- tools/indexer/.gitignore | 3 + tools/indexer/Dockerfile | 12 +- tools/indexer/api/index.ts | 7 +- tools/indexer/core/consumer.ts | 340 ++++++++--------- tools/indexer/core/db.ts | 347 ++++++++---------- tools/indexer/core/indexer.ts | 2 +- tools/indexer/core/utils.ts | 38 +- tools/indexer/docker-compose.yml | 2 + .../1642713407510_my-first-migration.js | 63 ---- .../1643485834250_increased-column-size.js | 11 - tools/indexer/package-lock.json | 81 +++- tools/indexer/package.json | 14 +- .../20220203202333_init/migration.sql | 54 +++ .../prisma/migrations/migration_lock.toml | 3 + tools/indexer/prisma/schema.prisma | 50 +++ 15 files changed, 557 insertions(+), 470 deletions(-) create mode 100644 tools/indexer/.gitignore delete mode 100644 tools/indexer/migrations/1642713407510_my-first-migration.js delete mode 100644 tools/indexer/migrations/1643485834250_increased-column-size.js create mode 100644 tools/indexer/prisma/migrations/20220203202333_init/migration.sql create mode 100644 tools/indexer/prisma/migrations/migration_lock.toml create mode 100644 tools/indexer/prisma/schema.prisma diff --git a/tools/indexer/.gitignore b/tools/indexer/.gitignore new file mode 100644 index 000000000..11ddd8dbe --- /dev/null +++ b/tools/indexer/.gitignore @@ -0,0 +1,3 @@ +node_modules +# Keep environment variables out of version control +.env diff --git a/tools/indexer/Dockerfile b/tools/indexer/Dockerfile index 076e8ce7c..902700ed1 100644 --- a/tools/indexer/Dockerfile +++ b/tools/indexer/Dockerfile @@ -5,6 +5,9 @@ WORKDIR /app COPY package.json ./package.json COPY package-lock.json ./package-lock.json + + +# START should delete later COPY nomad-xyz-sdk-1.2.4.tgz ./nomad-xyz-sdk-1.2.4.tgz RUN npm i @@ -12,10 +15,15 @@ RUN npm i RUN npm i ./nomad-xyz-sdk-1.2.4.tgz # RUN rm ./nomad-xyz-sdk-1.2.4.tgz +# END should delete later + COPY api ./api -COPY core ./core -COPY migrations ./migrations COPY main.ts ./main.ts +COPY prisma ./prisma +RUN npm run build + +COPY core ./core + CMD [ "npm", "run", "start" ] \ No newline at end of file diff --git a/tools/indexer/api/index.ts b/tools/indexer/api/index.ts index e54cb7a8a..26cdf18db 100644 --- a/tools/indexer/api/index.ts +++ b/tools/indexer/api/index.ts @@ -27,13 +27,14 @@ export async function run(db: DB, logger: Logger) { }); app.get("/tx/:tx", log, async (req, res) => { - const message = await db.getMessageByEvm(req.params.tx); - return res.json(message.toObject()); + const messages = await db.getMessageByEvm(req.params.tx); + return res.json(messages.map(m => m.serialize())); }); app.get("/hash/:hash", log, async (req, res) => { const message = await db.getMessageByHash(req.params.hash); - return res.json(message.toObject()); + if (!message) return res.status(404).json({}); + return res.json(message.serialize()); }); app.get( diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index cf8ee644a..c6ab7c39d 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -1,11 +1,12 @@ import { parseMessage } from "@nomad-xyz/sdk/dist/nomad/messages/NomadMessage"; -import { ethers } from "ethers"; +import { BigNumber, ethers } from "ethers"; import { EventType, NomadEvent } from "./event"; import { Statistics } from "./types"; -import { parseBody } from "@nomad-xyz/sdk/dist/nomad/messages/BridgeMessage"; +import { parseBody, ParsedTransferMessage } from "@nomad-xyz/sdk/dist/nomad/messages/BridgeMessage"; import { parseAction } from "@nomad-xyz/sdk/dist/nomad/messages/GovernanceMessage"; import { DB } from "./db"; import Logger from "bunyan"; +import { Padded } from "./utils"; class StatisticsCollector { s: Statistics; @@ -224,10 +225,31 @@ class Timings { } return undefined; } -} -function bytes32ToAddress(s: string) { - return "0x" + s.slice(26); + serialize() { + return { + dispatchedAt: this.dispatchedAt, + updatedAt: this.updatedAt, + relayedAt: this.relayedAt, + processedAt: this.processedAt, + receivedAt: this.receivedAt, + } + } + + static deserialize(s: { + dispatchedAt: number; + updatedAt: number; + relayedAt: number; + processedAt: number; + receivedAt: number; +}): Timings { + const t = new Timings(s.dispatchedAt); + t.updatedAt = s.updatedAt; + t.relayedAt = s.relayedAt; + t.processedAt = s.processedAt; + t.receivedAt = s.receivedAt; + return t; + } } enum MessageType { @@ -236,142 +258,181 @@ enum MessageType { GovernanceMessage, } +export type MinimumSerializedNomadMessage = { + origin: number,// m.origin, + destination: number,// m.destination, + nonce: number,// m.nonce, + root: string,// m.root, + messageHash: string,// m.hash, + leafIndex: string,// BigNumber.from(m.leaf_index), + body: string,// m.raw, + dispatchBlock: number,// m.block, + dispatchedAt: number,// Number(m.dispatched_at), + updatedAt: number,// Number(m.updated_at), + relayedAt: number,// Number(m.relayed_at), + receivedAt: number,// Number(m.received_at), + processedAt: number,// Number(m.processed_at), + sender: string | null,// m.sender || '', + tx: string | null,// m.evm || '' + state: MsgState, +} + +export type ExtendedSerializedNomadMessage = MinimumSerializedNomadMessage & { + internalSender: string,// PADDED! // internalSender: this.internalSender, + internalRecipient: string,// PADDED! // internalRecipient: this.internalRecipient, + // hasMessage: MessageType | null,// hasMessage: this.hasMessage, + // bridgeMsgType: this.transferMessage.action.type, + recipient: string | null,// PADDED!// bridgeMsgTo: this.recipient(), // PADDED! + amount: string | null,// bridgeMsgAmount: this.transferMessage.action.amount.toHexString(), + allowFast: boolean | null,// bridgeMsgAllowFast: this.transferMessage.action.allowFast, + detailsHash: string | null,// bridgeMsgDetailsHash: this.transferMessage.action.detailsHash, + tokenDomain: number | null,// bridgeMsgTokenDomain: this.tokenDomain(), + tokenId: string | null,// PADDED! // bridgeMsgTokenId: this.tokenId(), // PADDED! +} + export class NomadMessage { origin: number; destination: number; nonce: number; root: string; - hash: string; + messageHash: string; leafIndex: ethers.BigNumber; - raw: string; sender?: string; - nomadSender: string; - nomadRecipient: string; + internalSender: Padded; // PADDED! + internalRecipient: Padded; // PADDED! + + body: string; hasMessage: MessageType; - bridgeMsgType?: string; - bridgeMsgTo?: string; - bridgeMsgAmount?: ethers.BigNumber; - bridgeMsgAllowFast?: boolean; - bridgeMsgDetailsHash?: string; - bridgeMsgTokenDomain?: number; - bridgeMsgTokenId?: string; + transferMessage?: ParsedTransferMessage; + state: MsgState; + dispatchBlock: number; + tx?: string; + timings: Timings; - block: number; - evm?: string; constructor( origin: number, destination: number, nonce: number, root: string, - hash: string, + messageHash: string, leafIndex: ethers.BigNumber, // destinationAndNonce: ethers.BigNumber, - message: string, - createdAt: number, - block: number + body: string, + dispatchedAt: number, + dispatchBlock: number ) { this.origin = origin; this.destination = destination; this.nonce = nonce; this.root = root.toLowerCase(); - this.hash = hash.toLowerCase(); + this.messageHash = messageHash.toLowerCase(); this.leafIndex = leafIndex; - // this.destinationAndNonce = destinationAndNonce; - this.raw = message; - const parsed = parseMessage(message); - this.nomadSender = bytes32ToAddress(parsed.sender); - this.nomadRecipient = bytes32ToAddress(parsed.recipient); + + this.body = body; + const parsed = parseMessage(body); + this.internalSender = new Padded(parsed.sender); // PADDED! + this.internalRecipient = new Padded(parsed.recipient); // PADDED! this.hasMessage = MessageType.NoMessage; this.tryParseMessage(parsed.body); this.state = MsgState.Dispatched; - this.timings = new Timings(createdAt); - this.block = block; + this.timings = new Timings(dispatchedAt); + this.dispatchBlock = dispatchBlock; + } + + // PADDED! + /** + * PADDED! + */ + recipient(): Padded | undefined { + return this.transferMessage ? new Padded(this.transferMessage!.action.to) : undefined + } + + // PADDED! + /** + * PADDED! + */ + tokenId(): Padded | undefined { + return this.transferMessage ? new Padded(this.transferMessage!.token.id as string) : undefined + } + + tokenDomain(): number | undefined { + return this.transferMessage ? this.transferMessage?.token.domain as number : undefined } - toObject() { + amount(): BigNumber | undefined { + return this.transferMessage ? this.transferMessage?.action.amount : undefined + } + + allowFast(): boolean | undefined { + return this.transferMessage ? this.transferMessage?.action.allowFast : undefined + } + + detailsHash(): string | undefined { + return this.transferMessage ? this.transferMessage?.action.detailsHash : undefined + } + + + + + static deserialize(s: MinimumSerializedNomadMessage) { + const m = new NomadMessage( + s.origin, + s.destination, + s.nonce, + s.root, + s.messageHash, + BigNumber.from(s.leafIndex), + s.body, + s.dispatchedAt, + s.dispatchBlock + ); + m.timings.updated(s.updatedAt); + m.timings.relayed(s.relayedAt); + m.timings.received(s.receivedAt); + m.timings.processed(s.processedAt); + m.sender = s.sender || undefined; + m.tx = s.tx || undefined; + m.state = s.state; + return m; + } + + serialize(): ExtendedSerializedNomadMessage { return { origin: this.origin, destination: this.destination, nonce: this.nonce, root: this.root, - hash: this.hash, - leafIndex: this.leafIndex, - sender: this.sender, - nomadSender: this.nomadSender, - nomadRecipient: this.nomadRecipient, - hasMessage: this.hasMessage, - bridgeMsgType: this.bridgeMsgType, - bridgeMsgTo: this.bridgeMsgTo, - bridgeMsgAmount: this.bridgeMsgAmount, - bridgeMsgAllowFast: this.bridgeMsgAllowFast, - bridgeMsgDetailsHash: this.bridgeMsgDetailsHash, - bridgeMsgTokenDomain: this.bridgeMsgTokenDomain, - bridgeMsgTokenId: this.bridgeMsgTokenId, + messageHash: this.messageHash, + leafIndex: this.leafIndex.toHexString(), + sender: this.sender || null, state: this.state, - timings: this.timings, - tx: this.evm, + ...this.timings.serialize(), + tx: this.tx || null, + body: this.body, + dispatchBlock: this.dispatchBlock, + internalSender: this.internalSender.valueOf(), + internalRecipient: this.internalRecipient.valueOf(), + // hasMessage: this.hasMessage, + recipient: this.recipient()?.valueOf() || null, + amount: this.amount()?.toHexString() || null, + allowFast: this.allowFast() || null, + detailsHash: this.detailsHash() || null, + tokenDomain: this.tokenDomain() || null, + tokenId: this.tokenId()?.valueOf() || null, }; } - static fromDB( - origin: number, - destination: number, - nonce: number, - root: string, - hash: string, - leafIndex: ethers.BigNumber, - message: string, - createdAt: number, - updatedAt: number, - relayedAt: number, - receivedAt: number, - processedAt: number, - block: number, - sender: string, - evm: string - ): NomadMessage { - const m = new NomadMessage( - origin, - destination, - nonce, - root, - hash, - leafIndex, - message, - createdAt, - block - ); - m.timings.updated(updatedAt); - m.timings.relayed(relayedAt); - m.timings.received(receivedAt); - m.timings.processed(processedAt); - m.updateSender(sender); - m.evm = evm; - return m; - } - tryParseMessage(body: string) { this.tryParseTransferMessage(body) || this.tryParseGovernanceMessage(body); } tryParseTransferMessage(body: string): boolean { try { - const bridgeMessage = parseBody(body); - this.bridgeMsgType = bridgeMessage.action.type as string; - this.bridgeMsgTo = bytes32ToAddress( - bridgeMessage.action.to - ).toLowerCase(); - this.bridgeMsgAmount = bridgeMessage.action.amount; - this.bridgeMsgAllowFast = bridgeMessage.action.allowFast; - this.bridgeMsgDetailsHash = bridgeMessage.action.detailsHash; - this.bridgeMsgTokenDomain = bridgeMessage.token.domain as number; - this.bridgeMsgTokenId = bytes32ToAddress( - bridgeMessage.token.id as string - ).toLowerCase(); + this.transferMessage = parseBody(body); this.hasMessage = MessageType.TransferMessage; return true; } catch (e) { @@ -388,7 +449,7 @@ export class NomadMessage { message.address; message.domain; } - this.bridgeMsgType = message.type; + // this.bridgeMsgType = message.type; this.hasMessage = MessageType.GovernanceMessage; return true; } catch (e) { @@ -396,69 +457,11 @@ export class NomadMessage { } } - updateSender(sender: string) { - this.sender = sender; - } get originAndRoot(): string { return `${this.origin}${this.root}`; } - intoDB(): [ - string, - number, - number, - number, - string, - string, - string, - number, - number, - number, - number, - number, - number, - string | undefined, - string | undefined, - string | undefined, - boolean | undefined, - string | undefined, - number | undefined, - string | undefined, - string | undefined, - string, - string, - number, - string | undefined - ] { - return [ - this.hash.toLowerCase(), - this.origin, - this.destination, - this.nonce, - this.nomadSender.toLowerCase(), - this.nomadRecipient.toLowerCase(), - this.root.toLowerCase(), - this.state, - this.timings.dispatchedAt, - this.timings.updatedAt, - this.timings.relayedAt, - this.timings.receivedAt, - this.timings.processedAt, - this.bridgeMsgType?.toLowerCase(), - this.bridgeMsgTo?.toLowerCase(), - this.bridgeMsgAmount?.toString(), - this.bridgeMsgAllowFast, - this.bridgeMsgDetailsHash?.toLowerCase(), - this.bridgeMsgTokenDomain, - this.bridgeMsgTokenId?.toLowerCase(), - this.sender?.toLowerCase(), - this.raw, - this.leafIndex.toString(), - this.block, - this.evm?.toLowerCase(), - ]; - } } class SenderLostAndFound { @@ -493,10 +496,10 @@ class SenderLostAndFound { const some = this.dispatchEventsWithMessages.at(index); if (some) { const [_, msg] = some; - msg.updateSender(brSend.eventData.from!); - msg.evm = brSend.eventData.evmHash!; + msg.sender = brSend.eventData.from!; + msg.tx = brSend.eventData.evmHash!; this.dispatchEventsWithMessages.splice(index, 1); - return msg.hash; + return msg.messageHash; } } return undefined; @@ -505,9 +508,9 @@ class SenderLostAndFound { match(dispatch: NomadEvent, brSend: NomadEvent, m: NomadMessage): boolean { return ( brSend.eventData.toDomain! === m.destination && //brSend.eventData.token?.toLowerCase() === m.bridgeMsgTokenId?.toLowerCase() && - bytes32ToAddress(brSend.eventData.toId!).toLowerCase() === - m.bridgeMsgTo?.toLowerCase() && - brSend.eventData.amount!.eq(m.bridgeMsgAmount!) && + new Padded(brSend.eventData.toId!).toEVMAddress() === + m.recipient()!.toEVMAddress() && + brSend.eventData.amount!.eq(m.amount()!) && brSend.block === dispatch.block //&& // (dispatch.block - brSend.block <= 2 || brSend.block - dispatch.block <= 30) ); } @@ -522,8 +525,8 @@ class SenderLostAndFound { if (index >= 0) { const brSend = this.bridgeRouterSendEvents.at(index); if (brSend) { - m.updateSender(brSend.eventData.from!); - m.evm = brSend.eventData.evmHash!; + m.sender = brSend.eventData.from!; + m.tx = brSend.eventData.evmHash!; } this.bridgeRouterSendEvents.splice(index, 1); return true; @@ -637,7 +640,6 @@ export class Processor extends Consumer { e.eventData.committedRoot!, e.eventData.messageHash!, e.eventData.leafIndex!, - // e.eventData.destinationAndNonce!, e.eventData.message!, e.ts, e.block @@ -646,7 +648,7 @@ export class Processor extends Consumer { this.senderRegistry.dispatch(e, m); this.add(m); - this.addToSyncQueue(m.hash); + this.addToSyncQueue(m.messageHash); if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } @@ -658,7 +660,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Updated) { m.state = MsgState.Updated; m.timings.updated(e.ts); - this.addToSyncQueue(m.hash); + this.addToSyncQueue(m.messageHash); } }); } @@ -673,7 +675,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Relayed) { m.state = MsgState.Relayed; m.timings.relayed(e.ts); - this.addToSyncQueue(m.hash); + this.addToSyncQueue(m.messageHash); } }); } @@ -684,7 +686,7 @@ export class Processor extends Consumer { if (m.state < MsgState.Processed) { m.state = MsgState.Processed; m.timings.processed(e.ts); - this.addToSyncQueue(m.hash); + this.addToSyncQueue(m.messageHash); } } } @@ -703,14 +705,14 @@ export class Processor extends Consumer { if (m.state < MsgState.Received) { m.state = MsgState.Received; m.timings.received(e.ts); - this.addToSyncQueue(m.hash); + this.addToSyncQueue(m.messageHash); } } } add(m: NomadMessage) { const index = this.messages.length; - this.msgToIndex.set(m.hash, index); + this.msgToIndex.set(m.messageHash, index); const msgByOriginAndRoot = this.msgByOriginAndRoot.get(m.originAndRoot); if (msgByOriginAndRoot) { msgByOriginAndRoot.push(index); diff --git a/tools/indexer/core/db.ts b/tools/indexer/core/db.ts index 9dd9bb4c2..7c0ff1d8a 100644 --- a/tools/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -1,20 +1,42 @@ import { NomadMessage } from "./consumer"; -import { Pool } from "pg"; - -// expand(3, 2) returns "($1, $2), ($3, $4), ($5, $6)" -function expand(rowCount: number, columnCount: number, startAt = 1) { - var index = startAt; - return Array(rowCount) - .fill(0) - .map( - (v) => - `(${Array(columnCount) - .fill(0) - .map((v) => `$${index++}`) - .join(", ")})` - ) - .join(", "); -} + +import { messages, Prisma, PrismaClient } from '@prisma/client' +import { BigNumber } from "ethers"; + +// function fromDb(m: messages): NomadMessage { +// return + +// } + +// function toDb(m: NomadMessage): Prisma.messagesCreateManyInput { +// return { +// hash: m.hash, +// origin: m.origin, +// destination: m.destination, +// nonce: m.nonce, +// nomad_sender: m.nomadSender, +// nomad_recipient: m.nomadRecipient, +// root: m.root, +// state: m.state, +// block: m.block, +// dispatched_at: m.timings.dispatchedAt, +// updated_at: m.timings.updatedAt, +// relayed_at: m.timings.relayedAt, +// received_at: m.timings.receivedAt, +// processed_at: m.timings.processedAt, +// sender: m.sender, +// bridge_msg_type: m.bridgeMsgType, +// recipient: m.bridgeMsgTo, +// bridge_msg_amount: m.bridgeMsgAmount?.toHexString() || undefined, +// bridge_msg_allow_fast: m.bridgeMsgAllowFast, +// bridge_msg_details_hash: m.bridgeMsgDetailsHash, +// bridge_msg_token_domain: m.bridgeMsgTokenDomain, +// bridge_msg_token_id: m.bridgeMsgTokenId, +// raw: m.raw, +// leaf_index: m.leafIndex.toHexString(), +// evm: m.evm, +// } +// } export interface MsgRequest { size?: number; @@ -25,49 +47,21 @@ export interface MsgRequest { sender?: string; } -class Contr { - args: string[]; - offset: number; - constructor(offset: number) { - this.args = []; - this.offset = offset; - } - - add(arg: string) { - if (!arg.match(/[a-z]+/)) throw new Error(`Can add only a-z characters`); - this.args.push(arg); - } - - static fromReq(r: MsgRequest, offset = 0): string { - const c = new Contr(offset); - if (r.sender) c.add("sender"); - if (r.recipient) c.add("recipient"); - if (r.origin) c.add("origin"); - if (r.destination) c.add("destination"); - return c.construct(); - } - - construct(): string { - return this.args.length - ? "where " + - this.args - .map((a, i) => `${a} = $${i + 1 + this.offset}`) - .join(" and ") - : ""; - } -} export class DB { - pool: Pool; + client: PrismaClient; syncedOnce: boolean; constructor() { this.syncedOnce = false; - this.pool = new Pool(); + this.client = new PrismaClient(); } async connect() { - await this.pool.connect(); + } + + async disconnect() { + await this.client.$disconnect(); } get startupSync() { @@ -76,177 +70,134 @@ export class DB { return !value; } - async getMessageByEvm(tx: string): Promise { - const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, dispatched_at, updated_at, relayed_at, received_at, processed_at, hash FROM messages where evm = $1 order by dispatched_at desc;`; - const result = await this.pool.query(query, [tx.toLowerCase()]); - const entry = result.rows[0]; - return NomadMessage.fromDB( - entry.origin, - entry.destination, - entry.nonce, - entry.root, - entry.hash, - entry.leaf_index, - entry.raw, - entry.block, - entry.dispatched_at, - entry.updated_at, - entry.relayed_at, - entry.received_at, - entry.processed_at, - entry.sender, - tx - ); + async getMessageByEvm(tx: string): Promise { + const messages = await this.client.messages.findMany({ + where: { + tx + } + }); + + return messages.map(NomadMessage.deserialize) } - async getMessageByHash(hash: string): Promise { - const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages where hash = $1 order by dispatched_at desc;`; - const result = await this.pool.query(query, [hash.toLowerCase()]); - const entry = result.rows[0]; - return NomadMessage.fromDB( - entry.origin, - entry.destination, - entry.nonce, - entry.root, - hash, - entry.leaf_index, - entry.raw, - entry.block, - entry.dispatched_at, - entry.updated_at, - entry.relayed_at, - entry.received_at, - entry.processed_at, - entry.sender, - entry.evm - ); + async getMessageByHash(messageHash: string): Promise { + const message = await this.client.messages.findUnique({ + where: { + messageHash + } + }); + + return message ? NomadMessage.deserialize(message) : undefined } async getMessages(req: MsgRequest): Promise { - const limit = req.size || 15; + const take = req.size || 15; const page = req.page || 1; - const offset = (page || -1) * limit; - const args: any[] = [limit, offset]; - - const c = new Contr(args.length); - - if (req.sender) { - c.add("sender"); - args.push(req.sender.toLowerCase()); - } - if (req.recipient) { - c.add("recipient"); - args.push(req.recipient.toLowerCase()); - } - if (req.origin) { - c.add("origin"); - args.push(req.origin); - } - if (req.destination) { - c.add("destination"); - args.push(req.destination); - } - - const query = `SELECT origin, destination, nonce, root, leaf_index, raw, block, sender, hash, evm, dispatched_at, updated_at, relayed_at, received_at, processed_at FROM messages ${c.construct()} order by dispatched_at desc limit $1 offset $2;`; - const result = await this.pool.query(query, args); - - return result.rows.map((entry) => { - return NomadMessage.fromDB( - entry.origin, - entry.destination, - entry.nonce, - entry.root, - entry.hash, - entry.leaf_index, - entry.raw, - entry.block, - entry.dispatched_at, - entry.updated_at, - entry.relayed_at, - entry.received_at, - entry.processed_at, - entry.sender, - entry.evm - ); + const skip = (page || -1) * take; + + const messages = await this.client.messages.findMany({ + where: { + sender: req.sender, + recipient: req.recipient, + origin: req.origin, + destination: req.destination, + }, + take, + skip }); + + return messages.map(NomadMessage.deserialize) } async insertMessage(messages: NomadMessage[]) { if (!messages.length) return; - const columns = 25; - const batchSize = 2000; - - do { - const batch = messages.splice(0, batchSize); - const query = `INSERT INTO messages (hash, origin, destination, nonce, nomad_sender, nomad_recipient, root, state, dispatched_at, updated_at, relayed_at, received_at, processed_at, bridge_msg_type, recipient, bridge_msg_amount, bridge_msg_allow_fast, bridge_msg_details_hash, bridge_msg_token_domain, bridge_msg_token_id, sender, raw, leaf_index, block, evm) VALUES ${expand( - batch.length, - columns - )};`; - const values = batch.map((m) => m.intoDB()).flat(); - await this.pool.query(query, values); - } while (messages.length > 0); - return + return await this.client.messages.createMany({ + data: messages.map(message => message.serialize()), + skipDuplicates: true, + }) } async updateMessage(messages: NomadMessage[]) { - const rows = messages.length; - if (!rows) return; - const promises = messages.map((m) => { - const query = `UPDATE messages SET - origin = $2, - destination = $3, - nonce = $4, - nomad_sender = $5, - nomad_recipient = $6, - root = $7, - state = $8, - dispatched_at = $9, - updated_at = $10, - relayed_at = $11, - received_at = $12, - processed_at = $13, - bridge_msg_type = $14, - recipient = $15, - bridge_msg_amount = $16, - bridge_msg_allow_fast = $17, - bridge_msg_details_hash = $18, - bridge_msg_token_domain = $19, - bridge_msg_token_id = $20, - sender = $21, - raw = $22, - leaf_index = $23, - block = $24, - evm = $25 - WHERE hash = $1 - `; - return this.pool.query(query, m.intoDB()); - }); + if (!messages.length) return; - return await Promise.all(promises); + return await Promise.all(messages.map(m => { + this.client.messages.update({ + where: { + messageHash: m.messageHash + }, + data: m.serialize(), + }) + })); } async getExistingHashes(): Promise { - const res = await this.pool.query(`select hash from messages;`); - return res.rows.map((r) => r.hash) as string[]; + const rows = await this.client.messages.findMany({ + select: { + messageHash: true + } + }); + return rows.map(row => row.messageHash) } async getAllKeyPair(namespace: string): Promise> { - const res = await this.pool.query( - `select key, value from kv_storage where namespace = $1;`, - [namespace] - ); - return new Map(res.rows.map((r) => [r.key, r.value])); + const rows = await this.client.kv_storage.findMany({ + select: { + key: true, + value: true + }, + where: { + namespace + } + }); + return new Map(rows.map(row => [row.key, row.value])) + } + + async getKeyPair(namespace: string, key: string): Promise { + const row = await this.client.kv_storage.findUnique({ + select: { + value: true + }, + where: { + namespace_key: { + namespace, + key + } + } + }); + if (row) return row.value; + return undefined } - async setKeyPair(namespace: string, k: string, v: string): Promise { - await this.pool.query( - `INSERT INTO kv_storage (namespace, key, value) - VALUES($1,$2,$3) - ON CONFLICT (namespace, key) - DO - UPDATE SET value = $3;`, - [namespace, k, v] - ); + async setKeyPair(namespace: string, key: string, value: string): Promise { + const where: Prisma.kv_storageWhereUniqueInput = { + namespace_key: { + namespace, key + } + }; + + const create: Prisma.kv_storageCreateInput = { + namespace, key, value + }; + const update: Prisma.kv_storageUpdateInput = { + value + }; + await this.client.kv_storage.upsert({ + where, + update, + create, + }) + + // const found = await this.getKeyPair(namespace, key); + // if (found) { + // await this.client.kv_storage.update({ + // where, + // data: update, + // }) + // } else { + // await this.client.kv_storage.create({ + // data: create + // }) + // } } } diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index dd17ab99b..ac14ef6ac 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -43,7 +43,7 @@ export class Indexer { async getBlockInfo( blockNumber: number ): Promise<[number, Map]> { - const possibleBlock = this.blockCache.get(String(blockNumber)); + const possibleBlock = await this.blockCache.get(String(blockNumber)); if (possibleBlock) { const [ts, txs] = possibleBlock.split("."); const x: string[] = txs.split(","); diff --git a/tools/indexer/core/utils.ts b/tools/indexer/core/utils.ts index 2a86a0099..b688438cd 100644 --- a/tools/indexer/core/utils.ts +++ b/tools/indexer/core/utils.ts @@ -4,6 +4,7 @@ import fs from "fs"; import { Mean } from "./types"; import { DB } from "./db"; import Logger from "bunyan"; +import pLimit from 'p-limit'; export function sleep(ms: number) { return new Promise((resolve) => { @@ -69,33 +70,26 @@ export function reviver(key: any, value: any): any { } export class KVCache { - m: Map; name: string; db: DB; + limit: pLimit.Limit; constructor(name: string, db: DB) { this.db = db; - this.m = new Map(); this.name = name; + this.limit = pLimit(1); } async init() { - await this.tryLoad(); - } - - async tryLoad() { - try { - this.m = await this.db.getAllKeyPair(this.name); - } catch (_) {} + // await this.tryLoad(); } async set(k: string, v: string) { - this.m.set(k, v); - await this.db.setKeyPair(this.name, k, v); + await this.limit(() => this.db.setKeyPair(this.name, k, v)); } - get(k: string): string | undefined { - return this.m.get(k); + async get(k: string): Promise { + return await this.db.getKeyPair(this.name, k) } } @@ -119,3 +113,21 @@ export function createLogger(name: string, environment: string) { environment: environment, }); } + +export class Padded { + private s: string; + + constructor(s: string) { + if (s.length !== 66) throw new Error(`Input string length must be 66, got: ${s.length}`); + if (s.slice(0, 2) !== '0x') throw new Error(`Input string length must start with '0x', got: ${s}`); + this.s = s.toLowerCase(); + } + + toEVMAddress() { + return "0x" + this.s.slice(26); + } + + valueOf(): string { + return this.s; + } +} diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml index 353fca319..aa2b2ba05 100644 --- a/tools/indexer/docker-compose.yml +++ b/tools/indexer/docker-compose.yml @@ -7,6 +7,7 @@ services: env_file: .env environment: - PROGRAM=core + - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres?connect_timeout=300&schema=public ports: - "9090:3000" depends_on: @@ -27,6 +28,7 @@ services: env_file: .env environment: - PROGRAM=api + - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres?connect_timeout=300&schema=public depends_on: - "postgres" diff --git a/tools/indexer/migrations/1642713407510_my-first-migration.js b/tools/indexer/migrations/1642713407510_my-first-migration.js deleted file mode 100644 index 112dea592..000000000 --- a/tools/indexer/migrations/1642713407510_my-first-migration.js +++ /dev/null @@ -1,63 +0,0 @@ -/* eslint-disable camelcase */ - -exports.shorthands = undefined; - -exports.up = (pgm) => { - pgm.createTable("messages", { - id: "id", - hash: { type: "varchar(66)", notNull: true, unique: true }, // Nomad TX id - origin: { type: "integer", notNull: true }, - destination: { type: "integer", notNull: true }, - nonce: { type: "integer", notNull: true }, - nomad_sender: { type: "varchar(42)", notNull: true }, // Nomad sender (bridge router) - nomad_recipient: { type: "varchar(42)", notNull: true }, // Nomad recipient (bridge router) - root: { type: "varchar(66)", notNull: true }, - state: { type: "integer", notNull: true }, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) - block: { type: "integer", notNull: true }, // one of several states: Dispatched(0), Updated(1), Relayed(2), Processed(3) - dispatched_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state dispatched - updated_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state updated - relayed_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state relayed - received_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state received - processed_at: { type: "bigint", notNull: true }, // TS at which the transaction got to state processed - // Bridge message internals - sender: { type: "varchar(42)", notNull: false }, // sender - bridge_msg_type: { type: "varchar(14)", notNull: false }, // Mostly 'transaction' - recipient: { type: "varchar(42)", notNull: false }, // Real recipient on destination domain - bridge_msg_amount: { type: "varchar(256)", notNull: false }, // Amount of Token - bridge_msg_allow_fast: { type: "boolean", notNull: false }, // Allow fast? - bridge_msg_details_hash: { type: "varchar(66)", notNull: false }, // Details hash - don't know what it is (need to do homework) - bridge_msg_token_domain: { type: "integer", notNull: false }, // Token domain - bridge_msg_token_id: { type: "varchar(42)", notNull: false }, // Token id (address) - raw: { type: "varchar", notNull: true }, - leaf_index: { type: "varchar(256)", notNull: true }, - evm: { type: "varchar(66)", notNull: false }, - createdAt: { - type: "timestamp", - notNull: true, - default: pgm.func("current_timestamp"), - }, - }); - pgm.createIndex("messages", "id"); - - pgm.createTable("kv_storage", { - id: "id", - namespace: { type: "varchar", notNull: true }, - key: { type: "varchar", notNull: true }, - value: { type: "varchar", notNull: true }, - createdAt: { - type: "timestamp", - notNull: true, - default: pgm.func("current_timestamp"), - }, - }); - pgm.createIndex("kv_storage", "id"); - pgm.addConstraint("kv_storage", "unique_namespace_key", { - unique: ["namespace", "key"], - }); -}; - -exports.down = (pgm) => { - pgm.dropTable("messages"); - pgm.dropConstraint("kv_storage", "unique_namespace_key"); - pgm.dropTable("kv_storage"); -}; diff --git a/tools/indexer/migrations/1643485834250_increased-column-size.js b/tools/indexer/migrations/1643485834250_increased-column-size.js deleted file mode 100644 index 3f5fbc6c4..000000000 --- a/tools/indexer/migrations/1643485834250_increased-column-size.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable camelcase */ - -exports.shorthands = undefined; - -exports.up = pgm => { - pgm.alterColumn('messages', 'bridge_msg_type', {type: 'varchar(20)', notNull: false}); -}; - -exports.down = pgm => { - pgm.alterColumn('messages', 'bridge_msg_type', {type: 'varchar(14)', notNull: false}); -}; diff --git a/tools/indexer/package-lock.json b/tools/indexer/package-lock.json index d046714a5..56fe61798 100644 --- a/tools/indexer/package-lock.json +++ b/tools/indexer/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@nomad-xyz/contract-interfaces": "1.1.1", "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.2.4.tgz", + "@prisma/client": "^3.9.1", "@types/bunyan": "^1.8.7", "@types/express": "^4.17.13", "@types/node-pg-migrate": "^2.3.1", @@ -30,7 +31,8 @@ "typescript": "^4.4.3" }, "devDependencies": { - "prettier": "^2.4.1" + "prettier": "^2.4.1", + "prisma": "^3.9.1" } }, "../../typescript/nomad-sdk": { @@ -852,6 +854,38 @@ "web3": "^1.6.1" } }, + "node_modules/@prisma/client": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.9.1.tgz", + "integrity": "sha512-aLwfXKLvL+loQ0IuPPCXkcq8cXBg1IeoHHa5lqQu3dJHdj45wnislA/Ny4UxRQjD5FXqrfAb8sWtF+jhdmjFTg==", + "hasInstallScript": true, + "dependencies": { + "@prisma/engines-version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" + }, + "engines": { + "node": ">=12.6" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/engines": { + "version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009.tgz", + "integrity": "sha512-qM+uJbkelB21bnK44gYE049YTHIjHysOuj0mj5U2gDGyNLfmiazlggzFPCgEjgme4U5YB2tYs6Z5Hq08Kl8pjA==", + "devOptional": true, + "hasInstallScript": true + }, + "node_modules/@prisma/engines-version": { + "version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009.tgz", + "integrity": "sha512-5Dh+qTDhpPR66w6NNAnPs+/W/Qt4r1DSd+qhfPFcDThUK4uxoZKGlPb2IYQn5LL+18aIGnmteDf7BnVMmvBNSQ==" + }, "node_modules/@sindresorhus/is": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", @@ -3803,6 +3837,23 @@ "node": ">=0.8" } }, + "node_modules/prisma": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.9.1.tgz", + "integrity": "sha512-IGcJAu5LzlFv+i+NNhOEh1J1xVVttsVdRBxmrMN7eIH+7mRN6L89Hz1npUAiz4jOpNlHC7n9QwaOYZGxTqlwQw==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" + }, + "bin": { + "prisma": "build/index.js", + "prisma2": "build/index.js" + }, + "engines": { + "node": ">=12.6" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -5959,6 +6010,25 @@ "web3": "^1.6.1" } }, + "@prisma/client": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-3.9.1.tgz", + "integrity": "sha512-aLwfXKLvL+loQ0IuPPCXkcq8cXBg1IeoHHa5lqQu3dJHdj45wnislA/Ny4UxRQjD5FXqrfAb8sWtF+jhdmjFTg==", + "requires": { + "@prisma/engines-version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" + } + }, + "@prisma/engines": { + "version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009.tgz", + "integrity": "sha512-qM+uJbkelB21bnK44gYE049YTHIjHysOuj0mj5U2gDGyNLfmiazlggzFPCgEjgme4U5YB2tYs6Z5Hq08Kl8pjA==", + "devOptional": true + }, + "@prisma/engines-version": { + "version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009.tgz", + "integrity": "sha512-5Dh+qTDhpPR66w6NNAnPs+/W/Qt4r1DSd+qhfPFcDThUK4uxoZKGlPb2IYQn5LL+18aIGnmteDf7BnVMmvBNSQ==" + }, "@sindresorhus/is": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", @@ -8297,6 +8367,15 @@ "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz", "integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==" }, + "prisma": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.9.1.tgz", + "integrity": "sha512-IGcJAu5LzlFv+i+NNhOEh1J1xVVttsVdRBxmrMN7eIH+7mRN6L89Hz1npUAiz4jOpNlHC7n9QwaOYZGxTqlwQw==", + "devOptional": true, + "requires": { + "@prisma/engines": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" + } + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", diff --git a/tools/indexer/package.json b/tools/indexer/package.json index 832c50a93..db58d0ecc 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -8,33 +8,29 @@ "core": "PROGRAMM=core ts-node main", "api": "PROGRAMM=api ts-node main", "any": "ts-node main", + "build": "prisma generate", "start": "npm run migrate && npm run any", - "migrate": "node-pg-migrate up", + "migrate": "prisma db push", "release-docker": "docker buildx build --platform linux/amd64,linux/arm64 --push -t gcr.io/nomad-xyz/nomad-indexer:sha-$(git rev-parse --short HEAD) .", "get_sdk": "cd ../../typescript/nomad-sdk && npm i && npm run build && npm pack && cd ../../tools/indexer && mv ../../typescript/nomad-sdk/nomad-xyz-sdk-1.2.4.tgz ." }, "dependencies": { "@nomad-xyz/contract-interfaces": "1.1.1", "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.2.4.tgz", + "@prisma/client": "^3.9.1", "@types/bunyan": "^1.8.7", "@types/express": "^4.17.13", - "@types/node-pg-migrate": "^2.3.1", - "@types/pg": "^8.6.4", "bunyan": "^1.8.15", "dotenv": "^10.0.0", "ethers": "^5.4.6", "express": "^4.17.2", - "got": "^11.8.2", - "moment": "^2.29.1", - "node-pg-migrate": "^6.2.1", "p-limit": "^3.1.0", - "pg": "^8.7.1", "prom-client": "^14.0.0", - "request": "^2.88.2", + "prettier": "^2.4.1", + "prisma": "^3.9.1", "ts-node": "^10.4.0", "typescript": "^4.4.3" }, "devDependencies": { - "prettier": "^2.4.1" } } diff --git a/tools/indexer/prisma/migrations/20220203202333_init/migration.sql b/tools/indexer/prisma/migrations/20220203202333_init/migration.sql new file mode 100644 index 000000000..c8072099b --- /dev/null +++ b/tools/indexer/prisma/migrations/20220203202333_init/migration.sql @@ -0,0 +1,54 @@ +-- CreateTable +CREATE TABLE "kv_storage" ( + "id" SERIAL NOT NULL, + "namespace" VARCHAR NOT NULL, + "key" VARCHAR NOT NULL, + "value" VARCHAR NOT NULL, + "createdAt" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "kv_storage_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "messages" ( + "id" SERIAL NOT NULL, + "message_hash" VARCHAR(66) NOT NULL, + "origin" INTEGER NOT NULL, + "destination" INTEGER NOT NULL, + "nonce" INTEGER NOT NULL, + "internal_sender" VARCHAR(42) NOT NULL, + "internal_recipient" VARCHAR(42) NOT NULL, + "root" VARCHAR(66) NOT NULL, + "state" INTEGER NOT NULL, + "dispatchBlock" INTEGER NOT NULL, + "dispatched_at" INTEGER NOT NULL, + "updated_at" INTEGER NOT NULL, + "relayed_at" INTEGER NOT NULL, + "received_at" INTEGER NOT NULL, + "processed_at" INTEGER NOT NULL, + "sender" VARCHAR(42), + "recipient" VARCHAR(42), + "amount" VARCHAR(256), + "allow_fast" BOOLEAN, + "details_hash" VARCHAR(66), + "token_domain" INTEGER, + "token_id" VARCHAR(42), + "body" VARCHAR NOT NULL, + "leaf_index" VARCHAR(256) NOT NULL, + "tx" VARCHAR(66), + "created_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "messages_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "kv_storage_id_index" ON "kv_storage"("id"); + +-- CreateIndex +CREATE UNIQUE INDEX "unique_namespace_key" ON "kv_storage"("namespace", "key"); + +-- CreateIndex +CREATE UNIQUE INDEX "messages_message_hash_key" ON "messages"("message_hash"); + +-- CreateIndex +CREATE INDEX "messages_id_index" ON "messages"("id"); diff --git a/tools/indexer/prisma/migrations/migration_lock.toml b/tools/indexer/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000..fbffa92c2 --- /dev/null +++ b/tools/indexer/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/tools/indexer/prisma/schema.prisma b/tools/indexer/prisma/schema.prisma new file mode 100644 index 000000000..6bf4b3cc7 --- /dev/null +++ b/tools/indexer/prisma/schema.prisma @@ -0,0 +1,50 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model kv_storage { + id Int @id @default(autoincrement()) + namespace String @db.VarChar + key String @db.VarChar + value String @db.VarChar + createdAt DateTime @default(now()) @db.Timestamp(6) + + @@unique([namespace, key], map: "unique_namespace_key") + @@index([id], map: "kv_storage_id_index") +} + +model messages { + id Int @id @default(autoincrement()) + messageHash String @map("message_hash") @unique @db.VarChar(66) + origin Int + destination Int + nonce Int + internalSender String @map("internal_sender") @db.VarChar(66) + internalRecipient String @map("internal_recipient") @db.VarChar(66) + root String @db.VarChar(66) + state Int + dispatchBlock Int + dispatchedAt Int @map("dispatched_at") + updatedAt Int @map("updated_at") + relayedAt Int @map("relayed_at") + receivedAt Int @map("received_at") + processedAt Int @map("processed_at") + sender String? @db.VarChar(66) + recipient String? @db.VarChar(66) + amount String? @db.VarChar(256) + allowFast Boolean? @map("allow_fast") + detailsHash String? @map("details_hash") @db.VarChar(66) + tokenDomain Int? @map("token_domain") + tokenId String? @map("token_id") @db.VarChar(66) + body String @db.VarChar + leafIndex String @map("leaf_index") @db.VarChar(256) + tx String? @db.VarChar(66) + createdAt DateTime @map("created_at") @default(now()) @db.Timestamp(6) + + @@index([id], map: "messages_id_index") +} From e7a61bba0aa1af758fd07ee35ecec8404c9df4bc Mon Sep 17 00:00:00 2001 From: Daniil Naumetc <11177808+kekonen@users.noreply.github.com> Date: Sun, 6 Feb 2022 20:35:20 +0100 Subject: [PATCH 48/63] Daniil/tx monitor prisma (#178) * feature: prisma * feature: cleaned db naming * feature: naming and fixes * chore: serialize * fix: timestamps are seconds in db --- tools/indexer/api/index.ts | 2 +- tools/indexer/core/consumer.ts | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/indexer/api/index.ts b/tools/indexer/api/index.ts index 26cdf18db..e74a95a3a 100644 --- a/tools/indexer/api/index.ts +++ b/tools/indexer/api/index.ts @@ -47,7 +47,7 @@ export async function run(db: DB, logger: Logger) { const messages = await db.getMessages(req.query); - return res.json(messages); + return res.json(messages.map(m => m.serialize())); } ); diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index c6ab7c39d..a9aff39ec 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -228,11 +228,11 @@ class Timings { serialize() { return { - dispatchedAt: this.dispatchedAt, - updatedAt: this.updatedAt, - relayedAt: this.relayedAt, - processedAt: this.processedAt, - receivedAt: this.receivedAt, + dispatchedAt: this.dispatchedAt/1000, + updatedAt: this.updatedAt/1000, + relayedAt: this.relayedAt/1000, + processedAt: this.processedAt/1000, + receivedAt: this.receivedAt/1000, } } @@ -243,11 +243,11 @@ class Timings { processedAt: number; receivedAt: number; }): Timings { - const t = new Timings(s.dispatchedAt); - t.updatedAt = s.updatedAt; - t.relayedAt = s.relayedAt; - t.processedAt = s.processedAt; - t.receivedAt = s.receivedAt; + const t = new Timings(Number(s.dispatchedAt)*1000); + t.updatedAt = Number(s.updatedAt)*1000; + t.relayedAt = Number(s.relayedAt)*1000; + t.processedAt = Number(s.processedAt)*1000; + t.receivedAt = Number(s.receivedAt)*1000; return t; } } From a85dbaa98ddf47d7f1fb4acd4af6f22faf35544e Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 7 Feb 2022 15:40:09 +0100 Subject: [PATCH 49/63] chore: log and docker-compose --- tools/indexer/core/indexer.ts | 3 ++- tools/indexer/docker-compose.yml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index ac14ef6ac..ed8b560da 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -152,8 +152,9 @@ export class Indexer { let batchTo = from + batchSize; while (true) { + const done = Math.floor((batchTo-from)/(to-from) * 100); this.orchestrator.logger.debug( - `Fetching batch of events for domain ${this.domain} from: ${batchFrom}, to: ${batchTo}` + `Fetching batch of events for domain ${this.domain} from: ${batchFrom}, to: ${batchTo}, [${done}%]` ); const events = await fetchEvents(batchFrom, batchTo); if (!events) throw new Error(`KEk`); diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml index aa2b2ba05..772a2c056 100644 --- a/tools/indexer/docker-compose.yml +++ b/tools/indexer/docker-compose.yml @@ -39,6 +39,8 @@ services: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres - POSTGRES_DB=nomad-indexer + volumes: + - ./postgres-data:/var/lib/postgresql/data logging: options: max-size: 100m From 892759dde5ec8ccf0d3128be0a55c691c8a3308b Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 8 Feb 2022 16:53:22 +0100 Subject: [PATCH 50/63] feature: hostgrams --- tools/indexer/.gitignore | 1 + tools/indexer/core/consumer.ts | 16 +- tools/indexer/core/metrics.ts | 52 +- tools/indexer/core/orchestrator.ts | 36 +- tools/indexer/package-lock.json | 1599 +++++----------------------- tools/indexer/package.json | 10 +- 6 files changed, 384 insertions(+), 1330 deletions(-) diff --git a/tools/indexer/.gitignore b/tools/indexer/.gitignore index 11ddd8dbe..32323003e 100644 --- a/tools/indexer/.gitignore +++ b/tools/indexer/.gitignore @@ -1,3 +1,4 @@ node_modules # Keep environment variables out of version control .env +postgres-data \ No newline at end of file diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index a9aff39ec..42a2d7f4a 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -7,6 +7,7 @@ import { parseAction } from "@nomad-xyz/sdk/dist/nomad/messages/GovernanceMessag import { DB } from "./db"; import Logger from "bunyan"; import { Padded } from "./utils"; +import EventEmitter from "events"; class StatisticsCollector { s: Statistics; @@ -132,7 +133,7 @@ class StatisticsCollector { } } -export abstract class Consumer { +export abstract class Consumer extends EventEmitter { abstract consume(...evens: NomadEvent[]): Promise; abstract stats(): Statistics; } @@ -243,11 +244,11 @@ class Timings { processedAt: number; receivedAt: number; }): Timings { - const t = new Timings(Number(s.dispatchedAt)*1000); - t.updatedAt = Number(s.updatedAt)*1000; - t.relayedAt = Number(s.relayedAt)*1000; - t.processedAt = Number(s.processedAt)*1000; - t.receivedAt = Number(s.receivedAt)*1000; + const t = new Timings(s.dispatchedAt*1000); + t.updatedAt = s.updatedAt*1000; + t.relayedAt = s.relayedAt*1000; + t.processedAt = s.processedAt*1000; + t.receivedAt = s.receivedAt*1000; return t; } } @@ -661,6 +662,7 @@ export class Processor extends Consumer { m.state = MsgState.Updated; m.timings.updated(e.ts); this.addToSyncQueue(m.messageHash); + this.emit(`updated`, m.origin, m.destination, m.timings.inUpdated()) } }); } @@ -676,6 +678,7 @@ export class Processor extends Consumer { m.state = MsgState.Relayed; m.timings.relayed(e.ts); this.addToSyncQueue(m.messageHash); + this.emit(`relayed`, m.origin, m.destination, m.timings.inRelayed()) } }); } @@ -687,6 +690,7 @@ export class Processor extends Consumer { m.state = MsgState.Processed; m.timings.processed(e.ts); this.addToSyncQueue(m.messageHash); + this.emit(`processed`, m.origin, m.destination, m.timings.inProcessed(), m.timings.e2e()) } } } diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index 8f8e77ece..c3489b442 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -1,4 +1,4 @@ -import { Gauge } from "prom-client"; +import { Gauge, Histogram } from "prom-client"; import Logger from "bunyan"; import { register } from "prom-client"; @@ -49,6 +49,11 @@ export class IndexerCollector extends MetricsCollector { private meanProcessTimeGauge: Gauge; private meanEndToEndTimeGauge: Gauge; + private updateLatency: Histogram; + private relayLatency: Histogram; + private processLatency: Histogram; + private end2EndLatency: Histogram; + private homeFailedGauge: Gauge; constructor(environment: string, logger: Logger) { @@ -80,7 +85,7 @@ export class IndexerCollector extends MetricsCollector { labelNames: ["network", "environment"], }); - // Time + // Time Gauges this.meanUpdateTimeGauge = new Gauge({ name: prefix + "_mean_update_time", @@ -106,6 +111,36 @@ export class IndexerCollector extends MetricsCollector { labelNames: ["network", "environment"], }); + + // Time Histograms + + this.updateLatency = new Histogram({ + name: prefix + "_update_latency", + help: "Histogram that tracks latency of how long does it take to move from dispatched to updated.", + labelNames: ["home", "replica", "environment"], + }); + + this.relayLatency = new Histogram({ + name: prefix + "_relay_latency", + help: "Histogram that tracks latency of how long does it take to move from updated to relayed.", + labelNames: ["home", "replica", "environment"], + }); + + this.processLatency = new Histogram({ + name: prefix + "_process_latency", + help: "Histogram that tracks latency of how long does it take to move from relayed to processed.", + labelNames: ["home", "replica", "environment"], + }); + + this.end2EndLatency = new Histogram({ + name: prefix + "_end2end_latency", + help: "Histogram that tracks latency of how long does it take to move from dispatched to processed.", + labelNames: ["home", "replica", "environment"], + }); + + + // Home Health + this.homeFailedGauge = new Gauge({ name: "nomad_monitor_home_failed", help: "Gauge that indicates if home of a network is in failed state.", @@ -173,4 +208,17 @@ export class IndexerCollector extends MetricsCollector { homeFailed ? 1 : 0 ); } + + observeUpdate(home: string, replica: string, ms: number) { + this.updateLatency.labels(home, replica, this.environment).observe(ms) + } + observeRelayed(home: string, replica: string, ms: number) { + this.relayLatency.labels(home, replica, this.environment).observe(ms) + } + observeProcessed(home: string, replica: string, ms: number) { + this.processLatency.labels(home, replica, this.environment).observe(ms) + } + observeE2E(home: string, replica: string, ms: number) { + this.end2EndLatency.labels(home, replica, this.environment).observe(ms) + } } diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index 6f1f85252..0ccb41210 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -48,7 +48,7 @@ export class Orchestrator { healthCheckers: Map; gov: number; done: boolean; - freshStart: boolean; + chaseMode: boolean; metrics: IndexerCollector; logger: Logger; db: DB; @@ -67,7 +67,7 @@ export class Orchestrator { this.healthCheckers = new Map(); this.gov = gov; this.done = false; - this.freshStart = true; + this.chaseMode = true; this.metrics = metrics; this.logger = logger; this.db = db; @@ -137,11 +137,37 @@ export class Orchestrator { } } + subscribeStatisticEvents() { + this.consumer.on('updated', (home: number, replica: number ,ms: number) => { + const homeName = this.domain2name(home); + const replicaName = this.domain2name(replica); + this.metrics.observeUpdate(homeName, replicaName, ms) + }) + + this.consumer.on('relayed', (home: number, replica: number ,ms: number) => { + const homeName = this.domain2name(home); + const replicaName = this.domain2name(replica); + this.metrics.observeRelayed(homeName, replicaName, ms) + }) + + this.consumer.on('processed', (home: number, replica: number ,ms: number, e2e: number) => { + const homeName = this.domain2name(home); + const replicaName = this.domain2name(replica); + this.metrics.observeProcessed(homeName, replicaName, ms) + this.metrics.observeE2E(homeName, replicaName, e2e) + }) + } + async startConsuming() { while (!this.done) { this.logger.info(`Started to reindex`); const start = new Date().valueOf(); await Promise.all([this.indexAll(), this.checkAllHealth()]); + if (this.chaseMode) { + this.chaseMode = false; + this.subscribeStatisticEvents() + } + this.logger.info( `Finished reindexing after ${ (new Date().valueOf() - start) / 1000 @@ -163,6 +189,10 @@ export class Orchestrator { } } + domain2name(domain: number): string { + return this.sdk.getDomain(domain)!.name + } + reportMetrics(domain: number, statistics: Statistics) { const { counts: { dispatched, updated, relayed, processed }, @@ -171,7 +201,7 @@ export class Orchestrator { const homeFailed = this.healthCheckers.get(domain)!.failed; this.metrics.setNetworkState( - this.sdk.getDomain(domain)!.name, + this.domain2name(domain), dispatched, updated, relayed, diff --git a/tools/indexer/package-lock.json b/tools/indexer/package-lock.json index 56fe61798..d38282f3c 100644 --- a/tools/indexer/package-lock.json +++ b/tools/indexer/package-lock.json @@ -9,30 +9,21 @@ "version": "0.1.0", "license": "Apache 2.0", "dependencies": { - "@nomad-xyz/contract-interfaces": "1.1.1", - "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.2.4.tgz", + "@nomad-xyz/contract-interfaces": "^1.2.0", + "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.3.5.tgz", "@prisma/client": "^3.9.1", "@types/bunyan": "^1.8.7", "@types/express": "^4.17.13", - "@types/node-pg-migrate": "^2.3.1", - "@types/pg": "^8.6.4", "bunyan": "^1.8.15", "dotenv": "^10.0.0", "ethers": "^5.4.6", "express": "^4.17.2", - "got": "^11.8.2", - "moment": "^2.29.1", - "node-pg-migrate": "^6.2.1", "p-limit": "^3.1.0", - "pg": "^8.7.1", + "prettier": "^2.4.1", + "prisma": "^3.9.1", "prom-client": "^14.0.0", - "request": "^2.88.2", "ts-node": "^10.4.0", "typescript": "^4.4.3" - }, - "devDependencies": { - "prettier": "^2.4.1", - "prisma": "^3.9.1" } }, "../../typescript/nomad-sdk": { @@ -82,21 +73,21 @@ } }, "node_modules/@ethereumjs/common": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.0.tgz", - "integrity": "sha512-Cq2qS0FTu6O2VU1sgg+WyU9Ps0M6j/BEMHN+hRaECXCV/r0aI78u4N6p52QW/BDVhwWZpCdrvG8X7NJdzlpNUA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.2.tgz", + "integrity": "sha512-vDwye5v0SVeuDky4MtKsu+ogkH2oFUV8pBKzH/eNBzT8oI91pKa8WyzDuYuxOQsgNgv5R34LfFDh2aaw3H4HbQ==", "dependencies": { "crc-32": "^1.2.0", - "ethereumjs-util": "^7.1.3" + "ethereumjs-util": "^7.1.4" } }, "node_modules/@ethereumjs/tx": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.4.0.tgz", - "integrity": "sha512-WWUwg1PdjHKZZxPPo274ZuPsJCWV3SqATrEKQP1n2DrVYVP1aZIYpo/mFaA0BDoE0tIQmBeimRCEA0Lgil+yYw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.0.tgz", + "integrity": "sha512-/+ZNbnJhQhXC83Xuvy6I9k4jT5sXiV0tMR9C+AzSSpcCV64+NB8dTE1m3x98RYMqb8+TLYWA+HML4F5lfXTlJw==", "dependencies": { - "@ethereumjs/common": "^2.6.0", - "ethereumjs-util": "^7.1.3" + "@ethereumjs/common": "^2.6.1", + "ethereumjs-util": "^7.1.4" } }, "node_modules/@ethersproject/abi": { @@ -827,9 +818,9 @@ } }, "node_modules/@nomad-xyz/contract-interfaces": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@nomad-xyz/contract-interfaces/-/contract-interfaces-1.1.1.tgz", - "integrity": "sha512-bF0NYdck/LItN9s9D5ZQDzquiXv6sYCCi78lyHMiUNYaCGG3KLKeI5LkhW4fUlhgsNWBcQ9cdHt71S58m8KQrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nomad-xyz/contract-interfaces/-/contract-interfaces-1.2.0.tgz", + "integrity": "sha512-Hx5961SWgn6FODeX3BGX4rf5WOJj0xw9a2driFjspgIGfLE96daXqDBnzroUe/BR4ROA82NNdm1Kbw/hKfL9PQ==", "dependencies": { "@ethersproject/experimental": "^5.3.0", "@types/node": "^16.10.2", @@ -842,14 +833,14 @@ "integrity": "sha512-Pf8M1XD9i1ksZEcCP8vuSNwooJ/bZapNmIzpmsMaL+jMI+8mEYU3PKvs+xDNuQcJWF/x24WzY4qxLtB0zNow9A==" }, "node_modules/@nomad-xyz/sdk": { - "version": "1.2.4", - "resolved": "file:nomad-xyz-sdk-1.2.4.tgz", - "integrity": "sha512-sE4PqrAIFpQq0OOhnwyuD1soVHKGVV9LyqWLbN2XbacwOTZhxS2iTd8SyQCPU1cb+6fQj5diFysEKjoaFrFzQA==", + "version": "1.3.5", + "resolved": "file:nomad-xyz-sdk-1.3.5.tgz", + "integrity": "sha512-JvIkShn9Z5LXtIOUMngKD4MGigrQ0dkxXt5vfLnOJwPZ6YKv63Y6Rn2iaIM/4LQ6ooop5BZpw8H8+5fKpf8PkA==", "license": "Apache-2.0 OR MIT", "dependencies": { "@gnosis.pm/safe-core-sdk": "^1.3.0", "@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.7", - "@nomad-xyz/contract-interfaces": "1.1.1", + "@nomad-xyz/contract-interfaces": "1.2.0", "ethers": "^5.4.6", "web3": "^1.6.1" } @@ -878,7 +869,6 @@ "version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009.tgz", "integrity": "sha512-qM+uJbkelB21bnK44gYE049YTHIjHysOuj0mj5U2gDGyNLfmiazlggzFPCgEjgme4U5YB2tYs6Z5Hq08Kl8pjA==", - "devOptional": true, "hasInstallScript": true }, "node_modules/@prisma/engines-version": { @@ -887,25 +877,22 @@ "integrity": "sha512-5Dh+qTDhpPR66w6NNAnPs+/W/Qt4r1DSd+qhfPFcDThUK4uxoZKGlPb2IYQn5LL+18aIGnmteDf7BnVMmvBNSQ==" }, "node_modules/@sindresorhus/is": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", - "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "node": ">=6" } }, "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", "dependencies": { - "defer-to-connect": "^2.0.0" + "defer-to-connect": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">=6" } }, "node_modules/@tsconfig/node10": { @@ -953,17 +940,6 @@ "@types/node": "*" } }, - "node_modules/@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -993,19 +969,6 @@ "@types/range-parser": "*" } }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" - }, - "node_modules/@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -1016,12 +979,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.12.tgz", "integrity": "sha512-4YpbAsnJXWYK/fpTVFlMIcUIho2AYCi4wg5aNPrG1ng7fn/1/RZfCIpRCiBX+12RVa34RluilnvCqD+g3KiSiA==" }, - "node_modules/@types/node-pg-migrate": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@types/node-pg-migrate/-/node-pg-migrate-2.3.1.tgz", - "integrity": "sha512-ccKzTzDu8b6IRttXh1Ch6JlEQvVM4jrRUU9xyEp+Pq5yd3vBCg84pJeAUzycyts5Tg+eyiLqmlIW3g/SrRlqEQ==", - "deprecated": "This is a stub types definition for node-pg-migrate (https://github.com/theoephraim/node-pg-migrate#readme). node-pg-migrate provides its own type definitions, so you don\\'t need @types/node-pg-migrate installed!" - }, "node_modules/@types/pbkdf2": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", @@ -1030,16 +987,6 @@ "@types/node": "*" } }, - "node_modules/@types/pg": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.4.tgz", - "integrity": "sha512-uYA7UMVzDFpJobCrqwW/iWkFmvizy6knIUgr0Quaw7K1Le3ZnF7hI3bKqFoxPZ+fju1Sc7zdTvOl9YfFZPcmeA==", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -1050,14 +997,6 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/secp256k1": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", @@ -1126,28 +1065,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1451,14 +1368,6 @@ "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "engines": { - "node": ">=4" - } - }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -1501,27 +1410,41 @@ "node": ">= 0.8" } }, - "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "engines": { - "node": ">=10.6.0" - } - }, "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", + "keyv": "^3.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "engines": { "node": ">=8" } @@ -1589,16 +1512,6 @@ "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "node_modules/clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -1607,22 +1520,6 @@ "mimic-response": "^1.0.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1807,17 +1704,6 @@ "ms": "2.0.0" } }, - "node_modules/decamelize": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", - "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -1827,37 +1713,20 @@ } }, "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" + "mimic-response": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "engines": { - "node": ">=10" - } + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" }, "node_modules/define-properties": { "version": "1.1.3", @@ -1977,11 +1846,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -2076,14 +1940,6 @@ "ext": "^1.1.2" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2170,9 +2026,9 @@ } }, "node_modules/ethereumjs-util": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz", - "integrity": "sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz", + "integrity": "sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A==", "dependencies": { "@types/bn.js": "^5.1.0", "bn.js": "^5.1.2", @@ -2325,9 +2181,9 @@ } }, "node_modules/ext/node_modules/type": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", - "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" }, "node_modules/extend": { "version": "3.0.2", @@ -2453,14 +2309,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -2475,17 +2323,14 @@ } }, "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dependencies": { "pump": "^3.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/get-symbol-description": { @@ -2537,27 +2382,24 @@ } }, "node_modules/got": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", - "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", - "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" }, "engines": { - "node": ">=10.19.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" + "node": ">=8.6" } }, "node_modules/graceful-fs": { @@ -2720,18 +2562,6 @@ "npm": ">=1.3.7" } }, - "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2883,14 +2713,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-function": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", @@ -3089,9 +2911,9 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" }, "node_modules/json-schema": { "version": "0.4.0", @@ -3145,19 +2967,19 @@ } }, "node_modules/keyv": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", - "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dependencies": { - "json-buffer": "3.0.1" + "json-buffer": "3.0.0" } }, "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/lru-cache": { @@ -3346,6 +3168,7 @@ "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "optional": true, "engines": { "node": "*" } @@ -3456,46 +3279,12 @@ "node-gyp-build-test": "build-test.js" } }, - "node_modules/node-pg-migrate": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-6.2.1.tgz", - "integrity": "sha512-EsIOAWFBSBa/2g4BjA1tjRtPOEjOiZ/gqpjjxtsUyQRau0O2s1SVwyRW1HRA2qXZDMT/sA8GxcwLWqkK6BaGxA==", - "dependencies": { - "@types/pg": "^8.0.0", - "decamelize": "^5.0.0", - "mkdirp": "~1.0.0", - "yargs": "~17.3.0" - }, - "bin": { - "node-pg-migrate": "bin/node-pg-migrate" - }, - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "pg": ">=4.3.0 <9.0.0" - } - }, - "node_modules/node-pg-migrate/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/number-to-bn": { @@ -3593,11 +3382,11 @@ } }, "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/p-finally": { @@ -3633,11 +3422,6 @@ "node": ">=4" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "node_modules/parse-asn1": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", @@ -3697,128 +3481,18 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, - "node_modules/pg": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", - "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", - "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.4.1", - "pg-protocol": "^1.5.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "pg-native": ">=2.0.0" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", - "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", - "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/prettier": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", - "dev": true, "bin": { "prettier": "bin-prettier.js" }, @@ -3841,7 +3515,6 @@ "version": "3.9.1", "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.9.1.tgz", "integrity": "sha512-IGcJAu5LzlFv+i+NNhOEh1J1xVVttsVdRBxmrMN7eIH+7mRN6L89Hz1npUAiz4jOpNlHC7n9QwaOYZGxTqlwQw==", - "devOptional": true, "hasInstallScript": true, "dependencies": { "@prisma/engines": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" @@ -3944,17 +3617,6 @@ "node": ">=0.10.0" } }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -4046,25 +3708,12 @@ "node": ">=0.6" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, "node_modules/responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", "dependencies": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^1.0.0" } }, "node_modules/rimraf": { @@ -4279,34 +3928,15 @@ ] }, "node_modules/simple-get": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", - "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", + "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", "dependencies": { "decompress-response": "^3.3.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, - "node_modules/simple-get/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -4355,19 +3985,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -4392,17 +4009,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -4433,17 +4039,6 @@ "xhr-request": "^1.0.1" } }, - "node_modules/swarm-js/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/swarm-js/node_modules/get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -4476,14 +4071,6 @@ "node": ">=4" } }, - "node_modules/swarm-js/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/swarm-js/node_modules/p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", @@ -4492,6 +4079,25 @@ "node": ">=4" } }, + "node_modules/swarm-js/node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/swarm-js/node_modules/url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dependencies": { + "prepend-http": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tar": { "version": "4.4.19", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", @@ -4695,14 +4301,14 @@ } }, "node_modules/url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", "dependencies": { - "prepend-http": "^1.0.1" + "prepend-http": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, "node_modules/url-set-query": { @@ -4828,166 +4434,10 @@ "node": ">=8.0.0" } }, - "node_modules/web3-bzz/node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/web3-bzz/node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/web3-bzz/node_modules/@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" - }, - "node_modules/web3-bzz/node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/web3-bzz/node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/web3-bzz/node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "node_modules/web3-bzz/node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/web3-bzz/node_modules/got/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/web3-bzz/node_modules/got/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/web3-bzz/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "node_modules/web3-bzz/node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/web3-bzz/node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/web3-bzz/node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/web3-bzz/node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "engines": { - "node": ">=4" - } - }, - "node_modules/web3-bzz/node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/web3-bzz/node_modules/responselike/node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/web3-bzz/node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } + "version": "12.20.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.43.tgz", + "integrity": "sha512-HCfJdaYqJX3BCzeihgZrD7b85Cu05OC/GVJ4kEYIflwUs4jbnUlLLWoq7hw1LBcdvUyehO+gr6P5JQ895/2ZfA==" }, "node_modules/web3-core": { "version": "1.7.0", @@ -5080,9 +4530,9 @@ } }, "node_modules/web3-core/node_modules/@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + "version": "12.20.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.43.tgz", + "integrity": "sha512-HCfJdaYqJX3BCzeihgZrD7b85Cu05OC/GVJ4kEYIflwUs4jbnUlLLWoq7hw1LBcdvUyehO+gr6P5JQ895/2ZfA==" }, "node_modules/web3-eth": { "version": "1.7.0", @@ -5247,9 +4697,9 @@ } }, "node_modules/web3-eth-personal/node_modules/@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + "version": "12.20.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.43.tgz", + "integrity": "sha512-HCfJdaYqJX3BCzeihgZrD7b85Cu05OC/GVJ4kEYIflwUs4jbnUlLLWoq7hw1LBcdvUyehO+gr6P5JQ895/2ZfA==" }, "node_modules/web3-net": { "version": "1.7.0", @@ -5383,22 +4833,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -5473,14 +4907,6 @@ "node": ">=0.4" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, "node_modules/yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", @@ -5494,31 +4920,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", - "engines": { - "node": ">=12" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -5554,21 +4955,21 @@ } }, "@ethereumjs/common": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.0.tgz", - "integrity": "sha512-Cq2qS0FTu6O2VU1sgg+WyU9Ps0M6j/BEMHN+hRaECXCV/r0aI78u4N6p52QW/BDVhwWZpCdrvG8X7NJdzlpNUA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.2.tgz", + "integrity": "sha512-vDwye5v0SVeuDky4MtKsu+ogkH2oFUV8pBKzH/eNBzT8oI91pKa8WyzDuYuxOQsgNgv5R34LfFDh2aaw3H4HbQ==", "requires": { "crc-32": "^1.2.0", - "ethereumjs-util": "^7.1.3" + "ethereumjs-util": "^7.1.4" } }, "@ethereumjs/tx": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.4.0.tgz", - "integrity": "sha512-WWUwg1PdjHKZZxPPo274ZuPsJCWV3SqATrEKQP1n2DrVYVP1aZIYpo/mFaA0BDoE0tIQmBeimRCEA0Lgil+yYw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.0.tgz", + "integrity": "sha512-/+ZNbnJhQhXC83Xuvy6I9k4jT5sXiV0tMR9C+AzSSpcCV64+NB8dTE1m3x98RYMqb8+TLYWA+HML4F5lfXTlJw==", "requires": { - "@ethereumjs/common": "^2.6.0", - "ethereumjs-util": "^7.1.3" + "@ethereumjs/common": "^2.6.1", + "ethereumjs-util": "^7.1.4" } }, "@ethersproject/abi": { @@ -5983,9 +5384,9 @@ } }, "@nomad-xyz/contract-interfaces": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@nomad-xyz/contract-interfaces/-/contract-interfaces-1.1.1.tgz", - "integrity": "sha512-bF0NYdck/LItN9s9D5ZQDzquiXv6sYCCi78lyHMiUNYaCGG3KLKeI5LkhW4fUlhgsNWBcQ9cdHt71S58m8KQrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@nomad-xyz/contract-interfaces/-/contract-interfaces-1.2.0.tgz", + "integrity": "sha512-Hx5961SWgn6FODeX3BGX4rf5WOJj0xw9a2driFjspgIGfLE96daXqDBnzroUe/BR4ROA82NNdm1Kbw/hKfL9PQ==", "requires": { "@ethersproject/experimental": "^5.3.0", "@types/node": "^16.10.2", @@ -6000,12 +5401,12 @@ } }, "@nomad-xyz/sdk": { - "version": "file:nomad-xyz-sdk-1.2.4.tgz", - "integrity": "sha512-sE4PqrAIFpQq0OOhnwyuD1soVHKGVV9LyqWLbN2XbacwOTZhxS2iTd8SyQCPU1cb+6fQj5diFysEKjoaFrFzQA==", + "version": "file:nomad-xyz-sdk-1.3.5.tgz", + "integrity": "sha512-JvIkShn9Z5LXtIOUMngKD4MGigrQ0dkxXt5vfLnOJwPZ6YKv63Y6Rn2iaIM/4LQ6ooop5BZpw8H8+5fKpf8PkA==", "requires": { "@gnosis.pm/safe-core-sdk": "^1.3.0", "@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.7", - "@nomad-xyz/contract-interfaces": "1.1.1", + "@nomad-xyz/contract-interfaces": "1.2.0", "ethers": "^5.4.6", "web3": "^1.6.1" } @@ -6021,8 +5422,7 @@ "@prisma/engines": { "version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009.tgz", - "integrity": "sha512-qM+uJbkelB21bnK44gYE049YTHIjHysOuj0mj5U2gDGyNLfmiazlggzFPCgEjgme4U5YB2tYs6Z5Hq08Kl8pjA==", - "devOptional": true + "integrity": "sha512-qM+uJbkelB21bnK44gYE049YTHIjHysOuj0mj5U2gDGyNLfmiazlggzFPCgEjgme4U5YB2tYs6Z5Hq08Kl8pjA==" }, "@prisma/engines-version": { "version": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009", @@ -6030,16 +5430,16 @@ "integrity": "sha512-5Dh+qTDhpPR66w6NNAnPs+/W/Qt4r1DSd+qhfPFcDThUK4uxoZKGlPb2IYQn5LL+18aIGnmteDf7BnVMmvBNSQ==" }, "@sindresorhus/is": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz", - "integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" }, "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", "requires": { - "defer-to-connect": "^2.0.0" + "defer-to-connect": "^1.0.1" } }, "@tsconfig/node10": { @@ -6087,17 +5487,6 @@ "@types/node": "*" } }, - "@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, "@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -6127,19 +5516,6 @@ "@types/range-parser": "*" } }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" - }, - "@types/keyv": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz", - "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==", - "requires": { - "@types/node": "*" - } - }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -6150,11 +5526,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.12.tgz", "integrity": "sha512-4YpbAsnJXWYK/fpTVFlMIcUIho2AYCi4wg5aNPrG1ng7fn/1/RZfCIpRCiBX+12RVa34RluilnvCqD+g3KiSiA==" }, - "@types/node-pg-migrate": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@types/node-pg-migrate/-/node-pg-migrate-2.3.1.tgz", - "integrity": "sha512-ccKzTzDu8b6IRttXh1Ch6JlEQvVM4jrRUU9xyEp+Pq5yd3vBCg84pJeAUzycyts5Tg+eyiLqmlIW3g/SrRlqEQ==" - }, "@types/pbkdf2": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", @@ -6163,16 +5534,6 @@ "@types/node": "*" } }, - "@types/pg": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.4.tgz", - "integrity": "sha512-uYA7UMVzDFpJobCrqwW/iWkFmvizy6knIUgr0Quaw7K1Le3ZnF7hI3bKqFoxPZ+fju1Sc7zdTvOl9YfFZPcmeA==", - "requires": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", @@ -6183,14 +5544,6 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "requires": { - "@types/node": "*" - } - }, "@types/secp256k1": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", @@ -6243,19 +5596,6 @@ "uri-js": "^4.2.2" } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -6517,11 +5857,6 @@ "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" }, - "buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" - }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -6551,23 +5886,33 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" - }, "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", + "keyv": "^3.0.0", "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } } }, "call-bind": { @@ -6626,16 +5971,6 @@ "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -6644,19 +5979,6 @@ "mimic-response": "^1.0.0" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6814,35 +6136,23 @@ "ms": "2.0.0" } }, - "decamelize": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", - "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==" - }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - } + "mimic-response": "^1.0.0" } }, "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" }, "define-properties": { "version": "1.1.3", @@ -6943,11 +6253,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -7027,11 +6332,6 @@ "ext": "^1.1.2" } }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -7119,9 +6419,9 @@ } }, "ethereumjs-util": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz", - "integrity": "sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz", + "integrity": "sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A==", "requires": { "@types/bn.js": "^5.1.0", "bn.js": "^5.1.2", @@ -7255,9 +6555,9 @@ }, "dependencies": { "type": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", - "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" } } }, @@ -7353,11 +6653,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, "get-intrinsic": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", @@ -7369,9 +6664,9 @@ } }, "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "requires": { "pump": "^3.0.0" } @@ -7416,21 +6711,21 @@ } }, "got": { - "version": "11.8.3", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz", - "integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==", - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" } }, "graceful-fs": { @@ -7552,15 +6847,6 @@ "sshpk": "^1.7.0" } }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -7658,11 +6944,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, "is-function": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", @@ -7794,9 +7075,9 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" }, "json-schema": { "version": "0.4.0", @@ -7843,17 +7124,17 @@ } }, "keyv": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz", - "integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "requires": { - "json-buffer": "3.0.1" + "json-buffer": "3.0.0" } }, "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, "lru-cache": { "version": "6.0.0", @@ -8005,7 +7286,8 @@ "moment": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "optional": true }, "ms": { "version": "2.0.0", @@ -8098,28 +7380,10 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" }, - "node-pg-migrate": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-6.2.1.tgz", - "integrity": "sha512-EsIOAWFBSBa/2g4BjA1tjRtPOEjOiZ/gqpjjxtsUyQRau0O2s1SVwyRW1HRA2qXZDMT/sA8GxcwLWqkK6BaGxA==", - "requires": { - "@types/pg": "^8.0.0", - "decamelize": "^5.0.0", - "mkdirp": "~1.0.0", - "yargs": "~17.3.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - } - } - }, "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" }, "number-to-bn": { "version": "1.7.0", @@ -8193,9 +7457,9 @@ } }, "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" }, "p-finally": { "version": "1.0.0", @@ -8218,11 +7482,6 @@ "p-finally": "^1.0.0" } }, - "packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "parse-asn1": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", @@ -8273,94 +7532,15 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, - "pg": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.1.tgz", - "integrity": "sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA==", - "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.5.0", - "pg-pool": "^3.4.1", - "pg-protocol": "^1.5.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - } - }, - "pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" - }, - "pg-pool": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz", - "integrity": "sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==", - "requires": {} - }, - "pg-protocol": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", - "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" - }, - "pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - } - }, - "pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "requires": { - "split2": "^4.1.0" - } - }, - "postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" - }, - "postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" - }, - "postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" - }, - "postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "requires": { - "xtend": "^4.0.0" - } - }, "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" }, "prettier": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", - "dev": true + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==" }, "printj": { "version": "1.3.1", @@ -8371,7 +7551,6 @@ "version": "3.9.1", "resolved": "https://registry.npmjs.org/prisma/-/prisma-3.9.1.tgz", "integrity": "sha512-IGcJAu5LzlFv+i+NNhOEh1J1xVVttsVdRBxmrMN7eIH+7mRN6L89Hz1npUAiz4jOpNlHC7n9QwaOYZGxTqlwQw==", - "devOptional": true, "requires": { "@prisma/engines": "3.9.0-58.bcc2ff906db47790ee902e7bbc76d7ffb1893009" } @@ -8445,11 +7624,6 @@ "strict-uri-encode": "^1.0.0" } }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -8527,22 +7701,12 @@ } } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" - }, "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", "requires": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^1.0.0" } }, "rimraf": { @@ -8702,30 +7866,15 @@ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, "simple-get": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", - "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", + "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", "requires": { "decompress-response": "^3.3.0", "once": "^1.3.1", "simple-concat": "^1.0.0" - }, - "dependencies": { - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - } } }, - "split2": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", - "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" - }, "sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -8760,16 +7909,6 @@ "safe-buffer": "~5.2.0" } }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -8788,14 +7927,6 @@ "define-properties": "^1.1.3" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, "strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -8822,14 +7953,6 @@ "xhr-request": "^1.0.1" }, "dependencies": { - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -8856,15 +7979,23 @@ "url-to-options": "^1.0.1" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } } } }, @@ -9015,11 +8146,11 @@ } }, "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "^2.0.0" } }, "url-set-query": { @@ -9117,134 +8248,10 @@ "swarm-js": "^0.1.40" }, "dependencies": { - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "requires": { - "defer-to-connect": "^1.0.1" - } - }, "@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "dependencies": { - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - } - } - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "requires": { - "json-buffer": "3.0.0" - } - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" - }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "requires": { - "lowercase-keys": "^1.0.0" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - } - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "requires": { - "prepend-http": "^2.0.0" - } + "version": "12.20.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.43.tgz", + "integrity": "sha512-HCfJdaYqJX3BCzeihgZrD7b85Cu05OC/GVJ4kEYIflwUs4jbnUlLLWoq7hw1LBcdvUyehO+gr6P5JQ895/2ZfA==" } } }, @@ -9271,9 +8278,9 @@ } }, "@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + "version": "12.20.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.43.tgz", + "integrity": "sha512-HCfJdaYqJX3BCzeihgZrD7b85Cu05OC/GVJ4kEYIflwUs4jbnUlLLWoq7hw1LBcdvUyehO+gr6P5JQ895/2ZfA==" } } }, @@ -9471,9 +8478,9 @@ }, "dependencies": { "@types/node": { - "version": "12.20.42", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.42.tgz", - "integrity": "sha512-aI3/oo5DzyiI5R/xAhxxRzfZlWlsbbqdgxfTPkqu/Zt+23GXiJvMCyPJT4+xKSXOnLqoL8jJYMLTwvK2M3a5hw==" + "version": "12.20.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.43.tgz", + "integrity": "sha512-HCfJdaYqJX3BCzeihgZrD7b85Cu05OC/GVJ4kEYIflwUs4jbnUlLLWoq7hw1LBcdvUyehO+gr6P5JQ895/2ZfA==" } } }, @@ -9578,16 +8585,6 @@ "is-typed-array": "^1.1.7" } }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -9645,11 +8642,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, "yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", @@ -9660,25 +8652,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - } - }, - "yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" - }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/tools/indexer/package.json b/tools/indexer/package.json index db58d0ecc..2a08f11b0 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -12,11 +12,11 @@ "start": "npm run migrate && npm run any", "migrate": "prisma db push", "release-docker": "docker buildx build --platform linux/amd64,linux/arm64 --push -t gcr.io/nomad-xyz/nomad-indexer:sha-$(git rev-parse --short HEAD) .", - "get_sdk": "cd ../../typescript/nomad-sdk && npm i && npm run build && npm pack && cd ../../tools/indexer && mv ../../typescript/nomad-sdk/nomad-xyz-sdk-1.2.4.tgz ." + "get_sdk": "cd ../../typescript/nomad-sdk && npm i && npm run build && npm pack && cd ../../tools/indexer && mv ../../typescript/nomad-sdk/nomad-xyz-sdk-1.3.5.tgz ." }, "dependencies": { - "@nomad-xyz/contract-interfaces": "1.1.1", - "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.2.4.tgz", + "@nomad-xyz/contract-interfaces": "^1.2.0", + "@nomad-xyz/sdk": "file:nomad-xyz-sdk-1.3.5.tgz", "@prisma/client": "^3.9.1", "@types/bunyan": "^1.8.7", "@types/express": "^4.17.13", @@ -25,12 +25,10 @@ "ethers": "^5.4.6", "express": "^4.17.2", "p-limit": "^3.1.0", - "prom-client": "^14.0.0", "prettier": "^2.4.1", "prisma": "^3.9.1", + "prom-client": "^14.0.0", "ts-node": "^10.4.0", "typescript": "^4.4.3" - }, - "devDependencies": { } } From 122eb87d3223ef4833d744dcb68b42760d4f178e Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Tue, 8 Feb 2022 20:06:36 +0100 Subject: [PATCH 51/63] fix: hostgrams and ts --- tools/indexer/core/consumer.ts | 12 ++++++------ tools/indexer/core/metrics.ts | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index 42a2d7f4a..3327db04e 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -388,13 +388,13 @@ export class NomadMessage { s.messageHash, BigNumber.from(s.leafIndex), s.body, - s.dispatchedAt, + s.dispatchedAt*1000, s.dispatchBlock ); - m.timings.updated(s.updatedAt); - m.timings.relayed(s.relayedAt); - m.timings.received(s.receivedAt); - m.timings.processed(s.processedAt); + m.timings.updated(s.updatedAt*1000); + m.timings.relayed(s.relayedAt*1000); + m.timings.received(s.receivedAt*1000); + m.timings.processed(s.processedAt*1000); m.sender = s.sender || undefined; m.tx = s.tx || undefined; m.state = s.state; @@ -758,7 +758,7 @@ export class Processor extends Consumer { collector.contributeToCount(m); }); - this.messages.slice(this.messages.length - 50).forEach((m) => { + this.messages.slice(this.messages.length - 50).forEach((m) => { // Use last 50 message for statistics collector.contributeToTime(m); }); diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index c3489b442..16873ce5a 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -4,6 +4,20 @@ import Logger from "bunyan"; import { register } from "prom-client"; import express, { Response } from "express"; +const buckets = [ + 1 * 60, // 1 min + 5 * 60, // 5 min + 10 * 60, // 10 min + 20 * 60, // 20 min + 30 * 60, // 30 min + 60 * 60, // 1 hr + 120 * 60, // 2 hrs + 240 * 60, // 4 hrs + 480 * 60, // 8 hrs + 960 * 60, // 16 hrs + 1920 * 60, // 32 hrs +]; + export class MetricsCollector { readonly environment: string; private readonly logger: Logger; @@ -118,24 +132,28 @@ export class IndexerCollector extends MetricsCollector { name: prefix + "_update_latency", help: "Histogram that tracks latency of how long does it take to move from dispatched to updated.", labelNames: ["home", "replica", "environment"], + buckets, }); this.relayLatency = new Histogram({ name: prefix + "_relay_latency", help: "Histogram that tracks latency of how long does it take to move from updated to relayed.", labelNames: ["home", "replica", "environment"], + buckets, }); this.processLatency = new Histogram({ name: prefix + "_process_latency", help: "Histogram that tracks latency of how long does it take to move from relayed to processed.", labelNames: ["home", "replica", "environment"], + buckets, }); this.end2EndLatency = new Histogram({ name: prefix + "_end2end_latency", help: "Histogram that tracks latency of how long does it take to move from dispatched to processed.", labelNames: ["home", "replica", "environment"], + buckets, }); From 37e7f9e57657348ff56029f954c69f074e0db01b Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 10 Feb 2022 17:19:13 +0100 Subject: [PATCH 52/63] feature: gas usage metrics --- tools/indexer/core/consumer.ts | 73 +++- tools/indexer/core/event.ts | 18 +- tools/indexer/core/indexer.ts | 332 +++++++++++++++--- tools/indexer/core/metrics.ts | 67 +++- tools/indexer/core/orchestrator.ts | 30 +- .../20220203202333_init/migration.sql | 5 + tools/indexer/prisma/schema.prisma | 5 + 7 files changed, 459 insertions(+), 71 deletions(-) diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index 3327db04e..2ca24e63f 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -146,6 +146,51 @@ enum MsgState { Processed, } + +class GasUsed { + dispatch: ethers.BigNumber; + update: ethers.BigNumber; + relay: ethers.BigNumber; + receive: ethers.BigNumber; + process: ethers.BigNumber; + + constructor() { + this.dispatch = ethers.BigNumber.from(0); + this.update = ethers.BigNumber.from(0); + this.relay = ethers.BigNumber.from(0); + this.receive = ethers.BigNumber.from(0); + this.process = ethers.BigNumber.from(0); + } + + serialize() { + return { + gasAtDispatch: this.dispatch.toHexString(), + gasAtUpdate: this.update.toHexString(), + gasAtRelay: this.relay.toHexString(), + gasAtReceive: this.receive.toHexString(), + gasAtProcess: this.process.toHexString(), + } + } + + static deserialize(o: { + gasAtDispatch: string, + gasAtUpdate: string, + gasAtRelay: string, + gasAtReceive: string, + gasAtProcess: string, + }): GasUsed { + let g = new GasUsed(); + g.dispatch = ethers.BigNumber.from(o.gasAtDispatch); + g.update = ethers.BigNumber.from(o.gasAtUpdate); + g.relay = ethers.BigNumber.from(o.gasAtRelay); + g.receive = ethers.BigNumber.from(o.gasAtReceive); + g.process = ethers.BigNumber.from(o.gasAtProcess); + return g + } + +} + + class Timings { dispatchedAt: number; updatedAt: number; @@ -276,6 +321,11 @@ export type MinimumSerializedNomadMessage = { sender: string | null,// m.sender || '', tx: string | null,// m.evm || '' state: MsgState, + gasAtDispatch: string | null, + gasAtUpdate: string | null, + gasAtRelay: string | null, + gasAtReceive: string | null, + gasAtProcess: string | null, } export type ExtendedSerializedNomadMessage = MinimumSerializedNomadMessage & { @@ -311,6 +361,7 @@ export class NomadMessage { tx?: string; timings: Timings; + gasUsed: GasUsed; constructor( origin: number, @@ -342,6 +393,7 @@ export class NomadMessage { this.state = MsgState.Dispatched; this.timings = new Timings(dispatchedAt); this.dispatchBlock = dispatchBlock; + this.gasUsed = new GasUsed(); } // PADDED! @@ -395,6 +447,13 @@ export class NomadMessage { m.timings.relayed(s.relayedAt*1000); m.timings.received(s.receivedAt*1000); m.timings.processed(s.processedAt*1000); + + m.gasUsed.dispatch = (ethers.BigNumber.from(s.gasAtDispatch)); + m.gasUsed.update = (ethers.BigNumber.from(s.gasAtUpdate)); + m.gasUsed.relay = (ethers.BigNumber.from(s.gasAtRelay)); + m.gasUsed.receive = (ethers.BigNumber.from(s.gasAtReceive)); + m.gasUsed.process = (ethers.BigNumber.from(s.gasAtProcess)); + m.sender = s.sender || undefined; m.tx = s.tx || undefined; m.state = s.state; @@ -424,6 +483,7 @@ export class NomadMessage { detailsHash: this.detailsHash() || null, tokenDomain: this.tokenDomain() || null, tokenId: this.tokenId()?.valueOf() || null, + ...this.gasUsed.serialize() }; } @@ -645,11 +705,13 @@ export class Processor extends Consumer { e.ts, e.block ); + m.gasUsed.dispatch = e.gasUsed; this.senderRegistry.dispatch(e, m); this.add(m); this.addToSyncQueue(m.messageHash); + this.emit(`dispatched`, m.origin, m.destination, e.gasUsed) if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } @@ -661,8 +723,9 @@ export class Processor extends Consumer { if (m.state < MsgState.Updated) { m.state = MsgState.Updated; m.timings.updated(e.ts); + m.gasUsed.update = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`updated`, m.origin, m.destination, m.timings.inUpdated()) + this.emit(`updated`, m.origin, m.destination, m.timings.inUpdated(), e.gasUsed) } }); } @@ -677,8 +740,9 @@ export class Processor extends Consumer { if (m.state < MsgState.Relayed) { m.state = MsgState.Relayed; m.timings.relayed(e.ts); + m.gasUsed.relay = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`relayed`, m.origin, m.destination, m.timings.inRelayed()) + this.emit(`relayed`, m.origin, m.destination, m.timings.inRelayed(), e.gasUsed) } }); } @@ -689,8 +753,9 @@ export class Processor extends Consumer { if (m.state < MsgState.Processed) { m.state = MsgState.Processed; m.timings.processed(e.ts); + m.gasUsed.process = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`processed`, m.origin, m.destination, m.timings.inProcessed(), m.timings.e2e()) + this.emit(`processed`, m.origin, m.destination, m.timings.inProcessed(), m.timings.e2e(), e.gasUsed) } } } @@ -709,7 +774,9 @@ export class Processor extends Consumer { if (m.state < MsgState.Received) { m.state = MsgState.Received; m.timings.received(e.ts); + m.gasUsed.receive = e.gasUsed; this.addToSyncQueue(m.messageHash); + this.emit(`received`, e.gasUsed) } } } diff --git a/tools/indexer/core/event.ts b/tools/indexer/core/event.ts index 8877f0d8d..b59c5caa6 100644 --- a/tools/indexer/core/event.ts +++ b/tools/indexer/core/event.ts @@ -81,6 +81,8 @@ export class NomadEvent { eventData: EventData; block: number; source: EventSource; + gasUsed: ethers.BigNumber; + tx: string; constructor( domain: number, @@ -90,7 +92,9 @@ export class NomadEvent { ts: number, eventData: EventData, block: number, - source: EventSource + source: EventSource, + gasUsed: ethers.BigNumber, + tx: string, ) { this.domain = domain; this.eventType = eventType; @@ -104,6 +108,8 @@ export class NomadEvent { this.eventData = eventData; this.block = block; this.source = source; + this.gasUsed = gasUsed; + this.tx = tx; } destinationAndNonce(): [number, number] { @@ -140,6 +146,8 @@ export class NomadEvent { eventData: this.eventData, block: this.block, source: EventSource.Storage, + gasUsed: this.gasUsed, + tx: this.tx, }; } @@ -152,6 +160,8 @@ export class NomadEvent { ts: number; eventData: EventData; block: number; + gasUsed: ethers.BigNumber; + tx: string; }; return new NomadEvent( e.domain, @@ -161,12 +171,14 @@ export class NomadEvent { e.ts, e.eventData, e.block, - EventSource.Storage + EventSource.Storage, + e.gasUsed, + e.tx, ); } uniqueHash(): string { - return hash(this.domain.toString(), this.eventType, this.replicaOrigin.toString(), this.block.toString(), uniqueHash(this.eventData)) + return hash(this.domain.toString(), this.eventType, this.replicaOrigin.toString(), this.block.toString(), uniqueHash(this.eventData), this.gasUsed.toString(), this.tx) } } diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index ed8b560da..c33f70abf 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -8,6 +8,69 @@ import { ethers } from "ethers"; import { KVCache, replacer, retry, reviver } from "./utils"; import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; +import { TypedEvent } from "@nomad-xyz/contract-interfaces/core/commons"; +import { Result } from "ethers/lib/utils"; + +type ShortTx = { + gasPrice?: ethers.BigNumber, + timestamp: number, + from: string, + to?: string, + gasLimit: ethers.BigNumber, +}; + +function txEncode(tx: ShortTx): string { + const { + gasPrice, // ? + timestamp, + from, + to, // ? + gasLimit, + } = tx as ShortTx; + return JSON.stringify({ + gasPrice, // ? + timestamp, + from, + to, // ? + gasLimit, + }, replacer) +} + +function txDecode(encoded: string): ShortTx { + return JSON.parse(encoded, reviver) +} + +type ShortTxReceipt = { + effectiveGasPrice: ethers.BigNumber, + cumulativeGasUsed: ethers.BigNumber, + gasUsed: ethers.BigNumber, + from: string, + to: string, + status?: number, +}; + +function txReceiptEncode(receipt: ethers.providers.TransactionReceipt): string { + const { + effectiveGasPrice, + cumulativeGasUsed, + from, + to, + gasUsed, + status, + } = receipt as ShortTxReceipt; + return JSON.stringify({ + effectiveGasPrice, + cumulativeGasUsed, + from, + to, + gasUsed, + status, + }, replacer) +} + +function txReceiptDecode(encoded: string): ShortTxReceipt { + return JSON.parse(encoded, reviver) +} const BATCH_SIZE = process.env.BATCH_SIZE ? parseInt(process.env.BATCH_SIZE) : 2000; @@ -20,6 +83,9 @@ export class Indexer { orchestrator: Orchestrator; persistance: Persistance; blockCache: KVCache; + blockTimestampCache: KVCache; + txCache: KVCache; + txReceiptCache: KVCache; limit: pLimit.Limit; eventCallback: undefined | ((event: NomadEvent) => void); @@ -31,7 +97,10 @@ export class Indexer { this.persistance = new RamPersistance( `/tmp/persistance_${this.domain}.json` ); - this.blockCache = new KVCache(String(this.domain), this.orchestrator.db); + this.blockCache = new KVCache('b_' + String(this.domain), this.orchestrator.db); + this.blockTimestampCache = new KVCache('bts_' + String(this.domain), this.orchestrator.db); + this.txCache = new KVCache('tx_' + String(this.domain), this.orchestrator.db); + this.txReceiptCache = new KVCache('txr_' + String(this.domain), this.orchestrator.db); // 20 concurrent requests per indexer this.limit = pLimit(100); } @@ -40,7 +109,7 @@ export class Indexer { return this.sdk.getProvider(this.domain)!; } - async getBlockInfo( + async getBlockInfoLegacy( blockNumber: number ): Promise<[number, Map]> { const possibleBlock = await this.blockCache.get(String(blockNumber)); @@ -83,6 +152,145 @@ export class Indexer { return [time, senders2hashes]; } + async getBlockTimestamp(blockNumber: number): Promise { + const possible = await this.blockTimestampCache.get(String(blockNumber)); + if (possible) { + // this.orchestrator.logger.debug(`Gound transaction receipt in DB: ${hash}`); + return parseInt(possible) + } + + const [block, error] = await retry( + async () => { + return await this.limit(() => this.provider.getBlock(blockNumber)) + }, + RETRIES, + (error: any) => + this.orchestrator.logger.warn( + `Retrying after RPC Error... Block number: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` + ) + ); + if (!block) { + throw new Error( + `An RPC foo error occured, retried exhausted. Block number: ${blockNumber} Domain: ${this.domain}, Error: ${error}` + ); + } + + const timestamp = block.timestamp * 1000; + + await this.blockTimestampCache.set( + String(blockNumber), + String(timestamp) + ); + + // this.orchestrator.logger.debug(`Finished transaction receipt fetch: ${hash}`); + + return timestamp + } + + + + async getTransaction(hash: string, forceTimestamp=false): Promise { + const possible = await this.txCache.get(hash); + if (possible) { + // this.orchestrator.logger.debug(`Gound transaction in DB: ${hash}`); + return txDecode(possible) + } + + const [tx, error] = await retry( + async () => { + return await this.limit(() => this.provider.getTransaction(hash)) + }, + RETRIES, + (error: any) => + this.orchestrator.logger.warn( + `Retrying after RPC Error... TX hash: ${hash}, Domain: ${this.domain}, Error: ${error.code}` + ) + ); + if (!tx) { + throw new Error( + `An RPC foo error occured, retried exhausted. TX hash: ${hash} Domain: ${this.domain}, Error: ${error}` + ); + } + + let timestamp: number; + + if (forceTimestamp) { + this.orchestrator.logger.debug(`Using real time timestamp for: ${hash}, ${forceTimestamp} || ${tx.timestamp}`); + + timestamp = new Date().valueOf() + } else if (!tx.timestamp) { + + if (!tx.blockNumber) throw new Error( + `An RPC foo error occured. TX hash: ${hash} has no blockNumber. WTF?` + ); + + timestamp = await this.getBlockTimestamp(tx.blockNumber!) + } else { + timestamp = tx.timestamp! * 1000; + } + + let shortTx = { + gasPrice: tx.gasPrice, + timestamp, + from: tx.from, + to: tx.to, + gasLimit: tx.gasLimit, + }; + + + await this.txCache.set( + hash, + txEncode(shortTx) + ); + + // this.orchestrator.logger.debug(`Finished transaction fetch: ${hash}`); + + return shortTx; + } + + async getTransactionReceipt(hash: string): Promise { + const possible = await this.txReceiptCache.get(hash); + if (possible) { + // this.orchestrator.logger.debug(`Gound transaction receipt in DB: ${hash}`); + return txReceiptDecode(possible) + } + + const [receipt, error] = await retry( + async () => { + return await this.limit(() => this.provider.getTransactionReceipt(hash)) + }, + RETRIES, + (error: any) => + this.orchestrator.logger.warn( + `Retrying after RPC Error... TX hash: ${hash}, Domain: ${this.domain}, Error: ${error.code}` + ) + ); + if (!receipt) { + throw new Error( + `An RPC foo error occured, retried exhausted. TX hash: ${hash} Domain: ${this.domain}, Error: ${error}` + ); + } + + await this.txReceiptCache.set( + hash, + txReceiptEncode(receipt) + ); + + // this.orchestrator.logger.debug(`Finished transaction receipt fetch: ${hash}`); + + return receipt + } + + async getAdditionalInfo(hash: string): Promise<{timestamp: number, gasUsed: ethers.BigNumber, from: string}> { + const {timestamp} = await this.getTransaction(hash, !this.orchestrator.chaseMode); + + const {gasUsed, from} = await this.getTransactionReceipt(hash); + + // this.orchestrator.logger.debug(`Done with TX: ${hash}`); + + return {timestamp, gasUsed, from} + } + async init() { await this.blockCache.init(); await this.persistance.init(); @@ -168,7 +376,7 @@ export class Indexer { const pastTo = batchTo; batchFrom = batchFrom - batchSize/2; batchTo = batchFrom + batchSize; - this.orchestrator.logger.warn(`Integrity test not passed for ${this.domain} between ${pastFrom} and ${pastTo}, recollecting between ${batchFrom} and ${batchTo},: ${e}`); + this.orchestrator.logger.warn(`Integrity test not passed for ${this.domain} between ${pastFrom} and ${pastTo}, recollecting between ${batchFrom} and ${batchTo}: ${e}`); continue; } allEvents.push(...events.filter(newEvent => allEvents.every(oldEvent => newEvent.uniqueHash() !== oldEvent.uniqueHash()))); @@ -191,7 +399,7 @@ export class Indexer { let allEvents = this.persistance.allEvents(); if (blockTo) allEvents = allEvents.filter(e => e.block <= blockTo); if (allEvents.length === 0) { - this.orchestrator.logger.warn(`No events to test integrity!!!`); + this.orchestrator.logger.debug(`No events to test integrity!!!`); return ; } @@ -284,15 +492,14 @@ export class Indexer { } const parsedEvents = await Promise.all( events.map(async (event) => { - const [ts, senders2hashes] = await this.getBlockInfo( - event.blockNumber - ); + + let {timestamp, gasUsed} = await this.getAdditionalInfo(event.transactionHash); return new NomadEvent( this.domain, EventType.BridgeRouterSend, ContractType.BridgeRouter, 0, - ts, + timestamp, { token: event.args[0], from: event.args[1], @@ -300,10 +507,12 @@ export class Indexer { toId: event.args[3], amount: event.args[4], fastLiquidityEnabled: event.args[5], - evmHash: senders2hashes.get(event.args[1])!, + evmHash: event.transactionHash, }, event.blockNumber, - EventSource.Fetch + EventSource.Fetch, + gasUsed, + event.transactionHash ); }) ); @@ -333,14 +542,14 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( async (event) => - new NomadEvent( + { + let {timestamp, gasUsed, } = await this.getAdditionalInfo(event.transactionHash); + return new NomadEvent( this.domain, EventType.BridgeRouterReceive, ContractType.BridgeRouter, 0, - ( - await this.getBlockInfo(event.blockNumber) - )[0], + timestamp, { originAndNonce: event.args[0], token: event.args[1], @@ -349,8 +558,10 @@ export class Indexer { amount: event.args[4], }, event.blockNumber, - EventSource.Fetch - )) + EventSource.Fetch, + gasUsed, + event.transactionHash, + )}) ) ; allEvents.push(...parsedEvents); @@ -359,6 +570,8 @@ export class Indexer { return allEvents; } + + async fetchHome(from: number, to: number) { let fetchedEvents: NomadEvent[] = []; @@ -386,26 +599,31 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( async (event) => - new NomadEvent( - this.domain, - EventType.HomeDispatch, - ContractType.Home, - 0, - ( - await this.getBlockInfo(event.blockNumber) - )[0], - { - messageHash: event.args[0], - leafIndex: event.args[1], - destinationAndNonce: event.args[2], - committedRoot: event.args[3], - message: event.args[4], - }, - event.blockNumber, - EventSource.Fetch - )) - ) - ; + { + + let {timestamp, gasUsed} = await this.getAdditionalInfo(event.transactionHash); + + return new NomadEvent( + this.domain, + EventType.HomeDispatch, + ContractType.Home, + 0, + timestamp, + { + messageHash: event.args[0], + leafIndex: event.args[1], + destinationAndNonce: event.args[2], + committedRoot: event.args[3], + message: event.args[4], + }, + event.blockNumber, + EventSource.Fetch, + gasUsed, + event.transactionHash + ) + }) + ); + fetchedEvents.push(...parsedEvents); } @@ -432,14 +650,14 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( async (event) => - new NomadEvent( + { + let {timestamp, gasUsed} = await this.getAdditionalInfo(event.transactionHash); + return new NomadEvent( this.domain, EventType.HomeUpdate, ContractType.Home, 0, - ( - await this.getBlockInfo(event.blockNumber) - )[0], + timestamp, { homeDomain: event.args[0], oldRoot: event.args[1], @@ -447,8 +665,10 @@ export class Indexer { signature: event.args[3], }, event.blockNumber, - EventSource.Fetch - )) + EventSource.Fetch, + gasUsed, + event.transactionHash + )}) ) ; fetchedEvents.push(...parsedEvents); @@ -488,14 +708,14 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( async (event) => - new NomadEvent( + { + let {timestamp, gasUsed} = await this.getAdditionalInfo(event.transactionHash); + return new NomadEvent( this.domain, EventType.ReplicaUpdate, ContractType.Replica, domain, - ( - await this.getBlockInfo(event.blockNumber) - )[0], + timestamp, { homeDomain: event.args[0], oldRoot: event.args[1], @@ -503,8 +723,10 @@ export class Indexer { signature: event.args[3], }, event.blockNumber, - EventSource.Fetch - )) + EventSource.Fetch, + gasUsed, + event.transactionHash + )}) ) ; fetchedEvents.push(...parsedEvents); @@ -537,22 +759,24 @@ export class Indexer { const parsedEvents = await Promise.all( events.map( async (event) => - new NomadEvent( + { + let {timestamp, gasUsed} = await this.getAdditionalInfo(event.transactionHash); + return new NomadEvent( this.domain, EventType.ReplicaProcess, ContractType.Replica, domain, - ( - await this.getBlockInfo(event.blockNumber) - )[0], + timestamp, { messageHash: event.args[0], success: event.args[1], returnData: event.args[2], }, event.blockNumber, - EventSource.Fetch - )) + EventSource.Fetch, + gasUsed, + event.transactionHash + )}) ) ; fetchedEvents.push(...parsedEvents); diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index 16873ce5a..a0ef48e2a 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -68,6 +68,12 @@ export class IndexerCollector extends MetricsCollector { private processLatency: Histogram; private end2EndLatency: Histogram; + private dispatchGasUsage: Histogram; + private updateGasUsage: Histogram; + private relayGasUsage: Histogram; + private receiveGasUsage: Histogram; + private processGasUsage: Histogram; + private homeFailedGauge: Gauge; constructor(environment: string, logger: Logger) { @@ -156,6 +162,42 @@ export class IndexerCollector extends MetricsCollector { buckets, }); + // Gas + + this.dispatchGasUsage = new Histogram({ + name: prefix + "_dispatch_gas_usage", + help: "Histogram that tracks gas usage of a transaction that initiated dispatch stage.", + labelNames: ["home", "replica", "environment"], + buckets, + }); + + this.updateGasUsage = new Histogram({ + name: prefix + "_update_gas_usage", + help: "Histogram that tracks gas usage of a transaction that initiated update stage.", + labelNames: ["home", "replica", "environment"], + buckets, + }); + + this.relayGasUsage = new Histogram({ + name: prefix + "_relay_gas_usage", + help: "Histogram that tracks gas usage of a transaction that initiated relay stage.", + labelNames: ["home", "replica", "environment"], + buckets, + }); + + this.receiveGasUsage = new Histogram({ + name: prefix + "_receive_gas_usage", + help: "Histogram that tracks gas usage of a transaction that initiated receive stage.", + labelNames: ["home", "replica", "environment"], + buckets, + }); + + this.processGasUsage = new Histogram({ + name: prefix + "_process_gas_usage", + help: "Histogram that tracks gas usage of a transaction that initiated process stage.", + labelNames: ["home", "replica", "environment"], + buckets, + }); // Home Health @@ -227,16 +269,33 @@ export class IndexerCollector extends MetricsCollector { ); } - observeUpdate(home: string, replica: string, ms: number) { + observeUpdateLatency(home: string, replica: string, ms: number) { this.updateLatency.labels(home, replica, this.environment).observe(ms) } - observeRelayed(home: string, replica: string, ms: number) { + observeRelayLatency(home: string, replica: string, ms: number) { this.relayLatency.labels(home, replica, this.environment).observe(ms) } - observeProcessed(home: string, replica: string, ms: number) { + observeProcessLatency(home: string, replica: string, ms: number) { this.processLatency.labels(home, replica, this.environment).observe(ms) } - observeE2E(home: string, replica: string, ms: number) { + observeE2ELatency(home: string, replica: string, ms: number) { this.end2EndLatency.labels(home, replica, this.environment).observe(ms) } + + observeDispatchGasUsage(home: string, replica: string, gas: number) { + this.dispatchGasUsage.labels(home, replica, this.environment).observe(gas) + } + observeUpdateGasUsage(home: string, replica: string, gas: number) { + this.updateGasUsage.labels(home, replica, this.environment).observe(gas) + } + observeRelayGasUsage(home: string, replica: string, gas: number) { + this.relayGasUsage.labels(home, replica, this.environment).observe(gas) + } + observeReceiveGasUsage(home: string, replica: string, gas: number) { + this.receiveGasUsage.labels(home, replica, this.environment).observe(gas) + } + observeProcessGasUsage(home: string, replica: string, gas: number) { + this.processGasUsage.labels(home, replica, this.environment).observe(gas) + } + } diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index 0ccb41210..0b6108e1e 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -138,23 +138,39 @@ export class Orchestrator { } subscribeStatisticEvents() { - this.consumer.on('updated', (home: number, replica: number ,ms: number) => { + + this.consumer.on('dispatched', (home: number, replica: number, gas: number) => { + const homeName = this.domain2name(home); + const replicaName = this.domain2name(replica); + this.metrics.observeDispatchGasUsage(homeName, replicaName, gas); + }) + + this.consumer.on('updated', (home: number, replica: number ,ms: number, gas: number) => { + const homeName = this.domain2name(home); + const replicaName = this.domain2name(replica); + this.metrics.observeUpdateLatency(homeName, replicaName, ms) + this.metrics.observeUpdateGasUsage(homeName, replicaName, gas); + }) + + this.consumer.on('relayed', (home: number, replica: number ,ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeUpdate(homeName, replicaName, ms) + this.metrics.observeRelayLatency(homeName, replicaName, ms) + this.metrics.observeRelayGasUsage(homeName, replicaName, gas); }) - this.consumer.on('relayed', (home: number, replica: number ,ms: number) => { + this.consumer.on('received', (home: number, replica: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeRelayed(homeName, replicaName, ms) + this.metrics.observeReceiveGasUsage(homeName, replicaName, gas); }) - this.consumer.on('processed', (home: number, replica: number ,ms: number, e2e: number) => { + this.consumer.on('processed', (home: number, replica: number ,ms: number, e2e: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeProcessed(homeName, replicaName, ms) - this.metrics.observeE2E(homeName, replicaName, e2e) + this.metrics.observeProcessLatency(homeName, replicaName, ms) + this.metrics.observeE2ELatency(homeName, replicaName, e2e) + this.metrics.observeProcessGasUsage(homeName, replicaName, gas); }) } diff --git a/tools/indexer/prisma/migrations/20220203202333_init/migration.sql b/tools/indexer/prisma/migrations/20220203202333_init/migration.sql index c8072099b..ff2f38a23 100644 --- a/tools/indexer/prisma/migrations/20220203202333_init/migration.sql +++ b/tools/indexer/prisma/migrations/20220203202333_init/migration.sql @@ -36,6 +36,11 @@ CREATE TABLE "messages" ( "body" VARCHAR NOT NULL, "leaf_index" VARCHAR(256) NOT NULL, "tx" VARCHAR(66), + "gas_at_dispatch" VARCHAR(256), + "gas_at_update" VARCHAR(256), + "gas_at_relay" VARCHAR(256), + "gas_at_receive" VARCHAR(256), + "gas_at_process" VARCHAR(256), "created_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "messages_pkey" PRIMARY KEY ("id") diff --git a/tools/indexer/prisma/schema.prisma b/tools/indexer/prisma/schema.prisma index 6bf4b3cc7..cb457bb88 100644 --- a/tools/indexer/prisma/schema.prisma +++ b/tools/indexer/prisma/schema.prisma @@ -44,6 +44,11 @@ model messages { body String @db.VarChar leafIndex String @map("leaf_index") @db.VarChar(256) tx String? @db.VarChar(66) + gasAtDispatch String? @map("gas_at_dispatch") @db.VarChar(256) + gasAtUpdate String? @map("gas_at_update") @db.VarChar(256) + gasAtRelay String? @map("gas_at_relay") @db.VarChar(256) + gasAtReceive String? @map("gas_at_receive") @db.VarChar(256) + gasAtProcess String? @map("gas_at_process") @db.VarChar(256) createdAt DateTime @map("created_at") @default(now()) @db.Timestamp(6) @@index([id], map: "messages_id_index") From 08a5f2180693dc38cf6226e424dda60ec7009a6e Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Fri, 11 Feb 2022 12:52:37 +0100 Subject: [PATCH 53/63] refactor: metrics --- tools/indexer/core/consumer.ts | 138 ++++-------------- tools/indexer/core/index.ts | 2 +- tools/indexer/core/metrics.ts | 227 +++++------------------------ tools/indexer/core/orchestrator.ts | 88 +++++------ 4 files changed, 99 insertions(+), 356 deletions(-) diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index 2ca24e63f..ac754654c 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -40,52 +40,6 @@ class StatisticsCollector { this.s.counts.domainStatistics.get(domain)!.processed += 1; } - contributeUpdateTimings(m: NomadMessage) { - const inUpdateStat = m.timings.inUpdated(); - if (inUpdateStat) { - this.s.timings.total.meanUpdate.add(inUpdateStat); - this.s.timings.domainStatistics - .get(m.origin)! - .meanUpdate.add(inUpdateStat); - } - } - - contributeRelayTimings(m: NomadMessage) { - this.contributeUpdateTimings(m); - const inRelayStat = m.timings.inRelayed(); - if (inRelayStat) { - this.s.timings.total.meanRelay.add(inRelayStat); - this.s.timings.domainStatistics.get(m.origin)!.meanRelay.add(inRelayStat); - } - } - - contributeReceiveTimings(m: NomadMessage) { - this.contributeRelayTimings(m); - const inReceiveStat = m.timings.inReceived(); - if (inReceiveStat) { - this.s.timings.total.meanReceive.add(inReceiveStat); - this.s.timings.domainStatistics - .get(m.origin)! - .meanReceive.add(inReceiveStat); - } - } - - contributeProcessTimings(m: NomadMessage) { - this.contributeReceiveTimings(m); - const inProcessStat = m.timings.inProcessed(); - if (inProcessStat) { - this.s.timings.total.meanProcess.add(inProcessStat); - this.s.timings.domainStatistics - .get(m.origin)! - .meanProcess.add(inProcessStat); - } - - const e2e = m.timings.e2e(); - if (e2e) { - this.s.timings.total.meanE2E.add(e2e); - this.s.timings.domainStatistics.get(m.origin)!.meanE2E.add(e2e); - } - } contributeToCount(m: NomadMessage) { switch (m.state) { @@ -109,25 +63,6 @@ class StatisticsCollector { } } - contributeToTime(m: NomadMessage) { - switch (m.state) { - case MsgState.Updated: - this.contributeUpdateTimings(m); - break; - case MsgState.Relayed: - this.contributeRelayTimings(m); - break; - case MsgState.Received: - this.contributeReceiveTimings(m); - break; - case MsgState.Processed: - this.contributeProcessTimings(m); - break; - default: - break; - } - } - stats(): Statistics { return this.s; } @@ -146,7 +81,6 @@ enum MsgState { Processed, } - class GasUsed { dispatch: ethers.BigNumber; update: ethers.BigNumber; @@ -222,21 +156,21 @@ class Timings { this.processedAt = ts; } - inUpdated(): number | undefined { + toUpdate(): number | undefined { if (this.updatedAt) { return this.updatedAt - this.dispatchedAt; } return undefined; } - inRelayed(): number | undefined { + toRelay(): number | undefined { if (this.relayedAt) { return this.relayedAt - (this.updatedAt || this.dispatchedAt); // because of the problem with time that it is not ideal from RPC we could have skipped some stages. we take the last available } return undefined; } - inReceived(): number | undefined { + toReceive(): number | undefined { if (this.receivedAt) { return ( this.receivedAt - @@ -246,11 +180,11 @@ class Timings { return undefined; } - inProcessed(): number | undefined { + toProcess(): number | undefined { if (this.processedAt) { return ( this.processedAt - - (this.receivedAt || + ( // Attention: this.receivedAt is not what we are interested here this.relayedAt || this.updatedAt || this.dispatchedAt) @@ -259,26 +193,13 @@ class Timings { return undefined; } - e2e(): number | undefined { - if (this.processedAt) { - return ( - this.processedAt - - (this.dispatchedAt || - this.updatedAt || - this.relayedAt || - this.receivedAt) - ); // same as for .inRelayed() and .inProcessed() but opposit order - } - return undefined; - } - serialize() { return { - dispatchedAt: this.dispatchedAt/1000, - updatedAt: this.updatedAt/1000, - relayedAt: this.relayedAt/1000, - processedAt: this.processedAt/1000, - receivedAt: this.receivedAt/1000, + dispatchedAt: Math.floor(this.dispatchedAt/1000), + updatedAt: Math.floor(this.updatedAt/1000), + relayedAt: Math.floor(this.relayedAt/1000), + processedAt: Math.floor(this.processedAt/1000), + receivedAt: Math.floor(this.receivedAt/1000), } } @@ -373,7 +294,8 @@ export class NomadMessage { // destinationAndNonce: ethers.BigNumber, body: string, dispatchedAt: number, - dispatchBlock: number + dispatchBlock: number, + gasUsed?: GasUsed, ) { this.origin = origin; this.destination = destination; @@ -393,7 +315,7 @@ export class NomadMessage { this.state = MsgState.Dispatched; this.timings = new Timings(dispatchedAt); this.dispatchBlock = dispatchBlock; - this.gasUsed = new GasUsed(); + this.gasUsed = gasUsed || new GasUsed(); } // PADDED! @@ -673,24 +595,18 @@ export class Processor extends Consumer { async getMsgForSync(): Promise<[NomadMessage[], NomadMessage[]]> { let existingHashes = await this.db.getExistingHashes(); - - const insert: string[] = []; - const update: string[] = []; - - this.syncQueue.forEach((hash) => { - if (existingHashes.indexOf(hash) < 0) { - insert.push(hash); - } else { - update.push(hash); - } - }); + + const msgsForSync = this.syncQueue.reduce((acc: [string[], string[]], hash, i) => { + existingHashes.indexOf(hash) < 0 ? acc[0].push(hash) : acc[1].push(hash); + return acc; + }, [[],[]]).map(this.hash2msg.bind(this)) as [NomadMessage[], NomadMessage[]]; this.syncQueue = []; - return [this.mapHashesToMessages(insert), this.mapHashesToMessages(update)]; + return msgsForSync; } - mapHashesToMessages(hashes: string[]): NomadMessage[] { + hash2msg(hashes: string[]): NomadMessage[] { return hashes.map((hash) => this.getMsg(hash)!).filter((m) => !!m); } @@ -711,7 +627,7 @@ export class Processor extends Consumer { this.add(m); this.addToSyncQueue(m.messageHash); - this.emit(`dispatched`, m.origin, m.destination, e.gasUsed) + this.emit(`dispatched`, m.origin, m.destination, e.gasUsed.toNumber()) if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } @@ -725,7 +641,7 @@ export class Processor extends Consumer { m.timings.updated(e.ts); m.gasUsed.update = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`updated`, m.origin, m.destination, m.timings.inUpdated(), e.gasUsed) + this.emit(`updated`, m.origin, m.destination, m.timings.toUpdate(), e.gasUsed.toNumber()); } }); } @@ -742,7 +658,7 @@ export class Processor extends Consumer { m.timings.relayed(e.ts); m.gasUsed.relay = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`relayed`, m.origin, m.destination, m.timings.inRelayed(), e.gasUsed) + this.emit(`relayed`, m.origin, m.destination, m.timings.toRelay(), e.gasUsed.toNumber()); } }); } @@ -755,7 +671,7 @@ export class Processor extends Consumer { m.timings.processed(e.ts); m.gasUsed.process = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`processed`, m.origin, m.destination, m.timings.inProcessed(), m.timings.e2e(), e.gasUsed) + this.emit(`processed`, m.origin, m.destination, m.timings.toProcess(), e.gasUsed.toNumber()) } } } @@ -776,7 +692,7 @@ export class Processor extends Consumer { m.timings.received(e.ts); m.gasUsed.receive = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`received`, e.gasUsed) + this.emit(`received`, m.timings.toReceive(), e.gasUsed.toNumber()); } } } @@ -825,10 +741,6 @@ export class Processor extends Consumer { collector.contributeToCount(m); }); - this.messages.slice(this.messages.length - 50).forEach((m) => { // Use last 50 message for statistics - collector.contributeToTime(m); - }); - return collector.stats(); } } diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index c2364f29d..e815e5a33 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -32,8 +32,8 @@ export async function run(db: DB, environment: string, logger: Logger) { ctx.registerRpcProvider(domain, rpc); }); - const c = new Processor(db, logger); const m = new IndexerCollector(environment, logger); + const c = new Processor(db, logger); const o = new Orchestrator(ctx, c, ctx.domainNumbers[0], m, logger, db); m.startServer(3000); diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index a0ef48e2a..bd76e2c75 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -1,4 +1,4 @@ -import { Gauge, Histogram } from "prom-client"; +import { Gauge, Histogram, Counter } from "prom-client"; import Logger from "bunyan"; import { register } from "prom-client"; @@ -53,149 +53,48 @@ export class MetricsCollector { const prefix = `fancy_monitor`; export class IndexerCollector extends MetricsCollector { - private numDispatchedGauge: Gauge; - private numUpdatedGauge: Gauge; - private numRelayedGauge: Gauge; - private numProcessedGauge: Gauge; - - private meanUpdateTimeGauge: Gauge; - private meanRelayTimeGauge: Gauge; - private meanProcessTimeGauge: Gauge; - private meanEndToEndTimeGauge: Gauge; - - private updateLatency: Histogram; - private relayLatency: Histogram; - private processLatency: Histogram; - private end2EndLatency: Histogram; - - private dispatchGasUsage: Histogram; - private updateGasUsage: Histogram; - private relayGasUsage: Histogram; - private receiveGasUsage: Histogram; - private processGasUsage: Histogram; + private numMessages: Gauge; private homeFailedGauge: Gauge; - constructor(environment: string, logger: Logger) { - super(environment, logger); + private latency: Histogram; - // Count + private gasUsage: Histogram; - this.numDispatchedGauge = new Gauge({ - name: prefix + "_number_messages_dispatched", - help: "Gauge that indicates how many messages have been dispatched for a network.", - labelNames: ["network", "environment"], - }); + // private dbQueries: Histogram; - this.numUpdatedGauge = new Gauge({ - name: prefix + "_number_messages_updated", - help: "Gauge that indicates how many messages have been updated for a network.", - labelNames: ["network", "environment"], - }); - this.numRelayedGauge = new Gauge({ - name: prefix + "_number_messages_relayed", - help: "Gauge that indicates how many messages have been relayed for a network.", - labelNames: ["network", "environment"], - }); - this.numProcessedGauge = new Gauge({ - name: prefix + "_number_messages_processed", - help: "Gauge that indicates how many messages have been processed for a network.", - labelNames: ["network", "environment"], - }); - // Time Gauges - this.meanUpdateTimeGauge = new Gauge({ - name: prefix + "_mean_update_time", - help: "Gauge that indicates how long does it take to move from dispatched to updated.", - labelNames: ["network", "environment"], - }); + constructor(environment: string, logger: Logger) { + super(environment, logger); - this.meanRelayTimeGauge = new Gauge({ - name: prefix + "_mean_relay_time", - help: "Gauge that indicates how long does it take to move from updated to relayed.", - labelNames: ["network", "environment"], - }); + // Count - this.meanProcessTimeGauge = new Gauge({ - name: prefix + "_mean_process_time", - help: "Gauge that indicates how long does it take to move from relayed to processed.", - labelNames: ["network", "environment"], + this.numMessages = new Gauge({ + name: prefix + "_number_messages", + help: "Gauge that indicates how many messages are in dispatch, update, relay, receive or process stages", + labelNames: ["stage", "network", "environment"], }); - this.meanEndToEndTimeGauge = new Gauge({ - name: prefix + "_mean_e2e_time", - help: "Gauge that indicates how long does it take to move from dispatched to processed.", - labelNames: ["network", "environment"], - }); // Time Histograms - this.updateLatency = new Histogram({ - name: prefix + "_update_latency", - help: "Histogram that tracks latency of how long does it take to move from dispatched to updated.", - labelNames: ["home", "replica", "environment"], - buckets, - }); - - this.relayLatency = new Histogram({ - name: prefix + "_relay_latency", - help: "Histogram that tracks latency of how long does it take to move from updated to relayed.", - labelNames: ["home", "replica", "environment"], - buckets, - }); - - this.processLatency = new Histogram({ - name: prefix + "_process_latency", - help: "Histogram that tracks latency of how long does it take to move from relayed to processed.", - labelNames: ["home", "replica", "environment"], - buckets, - }); - - this.end2EndLatency = new Histogram({ - name: prefix + "_end2end_latency", - help: "Histogram that tracks latency of how long does it take to move from dispatched to processed.", - labelNames: ["home", "replica", "environment"], + this.latency = new Histogram({ + name: prefix + "_latency", + help: "Histogram that tracks latency of how long does it take to move between dispatch, update, relay, receive or process stages.", + labelNames: ["stage", "home", "replica", "environment"], buckets, }); // Gas - this.dispatchGasUsage = new Histogram({ - name: prefix + "_dispatch_gas_usage", - help: "Histogram that tracks gas usage of a transaction that initiated dispatch stage.", - labelNames: ["home", "replica", "environment"], - buckets, - }); - - this.updateGasUsage = new Histogram({ - name: prefix + "_update_gas_usage", - help: "Histogram that tracks gas usage of a transaction that initiated update stage.", - labelNames: ["home", "replica", "environment"], - buckets, - }); - - this.relayGasUsage = new Histogram({ - name: prefix + "_relay_gas_usage", - help: "Histogram that tracks gas usage of a transaction that initiated relay stage.", - labelNames: ["home", "replica", "environment"], - buckets, - }); - - this.receiveGasUsage = new Histogram({ - name: prefix + "_receive_gas_usage", - help: "Histogram that tracks gas usage of a transaction that initiated receive stage.", - labelNames: ["home", "replica", "environment"], - buckets, - }); - - this.processGasUsage = new Histogram({ - name: prefix + "_process_gas_usage", - help: "Histogram that tracks gas usage of a transaction that initiated process stage.", - labelNames: ["home", "replica", "environment"], + this.gasUsage = new Histogram({ + name: prefix + "_gas_usage", + help: "Histogram that tracks gas usage of a transaction that initiated at dispatch, update, relay, receive or process stages.", + labelNames: ["stage", "home", "replica", "environment"], buckets, }); @@ -211,91 +110,33 @@ export class IndexerCollector extends MetricsCollector { /** * Sets the state for a bridge. */ - setNetworkState( + setHomeState( network: string, - dispatched: number, - updated: number, - relayed: number, - processed: number, - updateTime: number, - relayTime: number, - processTime: number, - e2eTime: number, homeFailed: boolean ) { - this.numDispatchedGauge.set( - { network, environment: this.environment }, - dispatched - ); - - this.numUpdatedGauge.set( - { network, environment: this.environment }, - updated - ); - - this.numRelayedGauge.set( - { network, environment: this.environment }, - relayed - ); - - this.numProcessedGauge.set( - { network, environment: this.environment }, - processed - ); - - this.meanUpdateTimeGauge.set( - { network, environment: this.environment }, - updateTime - ); - - this.meanRelayTimeGauge.set( - { network, environment: this.environment }, - relayTime - ); - - this.meanProcessTimeGauge.set( - { network, environment: this.environment }, - processTime - ); - - this.meanEndToEndTimeGauge.set( - { network, environment: this.environment }, - e2eTime - ); - this.homeFailedGauge.set( { network, environment: this.environment }, homeFailed ? 1 : 0 ); } - observeUpdateLatency(home: string, replica: string, ms: number) { - this.updateLatency.labels(home, replica, this.environment).observe(ms) - } - observeRelayLatency(home: string, replica: string, ms: number) { - this.relayLatency.labels(home, replica, this.environment).observe(ms) - } - observeProcessLatency(home: string, replica: string, ms: number) { - this.processLatency.labels(home, replica, this.environment).observe(ms) - } - observeE2ELatency(home: string, replica: string, ms: number) { - this.end2EndLatency.labels(home, replica, this.environment).observe(ms) - } + - observeDispatchGasUsage(home: string, replica: string, gas: number) { - this.dispatchGasUsage.labels(home, replica, this.environment).observe(gas) - } - observeUpdateGasUsage(home: string, replica: string, gas: number) { - this.updateGasUsage.labels(home, replica, this.environment).observe(gas) + incNumMessages(stage: string, network: string) { + this.numMessages.labels(stage, network, this.environment).inc() } - observeRelayGasUsage(home: string, replica: string, gas: number) { - this.relayGasUsage.labels(home, replica, this.environment).observe(gas) + decNumMessages(stage: string, network: string) { + this.numMessages.labels(stage, network, this.environment).dec() } - observeReceiveGasUsage(home: string, replica: string, gas: number) { - this.receiveGasUsage.labels(home, replica, this.environment).observe(gas) + setNumMessages(stage: string, network: string, count: number) { + this.numMessages.labels(stage, network, this.environment).set(count) } - observeProcessGasUsage(home: string, replica: string, gas: number) { - this.processGasUsage.labels(home, replica, this.environment).observe(gas) + + observeLatency(stage: string, home: string, replica: string, ms: number) { + this.latency.labels(stage, home, replica, this.environment).observe(ms) } + observeGasUsage(stage: string, home: string, replica: string, gas: number) { + this.gasUsage.labels(stage, home, replica, this.environment).observe(gas) + } } diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index 0b6108e1e..b84c1c269 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -11,29 +11,31 @@ import { replacer, sleep } from "./utils"; class HomeHealth { home: Home; domain: number; - healthy: boolean; logger: Logger; + metrics: IndexerCollector; - constructor(domain: number, ctx: NomadContext, logger: Logger) { + constructor(domain: number, ctx: NomadContext, logger: Logger, metrics: IndexerCollector) { this.domain = domain; this.home = ctx.mustGetCore(domain).home; - this.healthy = true; this.logger = logger; + this.metrics = metrics; } - async updateHealth(): Promise { + async healthy(): Promise { try { const state = await this.home.state(); if (state !== 1) { - this.healthy = false; + return false; } else { - this.healthy = true; + return true; } } catch (e: any) { this.logger.warn( `Couldn't collect home state for ${this.domain} domain. Error: ${e.message}` ); + // TODO! something } + return true; // BAD!!! } get failed(): boolean { @@ -103,14 +105,25 @@ export class Orchestrator { return await indexer.updateAll(replicas); } - async checkAllHealth() { + async collectStatistics() { + const stats = this.consumer.stats(); + await Promise.all( - this.sdk.domainNumbers.map((domain: number) => this.checkHealth(domain)) + this.sdk.domainNumbers.map(async (domain: number) => { + await this.checkHealth(domain); + const network = this.domain2name(domain); + const s = stats.forDomain(domain).counts; + this.metrics.setNumMessages('dispatched', network, s.dispatched); + this.metrics.setNumMessages('updated', network, s.updated); + this.metrics.setNumMessages('relayed', network, s.relayed); + this.metrics.setNumMessages('received', network, s.received); + this.metrics.setNumMessages('processed', network, s.processed); + }) ); } async checkHealth(domain: number) { - await this.healthCheckers.get(domain)!.updateHealth(); + this.metrics.setHomeState(this.domain2name(domain), await this.healthCheckers.get(domain)!.healthy() !== true) } async initalFeedConsumer() { @@ -131,8 +144,7 @@ export class Orchestrator { async initHealthCheckers() { for (const domain of this.sdk.domainNumbers) { - const checker = new HomeHealth(domain, this.sdk, this.logger); - await checker.updateHealth(); + const checker = new HomeHealth(domain, this.sdk, this.logger, this.metrics); this.healthCheckers.set(domain, checker); } } @@ -142,35 +154,35 @@ export class Orchestrator { this.consumer.on('dispatched', (home: number, replica: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeDispatchGasUsage(homeName, replicaName, gas); + this.metrics.observeGasUsage('dispatched', homeName, replicaName, gas); }) this.consumer.on('updated', (home: number, replica: number ,ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeUpdateLatency(homeName, replicaName, ms) - this.metrics.observeUpdateGasUsage(homeName, replicaName, gas); + this.metrics.observeLatency('updated', homeName, replicaName, ms) + this.metrics.observeGasUsage('updated', homeName, replicaName, gas); }) this.consumer.on('relayed', (home: number, replica: number ,ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeRelayLatency(homeName, replicaName, ms) - this.metrics.observeRelayGasUsage(homeName, replicaName, gas); + this.metrics.observeLatency('relayed', homeName, replicaName, ms) + this.metrics.observeGasUsage('relayed', homeName, replicaName, gas); }) - this.consumer.on('received', (home: number, replica: number, gas: number) => { + this.consumer.on('received', (home: number, replica: number, ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeReceiveGasUsage(homeName, replicaName, gas); + this.metrics.observeGasUsage('received', homeName, replicaName, gas); + this.metrics.observeLatency('received', homeName, replicaName, ms) }) this.consumer.on('processed', (home: number, replica: number ,ms: number, e2e: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeProcessLatency(homeName, replicaName, ms) - this.metrics.observeE2ELatency(homeName, replicaName, e2e) - this.metrics.observeProcessGasUsage(homeName, replicaName, gas); + this.metrics.observeLatency('processed', homeName, replicaName, e2e) + this.metrics.observeGasUsage('processed', homeName, replicaName, gas); }) } @@ -178,7 +190,8 @@ export class Orchestrator { while (!this.done) { this.logger.info(`Started to reindex`); const start = new Date().valueOf(); - await Promise.all([this.indexAll(), this.checkAllHealth()]); + await this.indexAll(); + await this.collectStatistics(); if (this.chaseMode) { this.chaseMode = false; this.subscribeStatisticEvents() @@ -190,18 +203,16 @@ export class Orchestrator { } seconds` ); - const stats = this.consumer.stats(); - this.logger.debug(JSON.stringify(stats, replacer)); - - this.reportAllMetrics(stats); + this.reportAllMetrics(); await sleep(30000); } } - reportAllMetrics(statistics: Statistics) { + reportAllMetrics() { for (const domain of this.sdk.domainNumbers) { - this.reportMetrics(domain, statistics); + const network = this.domain2name(domain); + this.metrics.setHomeState(network, this.healthCheckers.get(domain)!.failed); } } @@ -209,27 +220,6 @@ export class Orchestrator { return this.sdk.getDomain(domain)!.name } - reportMetrics(domain: number, statistics: Statistics) { - const { - counts: { dispatched, updated, relayed, processed }, - timings: { meanUpdate, meanRelay, meanProcess, meanE2E }, - } = statistics.forDomain(domain); - - const homeFailed = this.healthCheckers.get(domain)!.failed; - this.metrics.setNetworkState( - this.domain2name(domain), - dispatched, - updated, - relayed, - processed, - meanUpdate, - meanRelay, - meanProcess, - meanE2E, - homeFailed - ); - } - stop() { this.done = true; } From c70a808303f0bae3d30338d161ba80742bfbf8fb Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Fri, 11 Feb 2022 21:17:39 +0100 Subject: [PATCH 54/63] fix: pagination --- tools/indexer/core/indexer.ts | 6 +++++- tools/indexer/core/orchestrator.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index c33f70abf..23c6d757f 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -87,6 +87,7 @@ export class Indexer { txCache: KVCache; txReceiptCache: KVCache; limit: pLimit.Limit; + lastBlock: number; eventCallback: undefined | ((event: NomadEvent) => void); @@ -103,6 +104,7 @@ export class Indexer { this.txReceiptCache = new KVCache('txr_' + String(this.domain), this.orchestrator.db); // 20 concurrent requests per indexer this.limit = pLimit(100); + this.lastBlock = 0; } get provider(): ethers.providers.Provider { @@ -318,6 +320,7 @@ export class Indexer { async updateAll(replicas: number[]) { let from = Math.max( + this.lastBlock, this.persistance.height, this.sdk.getDomain(this.domain)?.paginate?.from || 0 ); @@ -357,7 +360,7 @@ export class Indexer { const batchSize = BATCH_SIZE; let batchFrom = from; - let batchTo = from + batchSize; + let batchTo = Math.min(to, from + batchSize); while (true) { const done = Math.floor((batchTo-from)/(to-from) * 100); @@ -391,6 +394,7 @@ export class Indexer { this.dummyTestEventsIntegrity(); this.orchestrator.logger.info(`Fetched all for domain ${this.domain}`); + this.lastBlock = to; return allEvents; } diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index b84c1c269..3e5c77bcb 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -174,8 +174,8 @@ export class Orchestrator { this.consumer.on('received', (home: number, replica: number, ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeGasUsage('received', homeName, replicaName, gas); this.metrics.observeLatency('received', homeName, replicaName, ms) + this.metrics.observeGasUsage('received', homeName, replicaName, gas); }) this.consumer.on('processed', (home: number, replica: number ,ms: number, e2e: number, gas: number) => { From 3706672b4ba4ebffc249289aea76393b3c253f3f Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 14 Feb 2022 10:36:58 +0100 Subject: [PATCH 55/63] fix: reorg --- tools/indexer/core/consumer.ts | 20 ++++++++++---- tools/indexer/core/indexer.ts | 12 +++++--- tools/indexer/core/orchestrator.ts | 44 ++++++++++++++++++------------ 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index ac754654c..341d8b6bc 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -627,7 +627,9 @@ export class Processor extends Consumer { this.add(m); this.addToSyncQueue(m.messageHash); - this.emit(`dispatched`, m.origin, m.destination, e.gasUsed.toNumber()) + const gas = e.gasUsed.toNumber(); + // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); + this.emit(`dispatched`, m.origin, m.destination, gas) if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } @@ -641,7 +643,9 @@ export class Processor extends Consumer { m.timings.updated(e.ts); m.gasUsed.update = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`updated`, m.origin, m.destination, m.timings.toUpdate(), e.gasUsed.toNumber()); + const gas = e.gasUsed.toNumber(); + // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); + this.emit(`updated`, m.origin, m.destination, m.timings.toUpdate(), gas); } }); } @@ -658,7 +662,9 @@ export class Processor extends Consumer { m.timings.relayed(e.ts); m.gasUsed.relay = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`relayed`, m.origin, m.destination, m.timings.toRelay(), e.gasUsed.toNumber()); + const gas = e.gasUsed.toNumber(); + // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); + this.emit(`relayed`, m.origin, m.destination, m.timings.toRelay(), gas); } }); } @@ -671,7 +677,9 @@ export class Processor extends Consumer { m.timings.processed(e.ts); m.gasUsed.process = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`processed`, m.origin, m.destination, m.timings.toProcess(), e.gasUsed.toNumber()) + const gas = e.gasUsed.toNumber(); + // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); + this.emit(`processed`, m.origin, m.destination, m.timings.toProcess(), gas) } } } @@ -692,7 +700,9 @@ export class Processor extends Consumer { m.timings.received(e.ts); m.gasUsed.receive = e.gasUsed; this.addToSyncQueue(m.messageHash); - this.emit(`received`, m.timings.toReceive(), e.gasUsed.toNumber()); + const gas = e.gasUsed.toNumber(); + // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); + this.emit(`received`, m.origin, m.destination, m.timings.toReceive(), gas); } } } diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 23c6d757f..5109b3161 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -1,6 +1,5 @@ import { Orchestrator } from "./orchestrator"; import { NomadContext } from "@nomad-xyz/sdk/dist"; -import { getEvents } from "@nomad-xyz/sdk/dist/nomad/events/fetch" import fs from "fs"; import { ContractType, EventType, NomadEvent, EventSource } from "./event"; import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; @@ -8,8 +7,6 @@ import { ethers } from "ethers"; import { KVCache, replacer, retry, reviver } from "./utils"; import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; -import { TypedEvent } from "@nomad-xyz/contract-interfaces/core/commons"; -import { Result } from "ethers/lib/utils"; type ShortTx = { gasPrice?: ethers.BigNumber, @@ -340,6 +337,13 @@ export class Indexer { ); } + if (from === to) { + this.orchestrator.logger.info( + `Skipped fetching events for domain ${this.domain} from: ${from}, to: ${to}, because from and to are the same` + ); + return []; + } + this.orchestrator.logger.info( `Fetching events for domain ${this.domain} from: ${from}, to: ${to}` ); @@ -363,7 +367,7 @@ export class Indexer { let batchTo = Math.min(to, from + batchSize); while (true) { - const done = Math.floor((batchTo-from)/(to-from) * 100); + const done = Math.floor((batchTo-from+1)/(to-from+1) * 100); this.orchestrator.logger.debug( `Fetching batch of events for domain ${this.domain} from: ${batchFrom}, to: ${batchTo}, [${done}%]` ); diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index 3e5c77bcb..aef97fb93 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -81,7 +81,7 @@ export class Orchestrator { await this.initalFeedConsumer(); } - async indexAll() { + async indexAll(): Promise { const events = ( await Promise.all( this.sdk.domainNumbers.map((domain: number) => this.index(domain)) @@ -90,6 +90,7 @@ export class Orchestrator { events.sort((a, b) => a.ts - b.ts); this.logger.info(`Received ${events.length} events after reindexing`); await this.consumer.consume(...events); + return events.length } async index(domain: number) { @@ -105,21 +106,24 @@ export class Orchestrator { return await indexer.updateAll(replicas); } - async collectStatistics() { + collectStatistics() { const stats = this.consumer.stats(); - await Promise.all( - this.sdk.domainNumbers.map(async (domain: number) => { - await this.checkHealth(domain); - const network = this.domain2name(domain); - const s = stats.forDomain(domain).counts; - this.metrics.setNumMessages('dispatched', network, s.dispatched); - this.metrics.setNumMessages('updated', network, s.updated); - this.metrics.setNumMessages('relayed', network, s.relayed); - this.metrics.setNumMessages('received', network, s.received); - this.metrics.setNumMessages('processed', network, s.processed); - }) - ); + this.sdk.domainNumbers.forEach(async (domain: number) => { + const network = this.domain2name(domain); + const s = stats.forDomain(domain).counts; + this.metrics.setNumMessages('dispatched', network, s.dispatched); + this.metrics.setNumMessages('updated', network, s.updated); + this.metrics.setNumMessages('relayed', network, s.relayed); + this.metrics.setNumMessages('received', network, s.received); + this.metrics.setNumMessages('processed', network, s.processed); + }) + } + + async checkAllHealth() { + await Promise.all(this.sdk.domainNumbers.map(async (domain: number) => { + await this.checkHealth(domain); + })) } async checkHealth(domain: number) { @@ -178,7 +182,7 @@ export class Orchestrator { this.metrics.observeGasUsage('received', homeName, replicaName, gas); }) - this.consumer.on('processed', (home: number, replica: number ,ms: number, e2e: number, gas: number) => { + this.consumer.on('processed', (home: number, replica: number, e2e: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); this.metrics.observeLatency('processed', homeName, replicaName, e2e) @@ -190,8 +194,12 @@ export class Orchestrator { while (!this.done) { this.logger.info(`Started to reindex`); const start = new Date().valueOf(); - await this.indexAll(); - await this.collectStatistics(); + const eventsLength = await this.indexAll(); + await this.checkAllHealth(); + + if (eventsLength > 0) this.collectStatistics(); + + if (this.chaseMode) { this.chaseMode = false; this.subscribeStatisticEvents() @@ -205,7 +213,7 @@ export class Orchestrator { this.reportAllMetrics(); - await sleep(30000); + await sleep(5000); } } From 1bc7a18a6f1dc52e56353a6b4d78351eabb9bb84 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 14 Feb 2022 11:26:05 +0100 Subject: [PATCH 56/63] feature: rpc and db metrics --- tools/indexer/core/db.ts | 22 +++++++++++++++++--- tools/indexer/core/index.ts | 7 +++---- tools/indexer/core/indexer.ts | 26 ++++++++++++++++++++---- tools/indexer/core/metrics.ts | 38 ++++++++++++++++++++++++++++++++++- tools/indexer/main.ts | 16 ++++++++++++--- 5 files changed, 94 insertions(+), 15 deletions(-) diff --git a/tools/indexer/core/db.ts b/tools/indexer/core/db.ts index 7c0ff1d8a..f5031bf96 100644 --- a/tools/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -2,6 +2,7 @@ import { NomadMessage } from "./consumer"; import { messages, Prisma, PrismaClient } from '@prisma/client' import { BigNumber } from "ethers"; +import { DbRequestType, IndexerCollector } from "./metrics"; // function fromDb(m: messages): NomadMessage { // return @@ -51,10 +52,12 @@ export interface MsgRequest { export class DB { client: PrismaClient; syncedOnce: boolean; + metrics: IndexerCollector; - constructor() { + constructor(metrics: IndexerCollector) { this.syncedOnce = false; this.client = new PrismaClient(); + this.metrics = metrics; } async connect() { @@ -71,6 +74,7 @@ export class DB { } async getMessageByEvm(tx: string): Promise { + this.metrics.incDbRequests(DbRequestType.Select); const messages = await this.client.messages.findMany({ where: { tx @@ -81,6 +85,7 @@ export class DB { } async getMessageByHash(messageHash: string): Promise { + this.metrics.incDbRequests(DbRequestType.Select); const message = await this.client.messages.findUnique({ where: { messageHash @@ -95,6 +100,7 @@ export class DB { const page = req.page || 1; const skip = (page || -1) * take; + this.metrics.incDbRequests(DbRequestType.Select); const messages = await this.client.messages.findMany({ where: { sender: req.sender, @@ -112,16 +118,20 @@ export class DB { async insertMessage(messages: NomadMessage[]) { if (!messages.length) return; - return await this.client.messages.createMany({ + this.metrics.incDbRequests(DbRequestType.Insert); + await this.client.messages.createMany({ data: messages.map(message => message.serialize()), skipDuplicates: true, }) + + return; } async updateMessage(messages: NomadMessage[]) { if (!messages.length) return; - return await Promise.all(messages.map(m => { + await Promise.all(messages.map(m => { + this.metrics.incDbRequests(DbRequestType.Update); this.client.messages.update({ where: { messageHash: m.messageHash @@ -129,9 +139,12 @@ export class DB { data: m.serialize(), }) })); + + return } async getExistingHashes(): Promise { + this.metrics.incDbRequests(DbRequestType.Select); const rows = await this.client.messages.findMany({ select: { messageHash: true @@ -141,6 +154,7 @@ export class DB { } async getAllKeyPair(namespace: string): Promise> { + this.metrics.incDbRequests(DbRequestType.Select); const rows = await this.client.kv_storage.findMany({ select: { key: true, @@ -154,6 +168,7 @@ export class DB { } async getKeyPair(namespace: string, key: string): Promise { + this.metrics.incDbRequests(DbRequestType.Select); const row = await this.client.kv_storage.findUnique({ select: { value: true @@ -182,6 +197,7 @@ export class DB { const update: Prisma.kv_storageUpdateInput = { value }; + this.metrics.incDbRequests(DbRequestType.Upsert); await this.client.kv_storage.upsert({ where, update, diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index e815e5a33..9c61c9868 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -7,7 +7,7 @@ import { DB } from "./db"; import Logger from "bunyan"; dotenv.config({}); -export async function run(db: DB, environment: string, logger: Logger) { +export async function run(db: DB, environment: string, logger: Logger, metrics: IndexerCollector) { let ctx: NomadContext; if (environment === "production") { ctx = mainnet; @@ -32,11 +32,10 @@ export async function run(db: DB, environment: string, logger: Logger) { ctx.registerRpcProvider(domain, rpc); }); - const m = new IndexerCollector(environment, logger); const c = new Processor(db, logger); - const o = new Orchestrator(ctx, c, ctx.domainNumbers[0], m, logger, db); - m.startServer(3000); + const o = new Orchestrator(ctx, c, ctx.domainNumbers[0], metrics, logger, db); + await o.init(); await o.startConsuming(); } diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 5109b3161..bd013f770 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -7,6 +7,7 @@ import { ethers } from "ethers"; import { KVCache, replacer, retry, reviver } from "./utils"; import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; +import { RpcRequestIdentificator } from "./metrics"; type ShortTx = { gasPrice?: ethers.BigNumber, @@ -108,6 +109,10 @@ export class Indexer { return this.sdk.getProvider(this.domain)!; } + get network(): string { + return this.orchestrator.domain2name(this.domain); + } + async getBlockInfoLegacy( blockNumber: number ): Promise<[number, Map]> { @@ -123,7 +128,10 @@ export class Indexer { const [block, error] = await retry( async () => { - return await this.limit(() => this.provider.getBlockWithTransactions(blockNumber)) + return await this.limit(() => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockWithTxs, this.network) + return this.provider.getBlockWithTransactions(blockNumber) + }) }, RETRIES, (error: any) => @@ -160,7 +168,10 @@ export class Indexer { const [block, error] = await retry( async () => { - return await this.limit(() => this.provider.getBlock(blockNumber)) + return await this.limit(() => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlock, this.network) + return this.provider.getBlock(blockNumber) + }) }, RETRIES, (error: any) => @@ -197,7 +208,10 @@ export class Indexer { const [tx, error] = await retry( async () => { - return await this.limit(() => this.provider.getTransaction(hash)) + return await this.limit(() => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTx, this.network) + return this.provider.getTransaction(hash) + }) }, RETRIES, (error: any) => @@ -256,7 +270,10 @@ export class Indexer { const [receipt, error] = await retry( async () => { - return await this.limit(() => this.provider.getTransactionReceipt(hash)) + return await this.limit(() => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTxReceipt, this.network) + return this.provider.getTransactionReceipt(hash) + }) }, RETRIES, (error: any) => @@ -323,6 +340,7 @@ export class Indexer { ); const [to, error] = await retry( async () => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network) return await this.provider.getBlockNumber() }, RETRIES, diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index bd76e2c75..d10571f6d 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -4,6 +4,21 @@ import Logger from "bunyan"; import { register } from "prom-client"; import express, { Response } from "express"; +export enum RpcRequestIdentificator { + GetBlock = 'get_block', + GetBlockWithTxs = 'get_block_with_transactions', + GetTx = 'get_transaction', + GetTxReceipt = 'get_transaction_receipt', + GetBlockNumber = 'get_block_number', +}; + +export enum DbRequestType { + Select = 'select', + Insert = 'insert', + Update = 'update', + Upsert = 'upsert', +}; + const buckets = [ 1 * 60, // 1 min 5 * 60, // 5 min @@ -61,7 +76,8 @@ export class IndexerCollector extends MetricsCollector { private gasUsage: Histogram; - // private dbQueries: Histogram; + private dbRequests: Counter; + private rpcRequests: Counter; @@ -78,6 +94,18 @@ export class IndexerCollector extends MetricsCollector { labelNames: ["stage", "network", "environment"], }); + this.dbRequests = new Counter({ + name: prefix + "_db_requests", + help: "Count that indicates how many requests are coming to the db", + labelNames: ["type", "environment"], + }); + + this.rpcRequests = new Counter({ + name: prefix + "_rpc_requests", + help: "Count that indicates how many PRC requests are made", + labelNames: ["identificator", "network", "environment"], + }); + // Time Histograms @@ -139,4 +167,12 @@ export class IndexerCollector extends MetricsCollector { observeGasUsage(stage: string, home: string, replica: string, gas: number) { this.gasUsage.labels(stage, home, replica, this.environment).observe(gas) } + + incDbRequests(type: DbRequestType, req?: number) { + this.dbRequests.labels(type, this.environment).inc(req) + } + + incRpcRequests(identificator: RpcRequestIdentificator, network: string, req?: number) { + this.rpcRequests.labels(identificator, network, this.environment).inc(req) + } } diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts index 5d96667a0..dba92e95e 100644 --- a/tools/indexer/main.ts +++ b/tools/indexer/main.ts @@ -2,6 +2,7 @@ import * as core from "./core"; import * as api from "./api"; import { DB } from "./core/db"; import { createLogger } from "./core/utils"; +import { IndexerCollector } from "./core/metrics"; export type NomadEnvironment = "development" | "staging" | "production"; export type Program = "api" | "core"; @@ -10,14 +11,23 @@ const environment = process.env.ENVIRONMENT! as NomadEnvironment; const program = process.env.PROGRAM! as Program; (async () => { - const db = new DB(); + const logger = createLogger("indexer", environment); + const m = new IndexerCollector(environment, logger); + m.startServer(3000); + + const db = new DB(m); await db.connect(); - const logger = createLogger("indexer", environment); if (program === "api") { await api.run(db, logger); } else if (program === "core") { - await core.run(db, environment, logger); + await core.run(db, environment, logger, m); + } else { + logger.warn(`Started both indexer and api on the same process.`); + await Promise.all([ + api.run(db, logger), + core.run(db, environment, logger, m), + ]); } })(); From 556e092fc7e320bc2bd00ac079a17d2ea35eda6d Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Mon, 14 Feb 2022 18:25:52 +0100 Subject: [PATCH 57/63] feature: rpc metrics --- tools/indexer/.gitignore | 2 +- tools/indexer/core/indexer.ts | 129 +++++++++++++++++++++++++--------- tools/indexer/core/metrics.ts | 27 ++++++- tools/indexer/main.ts | 3 +- 4 files changed, 124 insertions(+), 37 deletions(-) diff --git a/tools/indexer/.gitignore b/tools/indexer/.gitignore index 32323003e..9c54ebf91 100644 --- a/tools/indexer/.gitignore +++ b/tools/indexer/.gitignore @@ -1,4 +1,4 @@ node_modules # Keep environment variables out of version control .env -postgres-data \ No newline at end of file +postgres-data* diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index bd013f770..a1847da93 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -128,16 +128,22 @@ export class Indexer { const [block, error] = await retry( async () => { - return await this.limit(() => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockWithTxs, this.network) - return this.provider.getBlockWithTransactions(blockNumber) + return await this.limit(async () => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockWithTxs, this.network); + const start = new Date().valueOf(); + const r = await this.provider.getBlockWithTransactions(blockNumber); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockWithTxs, this.network, new Date().valueOf() - start); + return r; }) }, RETRIES, (error: any) => - this.orchestrator.logger.warn( + { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockWithTxs, this.network, error.code); + this.orchestrator.logger.warn( `Retrying after RPC Error... Block: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` ) + } ); if (!block) { throw new Error( @@ -168,16 +174,22 @@ export class Indexer { const [block, error] = await retry( async () => { - return await this.limit(() => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlock, this.network) - return this.provider.getBlock(blockNumber) + return await this.limit(async () => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlock, this.network); + const start = new Date().valueOf(); + const r = await this.provider.getBlock(blockNumber) + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlock, this.network, new Date().valueOf() - start); + return r; }) }, RETRIES, (error: any) => - this.orchestrator.logger.warn( + { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlock, this.network, error.code); + this.orchestrator.logger.warn( `Retrying after RPC Error... Block number: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` ) + } ); if (!block) { throw new Error( @@ -208,16 +220,22 @@ export class Indexer { const [tx, error] = await retry( async () => { - return await this.limit(() => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTx, this.network) - return this.provider.getTransaction(hash) + return await this.limit(async () => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTx, this.network); + const start = new Date().valueOf(); + const r = await this.provider.getTransaction(hash) + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetTx, this.network, new Date().valueOf() - start); + return r; }) }, RETRIES, (error: any) => - this.orchestrator.logger.warn( + { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetTx, this.network, error.code); + this.orchestrator.logger.warn( `Retrying after RPC Error... TX hash: ${hash}, Domain: ${this.domain}, Error: ${error.code}` ) + } ); if (!tx) { throw new Error( @@ -270,16 +288,22 @@ export class Indexer { const [receipt, error] = await retry( async () => { - return await this.limit(() => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTxReceipt, this.network) - return this.provider.getTransactionReceipt(hash) + return await this.limit(async () => { + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTxReceipt, this.network); + const start = new Date().valueOf(); + const r = await this.provider.getTransactionReceipt(hash) + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetTxReceipt, this.network, new Date().valueOf() - start); + return r; }) }, RETRIES, (error: any) => + { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetTxReceipt, this.network, error.code) this.orchestrator.logger.warn( `Retrying after RPC Error... TX hash: ${hash}, Domain: ${this.domain}, Error: ${error.code}` ) + } ); if (!receipt) { throw new Error( @@ -340,14 +364,20 @@ export class Indexer { ); const [to, error] = await retry( async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network) - return await this.provider.getBlockNumber() + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + const start = new Date().valueOf(); + const r = await this.provider.getBlockNumber() + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + return r; }, RETRIES, (error: any) => - this.orchestrator.logger.warn( - `Retrying after RPC Error on .getBlockNumber() method... Error: ${error}` - ) + { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, error.code) + this.orchestrator.logger.warn( + `Retrying after RPC Error on .getBlockNumber() method... Error: ${error}` + ) + } ); if (!to) { throw new Error( @@ -507,8 +537,15 @@ export class Indexer { // to // ) const [events, error] = await retry(async () => { - return await br.queryFilter(br.filters.Send(), from, to); - }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + const start = new Date().valueOf(); + const r = await br.queryFilter(br.filters.Send(), from, to); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + return r; + }, RETRIES, (e) => { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) + this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`) + }) if (error) { this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -556,8 +593,14 @@ export class Indexer { // ) const [events, error] = await retry(async () => { - return await br.queryFilter(br.filters.Receive(), from, to); - }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + const start = new Date().valueOf(); + const r = await br.queryFilter(br.filters.Receive(), from, to); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + return r; + }, RETRIES, (e) => { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code); + this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) if (error) { this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -612,8 +655,14 @@ export class Indexer { // to // ) const [events, error] = await retry(async () => { - return await home.queryFilter(home.filters.Dispatch(), from, to); - }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + const start = new Date().valueOf(); + const r = await home.queryFilter(home.filters.Dispatch(), from, to); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + return r; + }, RETRIES, (e) => { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) + this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) if (error) { this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -663,8 +712,14 @@ export class Indexer { // to // ) const [events, error] = await retry(async () => { - return await home.queryFilter(home.filters.Update(), from, to); - }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + const start = new Date().valueOf(); + const r = await home.queryFilter(home.filters.Update(), from, to); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + return r; + }, RETRIES, (e) => { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) + this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) if (error) { this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -709,12 +764,18 @@ export class Indexer { const replica = this.replicaForDomain(domain); { const [events, error] = await retry(async () => { - return await replica.queryFilter( - replica.filters.Update(), - from, - to - ); - }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + const start = new Date().valueOf(); + const r = await replica.queryFilter( + replica.filters.Update(), + from, + to + ); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + return r; + }, RETRIES, (e) => { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) + this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) if (error) { this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index d10571f6d..c70a997d2 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -10,6 +10,7 @@ export enum RpcRequestIdentificator { GetTx = 'get_transaction', GetTxReceipt = 'get_transaction_receipt', GetBlockNumber = 'get_block_number', + GetLogs = 'get_logs', }; export enum DbRequestType { @@ -65,7 +66,7 @@ export class MetricsCollector { } } -const prefix = `fancy_monitor`; +const prefix = `nomad_indexer`; export class IndexerCollector extends MetricsCollector { private numMessages: Gauge; @@ -78,6 +79,9 @@ export class IndexerCollector extends MetricsCollector { private dbRequests: Counter; private rpcRequests: Counter; + private rpcLatency: Histogram; + private rpcErrors: Counter; + @@ -106,7 +110,20 @@ export class IndexerCollector extends MetricsCollector { labelNames: ["identificator", "network", "environment"], }); + this.rpcLatency = new Histogram({ + name: prefix + "_rpc_latency", + help: "Histogram that tracks latency of how long does it take to make request", + labelNames: ["identificator", "network", "environment"], + buckets, + }); + + this.rpcErrors = new Counter({ + name: prefix + "_rpc_errors", + help: "Counter that tracks error codes from RPC endpoint", + labelNames: ["code", "identificator", "network", "environment"], + }); + // Time Histograms @@ -175,4 +192,12 @@ export class IndexerCollector extends MetricsCollector { incRpcRequests(identificator: RpcRequestIdentificator, network: string, req?: number) { this.rpcRequests.labels(identificator, network, this.environment).inc(req) } + + observeRpcLatency(identificator: RpcRequestIdentificator, network: string, ms: number) { + this.rpcLatency.labels(identificator, network, this.environment).observe(ms) + } + + incRpcErrors(identificator: RpcRequestIdentificator, network: string, code: string) { + this.rpcErrors.labels(code, identificator, network, this.environment).inc() + } } diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts index dba92e95e..88d45f434 100644 --- a/tools/indexer/main.ts +++ b/tools/indexer/main.ts @@ -13,7 +13,7 @@ const program = process.env.PROGRAM! as Program; (async () => { const logger = createLogger("indexer", environment); const m = new IndexerCollector(environment, logger); - m.startServer(3000); + const db = new DB(m); await db.connect(); @@ -22,6 +22,7 @@ const program = process.env.PROGRAM! as Program; if (program === "api") { await api.run(db, logger); } else if (program === "core") { + m.startServer(3000); await core.run(db, environment, logger, m); } else { logger.warn(`Started both indexer and api on the same process.`); From 4477c843672efeb28949ce5701bb6116e2b18545 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 16 Feb 2022 16:24:33 +0100 Subject: [PATCH 58/63] feature: logs improved, debug api --- tools/indexer/core/consumer.ts | 128 ++++++++++++++++++++-------- tools/indexer/core/db.ts | 20 +++-- tools/indexer/core/debugApi.ts | 75 +++++++++++++++++ tools/indexer/core/index.ts | 3 + tools/indexer/core/indexer.ts | 138 +++++++++---------------------- tools/indexer/docker-compose.yml | 1 + tools/indexer/main.ts | 2 +- tools/indexer/package-lock.json | 51 ++++++++---- tools/indexer/package.json | 3 + 9 files changed, 267 insertions(+), 154 deletions(-) create mode 100644 tools/indexer/core/debugApi.ts diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index 341d8b6bc..e6c3275ec 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -283,6 +283,7 @@ export class NomadMessage { timings: Timings; gasUsed: GasUsed; + logger: Logger; constructor( origin: number, @@ -295,6 +296,7 @@ export class NomadMessage { body: string, dispatchedAt: number, dispatchBlock: number, + logger: Logger, gasUsed?: GasUsed, ) { this.origin = origin; @@ -316,6 +318,7 @@ export class NomadMessage { this.timings = new Timings(dispatchedAt); this.dispatchBlock = dispatchBlock; this.gasUsed = gasUsed || new GasUsed(); + this.logger = logger.child({messageHash}); } // PADDED! @@ -350,10 +353,56 @@ export class NomadMessage { return this.transferMessage ? this.transferMessage?.action.detailsHash : undefined } - + update(ts: number, gasUsed: BigNumber) { + if (this.state < MsgState.Updated) { + this.logger.debug(`Updated message from state ${this.state} to ${MsgState.Updated} (Updated)`); + this.state = MsgState.Updated; + this.timings.updated(ts); + this.gasUsed.update = gasUsed; + return true; + } + this.logger.debug(`The message is in the higher state for being Updated. Want < ${MsgState.Updated}, is ${this.state}`); + return false; + + } + + relay(ts: number, gasUsed: BigNumber) { + if (this.state < MsgState.Relayed) { + this.logger.debug(`Updated message from state ${this.state} to ${MsgState.Relayed} (Relayed)`); + this.state = MsgState.Relayed; + this.timings.relayed(ts); + this.gasUsed.relay = gasUsed; + return true; + } + this.logger.debug(`The message is in the higher state for being Relayed. Want < ${MsgState.Relayed}, is ${this.state}`); + return false; + } + + receive(ts: number, gasUsed: BigNumber) { + if (this.state < MsgState.Received) { + this.logger.debug(`Updated message from state ${this.state} to ${MsgState.Received} (Received)`); + this.state = MsgState.Received; + this.timings.received(ts); + this.gasUsed.receive = gasUsed; + return true; + } + this.logger.debug(`The message is in the higher state for being Received. Want < ${MsgState.Received}, is ${this.state}`); + return false; + } + process(ts: number, gasUsed: BigNumber) { + if (this.state < MsgState.Processed) { + this.logger.debug(`Updated message from state ${this.state} to ${MsgState.Processed} (Processed)`); + this.state = MsgState.Processed; + this.timings.processed(ts); + this.gasUsed.process = gasUsed; + return true; + } + this.logger.debug(`The message is in the higher state for being Proce. Want < ${MsgState.Processed}, is ${this.state}`); + return false; + } - static deserialize(s: MinimumSerializedNomadMessage) { + static deserialize(s: MinimumSerializedNomadMessage, logger: Logger) { const m = new NomadMessage( s.origin, s.destination, @@ -363,7 +412,8 @@ export class NomadMessage { BigNumber.from(s.leafIndex), s.body, s.dispatchedAt*1000, - s.dispatchBlock + s.dispatchBlock, + logger.child({messageSource: 'deserialize'}) ); m.timings.updated(s.updatedAt*1000); m.timings.relayed(s.relayedAt*1000); @@ -521,8 +571,10 @@ class SenderLostAndFound { if (m.hasMessage !== MessageType.TransferMessage) return false; if (this.findMatchingBRSendUpdateAndRemove(e, m)) { + m.logger.info(`SenderLostAndFound found existing Sent event`) return true; } else { + m.logger.info(`SenderLostAndFound haven't found existing Sent event, pushing to dispatched`) this.dispatchEventsWithMessages.push([e, m]); return false; } @@ -551,7 +603,7 @@ export class Processor extends Consumer { this.senderRegistry = new SenderLostAndFound(this); this.db = db; - this.logger = logger; + this.logger = logger.child({span: 'consumer'}); } async consume(...events: NomadEvent[]): Promise { @@ -619,91 +671,99 @@ export class Processor extends Consumer { e.eventData.leafIndex!, e.eventData.message!, e.ts, - e.block + e.block, + this.logger.child({messageSource: 'consumer'}), ); + + let logger = m.logger.child({eventName: 'Dispatched'}); + m.gasUsed.dispatch = e.gasUsed; - this.senderRegistry.dispatch(e, m); + this.senderRegistry.dispatch(e, m, ); this.add(m); this.addToSyncQueue(m.messageHash); const gas = e.gasUsed.toNumber(); // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); this.emit(`dispatched`, m.origin, m.destination, gas) + logger.debug(`Created message`); if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } homeUpdate(e: NomadEvent) { + let logger = this.logger.child({stage: MsgState.Updated}); const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); - if (ms.length) + if (ms.length) { ms.forEach((m) => { - if (m.state < MsgState.Updated) { - m.state = MsgState.Updated; - m.timings.updated(e.ts); - m.gasUsed.update = e.gasUsed; + if (m.update(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); - const gas = e.gasUsed.toNumber(); - // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); - this.emit(`updated`, m.origin, m.destination, m.timings.toUpdate(), gas); + + this.emit(`updated`, m.origin, m.destination, m.timings.toUpdate(), e.gasUsed.toNumber()); } }); + } else { + logger.warn({origin: e.replicaOrigin, root: e.eventData.oldRoot!}, `Haven't found a message for Update event`) + } } replicaUpdate(e: NomadEvent) { + let logger = this.logger.child({eventName: 'Relayed'}); const ms = this.getMsgsByOriginAndRoot( e.replicaOrigin, e.eventData.oldRoot! ); - if (ms.length) + + if (ms.length) { ms.forEach((m) => { - if (m.state < MsgState.Relayed) { - m.state = MsgState.Relayed; - m.timings.relayed(e.ts); - m.gasUsed.relay = e.gasUsed; + if (m.relay(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); - const gas = e.gasUsed.toNumber(); - // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); - this.emit(`relayed`, m.origin, m.destination, m.timings.toRelay(), gas); + this.emit(`relayed`, m.origin, m.destination, m.timings.toRelay(), e.gasUsed.toNumber()); } }); + } else { + logger.warn({origin: e.replicaOrigin, root: e.eventData.oldRoot!}, `Haven't found a message for ReplicaUpdate event`) + } } process(e: NomadEvent) { + let logger = this.logger.child({eventName: 'Processed'}); const m = this.getMsg(e.eventData.messageHash!); if (m) { - if (m.state < MsgState.Processed) { - m.state = MsgState.Processed; - m.timings.processed(e.ts); - m.gasUsed.process = e.gasUsed; + if (m.process(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); - const gas = e.gasUsed.toNumber(); - // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); - this.emit(`processed`, m.origin, m.destination, m.timings.toProcess(), gas) + this.emit(`processed`, m.origin, m.destination, m.timings.toProcess(), e.gasUsed.toNumber()) } + } else { + logger.warn({messageHash: e.eventData.messageHash!}, `Haven't found a message for Processed event`) } } bridgeRouterSend(e: NomadEvent) { + let logger = this.logger.child({eventName: 'BridgeSent',}); const hash = this.senderRegistry.bridgeRouterSend(e); if (hash) { + logger.child({messageHash: hash}).debug(`Found dispatched message`); this.addToSyncQueue(hash); + } else { + let [origin, nonce] = e.originAndNonce(); + logger.warn({origin, nonce}, `Haven't found a message for BridgeReceived event`); } } bridgeRouterReceive(e: NomadEvent) { const m = this.getMsgsByOriginAndNonce(...e.originAndNonce()); + let logger = this.logger.child({eventName: 'BridgeReceived'}); if (m) { - if (m.state < MsgState.Received) { - m.state = MsgState.Received; - m.timings.received(e.ts); - m.gasUsed.receive = e.gasUsed; + if (m.receive(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); const gas = e.gasUsed.toNumber(); - // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); this.emit(`received`, m.origin, m.destination, m.timings.toReceive(), gas); } + } else { + let [origin, nonce] = e.originAndNonce(); + logger.warn({origin, nonce}, `Haven't found a message for BridgeReceived event`) } } diff --git a/tools/indexer/core/db.ts b/tools/indexer/core/db.ts index f5031bf96..32c5aa065 100644 --- a/tools/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -1,8 +1,8 @@ import { NomadMessage } from "./consumer"; -import { messages, Prisma, PrismaClient } from '@prisma/client' -import { BigNumber } from "ethers"; +import { Prisma, PrismaClient } from '@prisma/client' import { DbRequestType, IndexerCollector } from "./metrics"; +import Logger from "bunyan"; // function fromDb(m: messages): NomadMessage { // return @@ -53,11 +53,13 @@ export class DB { client: PrismaClient; syncedOnce: boolean; metrics: IndexerCollector; + logger: Logger; - constructor(metrics: IndexerCollector) { + constructor(metrics: IndexerCollector, logger: Logger) { this.syncedOnce = false; this.client = new PrismaClient(); this.metrics = metrics; + this.logger = logger.child({span: 'DB'}); } async connect() { @@ -81,7 +83,7 @@ export class DB { } }); - return messages.map(NomadMessage.deserialize) + return messages.map(m => NomadMessage.deserialize(m, this.logger)) } async getMessageByHash(messageHash: string): Promise { @@ -92,7 +94,7 @@ export class DB { } }); - return message ? NomadMessage.deserialize(message) : undefined + return message ? NomadMessage.deserialize(message, this.logger) : undefined } async getMessages(req: MsgRequest): Promise { @@ -112,7 +114,7 @@ export class DB { skip }); - return messages.map(NomadMessage.deserialize) + return messages.map(m => NomadMessage.deserialize(m, this.logger)) } async insertMessage(messages: NomadMessage[]) { @@ -120,7 +122,10 @@ export class DB { this.metrics.incDbRequests(DbRequestType.Insert); await this.client.messages.createMany({ - data: messages.map(message => message.serialize()), + data: messages.map(message => { + message.logger.debug(`Serializing message for insert`); + return message.serialize() + }), skipDuplicates: true, }) @@ -132,6 +137,7 @@ export class DB { await Promise.all(messages.map(m => { this.metrics.incDbRequests(DbRequestType.Update); + m.logger.debug(`Serializing message for update`); this.client.messages.update({ where: { messageHash: m.messageHash diff --git a/tools/indexer/core/debugApi.ts b/tools/indexer/core/debugApi.ts new file mode 100644 index 000000000..335c58e46 --- /dev/null +++ b/tools/indexer/core/debugApi.ts @@ -0,0 +1,75 @@ +import express from "express"; +import { DB, MsgRequest } from "../core/db"; +import * as dotenv from "dotenv"; +import Logger from "bunyan"; +import { Orchestrator } from "./orchestrator"; +import { Processor } from "./consumer"; +dotenv.config({}); + +function fail(res: any, code: number, reason: string) { + return res.status(code).json({ error: reason }); +} + +const PORT = process.env.DEBUG_PORT || '1337'; + +export async function run(o: Orchestrator, logger: Logger) { + const app = express(); + + const log = ( + req: express.Request, + res: express.Response, + next: express.NextFunction + ) => { + logger.info(`request to ${req.url}`); + next(); + }; + + app.get("/healthcheck", log, (req, res) => { + res.send("OK!"); + }); + + app.get("/hash/:hash", log, async (req, res) => { + const p = (o.consumer as Processor); + const message = p.getMsg(req.params.hash); + if (message) { + return res.json(message.serialize()); + } else { + return res.status(404).json({}); + } + }); + + app.get("/tx/:tx", log, async (req, res) => { + const p = (o.consumer as Processor); + const message = Array.from(p.messages).find(m => m.tx && m.tx! === req.params.tx); + if (message) { + return res.json(message.serialize()); + } else { + return res.status(404).json({}); + } + }); + + app.get("/msg/:origin/:state", log, async (req, res) => { + + const {origin: originStr, state: stateStr} = req.params; + let origin: number, state: number; + + try { + origin = parseInt(originStr); + state = parseInt(stateStr); + } catch(e) { + return res.status(407).json({error: `One of the params (origin or stage) is invalid`}); + } + + const p = (o.consumer as Processor); + const messages = Array.from(p.messages).filter(m => m.origin === origin && m.state === state); + if (messages.length) { + return res.json(messages.map(m => m.serialize())); + } else { + return res.status(404).json({}); + } + }); + + app.listen(PORT, () => { + logger.info(`Server is running at https://localhost:${PORT}`); + }); +} diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index 9c61c9868..4a1ba85b1 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -5,6 +5,7 @@ import * as dotenv from "dotenv"; import { IndexerCollector } from "./metrics"; import { DB } from "./db"; import Logger from "bunyan"; +import {run as runDebugApi} from "./debugApi"; dotenv.config({}); export async function run(db: DB, environment: string, logger: Logger, metrics: IndexerCollector) { @@ -35,6 +36,8 @@ export async function run(db: DB, environment: string, logger: Logger, metrics: const c = new Processor(db, logger); const o = new Orchestrator(ctx, c, ctx.domainNumbers[0], metrics, logger, db); + + if (!!process.env.DEBUG_API) runDebugApi(o, logger.child({span: 'debugApi'})); await o.init(); await o.startConsuming(); diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index a1847da93..b50e00d06 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -8,6 +8,7 @@ import { KVCache, replacer, retry, reviver } from "./utils"; import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; import { RpcRequestIdentificator } from "./metrics"; +import Logger from "bunyan"; type ShortTx = { gasPrice?: ethers.BigNumber, @@ -86,6 +87,7 @@ export class Indexer { txReceiptCache: KVCache; limit: pLimit.Limit; lastBlock: number; + logger: Logger; eventCallback: undefined | ((event: NomadEvent) => void); @@ -103,6 +105,7 @@ export class Indexer { // 20 concurrent requests per indexer this.limit = pLimit(100); this.lastBlock = 0; + this.logger = orchestrator.logger.child({span: 'indexer', network: this.network, domain: this.domain}); } get provider(): ethers.providers.Provider { @@ -140,14 +143,14 @@ export class Indexer { (error: any) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockWithTxs, this.network, error.code); - this.orchestrator.logger.warn( - `Retrying after RPC Error... Block: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` + this.logger.warn( + `Retrying after RPC Error... Block: ${blockNumber}, Error: ${error.code}` ) } ); if (!block) { throw new Error( - `An RPC foo error occured, retried exhausted. Block: ${blockNumber} Domain: ${this.domain}, Error: ${error}` + `An RPC foo error occured, retried exhausted. Block: ${blockNumber} Error: ${error}` ); } const time = block.timestamp * 1000; @@ -168,7 +171,6 @@ export class Indexer { async getBlockTimestamp(blockNumber: number): Promise { const possible = await this.blockTimestampCache.get(String(blockNumber)); if (possible) { - // this.orchestrator.logger.debug(`Gound transaction receipt in DB: ${hash}`); return parseInt(possible) } @@ -186,14 +188,14 @@ export class Indexer { (error: any) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlock, this.network, error.code); - this.orchestrator.logger.warn( - `Retrying after RPC Error... Block number: ${blockNumber}, Domain: ${this.domain}, Error: ${error.code}` + this.logger.warn( + `Retrying after RPC Error... Block number: ${blockNumber}, Error: ${error.code}` ) } ); if (!block) { throw new Error( - `An RPC foo error occured, retried exhausted. Block number: ${blockNumber} Domain: ${this.domain}, Error: ${error}` + `An RPC foo error occured, retried exhausted. Block number: ${blockNumber} Error: ${error}` ); } @@ -204,8 +206,6 @@ export class Indexer { String(timestamp) ); - // this.orchestrator.logger.debug(`Finished transaction receipt fetch: ${hash}`); - return timestamp } @@ -214,7 +214,6 @@ export class Indexer { async getTransaction(hash: string, forceTimestamp=false): Promise { const possible = await this.txCache.get(hash); if (possible) { - // this.orchestrator.logger.debug(`Gound transaction in DB: ${hash}`); return txDecode(possible) } @@ -232,21 +231,20 @@ export class Indexer { (error: any) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetTx, this.network, error.code); - this.orchestrator.logger.warn( - `Retrying after RPC Error... TX hash: ${hash}, Domain: ${this.domain}, Error: ${error.code}` + this.logger.warn( + `Retrying after RPC Error... TX hash: ${hash}, Error: ${error.code}` ) } ); if (!tx) { throw new Error( - `An RPC foo error occured, retried exhausted. TX hash: ${hash} Domain: ${this.domain}, Error: ${error}` + `An RPC foo error occured, retried exhausted. TX hash: ${hash} Error: ${error}` ); } let timestamp: number; if (forceTimestamp) { - this.orchestrator.logger.debug(`Using real time timestamp for: ${hash}, ${forceTimestamp} || ${tx.timestamp}`); timestamp = new Date().valueOf() } else if (!tx.timestamp) { @@ -274,15 +272,12 @@ export class Indexer { txEncode(shortTx) ); - // this.orchestrator.logger.debug(`Finished transaction fetch: ${hash}`); - return shortTx; } async getTransactionReceipt(hash: string): Promise { const possible = await this.txReceiptCache.get(hash); if (possible) { - // this.orchestrator.logger.debug(`Gound transaction receipt in DB: ${hash}`); return txReceiptDecode(possible) } @@ -300,14 +295,14 @@ export class Indexer { (error: any) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetTxReceipt, this.network, error.code) - this.orchestrator.logger.warn( - `Retrying after RPC Error... TX hash: ${hash}, Domain: ${this.domain}, Error: ${error.code}` + this.logger.warn( + `Retrying after RPC Error... , Error: ${error.code}` ) } ); if (!receipt) { throw new Error( - `An RPC foo error occured, retried exhausted. TX hash: ${hash} Domain: ${this.domain}, Error: ${error}` + `An RPC foo error occured, retried exhausted. TX hash: ${hash} Error: ${error}` ); } @@ -316,8 +311,6 @@ export class Indexer { txReceiptEncode(receipt) ); - // this.orchestrator.logger.debug(`Finished transaction receipt fetch: ${hash}`); - return receipt } @@ -326,8 +319,6 @@ export class Indexer { const {gasUsed, from} = await this.getTransactionReceipt(hash); - // this.orchestrator.logger.debug(`Done with TX: ${hash}`); - return {timestamp, gasUsed, from} } @@ -358,7 +349,7 @@ export class Indexer { async updateAll(replicas: number[]) { let from = Math.max( - this.lastBlock, + this.lastBlock + 1, this.persistance.height, this.sdk.getDomain(this.domain)?.paginate?.from || 0 ); @@ -374,7 +365,7 @@ export class Indexer { (error: any) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, error.code) - this.orchestrator.logger.warn( + this.logger.warn( `Retrying after RPC Error on .getBlockNumber() method... Error: ${error}` ) } @@ -385,15 +376,15 @@ export class Indexer { ); } - if (from === to) { - this.orchestrator.logger.info( - `Skipped fetching events for domain ${this.domain} from: ${from}, to: ${to}, because from and to are the same` + if (from > to) { + this.logger.info( + `Skipped fetching events, because from is higher than to. From: ${from}, to: ${to}` ); return []; } - this.orchestrator.logger.info( - `Fetching events for domain ${this.domain} from: ${from}, to: ${to}` + this.logger.info( + `Fetching events for from: ${from}, to: ${to}` ); const fetchEvents = async (from: number, to: number): Promise => { @@ -416,8 +407,8 @@ export class Indexer { while (true) { const done = Math.floor((batchTo-from+1)/(to-from+1) * 100); - this.orchestrator.logger.debug( - `Fetching batch of events for domain ${this.domain} from: ${batchFrom}, to: ${batchTo}, [${done}%]` + this.logger.debug( + `Fetching batch of events for from: ${batchFrom}, to: ${batchTo}, [${done}%]` ); const events = await fetchEvents(batchFrom, batchTo); if (!events) throw new Error(`KEk`); @@ -425,13 +416,13 @@ export class Indexer { this.persistance.store(...events); try { this.dummyTestEventsIntegrity(batchTo); - this.orchestrator.logger.debug(`Integrity test PASSED for ${this.domain} between ${batchFrom} and ${batchTo}`); + this.logger.debug(`Integrity test PASSED between ${batchFrom} and ${batchTo}`); } catch(e) { const pastFrom = batchFrom; const pastTo = batchTo; batchFrom = batchFrom - batchSize/2; batchTo = batchFrom + batchSize; - this.orchestrator.logger.warn(`Integrity test not passed for ${this.domain} between ${pastFrom} and ${pastTo}, recollecting between ${batchFrom} and ${batchTo}: ${e}`); + this.logger.warn(`Integrity test not passed between ${pastFrom} and ${pastTo}, recollecting between ${batchFrom} and ${batchTo}: ${e}`); continue; } allEvents.push(...events.filter(newEvent => allEvents.every(oldEvent => newEvent.uniqueHash() !== oldEvent.uniqueHash()))); @@ -445,7 +436,7 @@ export class Indexer { allEvents.sort((a, b) => a.ts - b.ts); this.dummyTestEventsIntegrity(); - this.orchestrator.logger.info(`Fetched all for domain ${this.domain}`); + this.logger.info(`Fetched all`); this.lastBlock = to; return allEvents; @@ -455,7 +446,7 @@ export class Indexer { let allEvents = this.persistance.allEvents(); if (blockTo) allEvents = allEvents.filter(e => e.block <= blockTo); if (allEvents.length === 0) { - this.orchestrator.logger.debug(`No events to test integrity!!!`); + this.logger.debug(`No events to test integrity!!!`); return ; } @@ -528,14 +519,6 @@ export class Indexer { const br = this.bridgeRouter(); const allEvents = []; { - // const events = await getEvents( - // this.sdk, - // this.domain, - // br, - // br.filters.Send(), - // from, - // to - // ) const [events, error] = await retry(async () => { this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); const start = new Date().valueOf(); @@ -544,10 +527,10 @@ export class Indexer { return r; }, RETRIES, (e) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) - this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) }) if (error) { - this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error } if (events === undefined) { @@ -583,15 +566,6 @@ export class Indexer { } { - // const events = await getEvents( - // this.sdk, - // this.domain, - // br, - // br.filters.Receive(), - // from, - // to - // ) - const [events, error] = await retry(async () => { this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); const start = new Date().valueOf(); @@ -600,9 +574,9 @@ export class Indexer { return r; }, RETRIES, (e) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code); - this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) if (error) { - this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error } if (events === undefined) { @@ -646,14 +620,6 @@ export class Indexer { const home = this.home(); { - // const events = await getEvents( - // this.sdk, - // this.domain, - // home, - // home.filters.Dispatch(), - // from, - // to - // ) const [events, error] = await retry(async () => { this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); const start = new Date().valueOf(); @@ -662,9 +628,9 @@ export class Indexer { return r; }, RETRIES, (e) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) - this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) if (error) { - this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error } if (events === undefined) { @@ -703,14 +669,6 @@ export class Indexer { } { - // const events = await getEvents( - // this.sdk, - // this.domain, - // home, - // home.filters.Update(), - // from, - // to - // ) const [events, error] = await retry(async () => { this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); const start = new Date().valueOf(); @@ -719,9 +677,9 @@ export class Indexer { return r; }, RETRIES, (e) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) - this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) if (error) { - this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error } if (events === undefined) { @@ -775,22 +733,14 @@ export class Indexer { return r; }, RETRIES, (e) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) - this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) if (error) { - this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error } if (events === undefined) { throw new Error(`There is no error, but events for some reason are still undefined`); } - // const events = await getEvents( - // this.sdk, - // domain, - // replica, - // replica.filters.Update(), - // from, - // to - // ); const parsedEvents = await Promise.all( events.map( @@ -820,23 +770,15 @@ export class Indexer { } { - // const events = await getEvents( - // this.sdk, - // domain, - // replica, - // replica.filters.Process(), - // from, - // to - // ) const [events, error] = await retry(async () => { return await replica.queryFilter( replica.filters.Process(), from, to ); - }, RETRIES, (e) => {this.orchestrator.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to} for ${this.domain} domain, error: ${e.message}`)}) + }, RETRIES, (e) => {this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) if (error) { - this.orchestrator.logger.error(`Couldn't recover the error after ${RETRIES} retries`) + this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error } if (events === undefined) { diff --git a/tools/indexer/docker-compose.yml b/tools/indexer/docker-compose.yml index 772a2c056..eea8ee00c 100644 --- a/tools/indexer/docker-compose.yml +++ b/tools/indexer/docker-compose.yml @@ -10,6 +10,7 @@ services: - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres?connect_timeout=300&schema=public ports: - "9090:3000" + - "9091:1337" depends_on: - "postgres" links: diff --git a/tools/indexer/main.ts b/tools/indexer/main.ts index 88d45f434..cc21ca237 100644 --- a/tools/indexer/main.ts +++ b/tools/indexer/main.ts @@ -15,7 +15,7 @@ const program = process.env.PROGRAM! as Program; const m = new IndexerCollector(environment, logger); - const db = new DB(m); + const db = new DB(m, logger); await db.connect(); diff --git a/tools/indexer/package-lock.json b/tools/indexer/package-lock.json index d38282f3c..e2329400b 100644 --- a/tools/indexer/package-lock.json +++ b/tools/indexer/package-lock.json @@ -24,6 +24,9 @@ "prom-client": "^14.0.0", "ts-node": "^10.4.0", "typescript": "^4.4.3" + }, + "devDependencies": { + "axios": "^0.26.0" } }, "../../typescript/nomad-sdk": { @@ -817,6 +820,14 @@ "@ethersproject/properties": "^5.0.0" } }, + "node_modules/@gnosis.pm/safe-ethers-adapters/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/@nomad-xyz/contract-interfaces": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@nomad-xyz/contract-interfaces/-/contract-interfaces-1.2.0.tgz", @@ -1137,11 +1148,12 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", + "dev": true, "dependencies": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.8" } }, "node_modules/balanced-match": { @@ -2226,9 +2238,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", "funding": [ { "type": "individual", @@ -5381,6 +5393,16 @@ "@gnosis.pm/safe-core-sdk-types": "^0.1.1", "@gnosis.pm/safe-deployments": "^1.0.0", "axios": "^0.21.1" + }, + "dependencies": { + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + } } }, "@nomad-xyz/contract-interfaces": { @@ -5656,11 +5678,12 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", + "dev": true, "requires": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.8" } }, "balanced-match": { @@ -6596,9 +6619,9 @@ } }, "follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==" }, "foreach": { "version": "2.0.5", diff --git a/tools/indexer/package.json b/tools/indexer/package.json index 2a08f11b0..d3279dfd9 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -30,5 +30,8 @@ "prom-client": "^14.0.0", "ts-node": "^10.4.0", "typescript": "^4.4.3" + }, + "devDependencies": { + "axios": "^0.26.0" } } From 2fbca409cf141d6af743f91835bb32b3646cb6e6 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 17 Feb 2022 19:35:29 +0100 Subject: [PATCH 59/63] fix: db load --- tools/indexer/core/db.ts | 14 ++++++++------ tools/indexer/core/index.ts | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tools/indexer/core/db.ts b/tools/indexer/core/db.ts index 32c5aa065..2b1a217c3 100644 --- a/tools/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -123,7 +123,7 @@ export class DB { this.metrics.incDbRequests(DbRequestType.Insert); await this.client.messages.createMany({ data: messages.map(message => { - message.logger.debug(`Serializing message for insert`); + message.logger.debug(`Message created in DB`); return message.serialize() }), skipDuplicates: true, @@ -135,15 +135,17 @@ export class DB { async updateMessage(messages: NomadMessage[]) { if (!messages.length) return; - await Promise.all(messages.map(m => { + await Promise.all(messages.map(async (m) => { this.metrics.incDbRequests(DbRequestType.Update); - m.logger.debug(`Serializing message for update`); - this.client.messages.update({ + + const serialized = m.serialize(); + await this.client.messages.update({ where: { messageHash: m.messageHash }, - data: m.serialize(), - }) + data: serialized, + }); + m.logger.debug(`Message updated in DB. updated: ${serialized.updatedAt}, relayed: ${serialized.relayedAt}, received: ${serialized.receivedAt}, processed: ${serialized.processedAt}`); })); return diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index 4a1ba85b1..e61249e59 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -37,7 +37,7 @@ export async function run(db: DB, environment: string, logger: Logger, metrics: const o = new Orchestrator(ctx, c, ctx.domainNumbers[0], metrics, logger, db); - if (!!process.env.DEBUG_API) runDebugApi(o, logger.child({span: 'debugApi'})); + if (!!process.env.DEBUG_PORT) runDebugApi(o, logger.child({span: 'debugApi'})); await o.init(); await o.startConsuming(); From 58bcd53f82237790b1ccf89d33cdce65b253e8aa Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Thu, 17 Feb 2022 21:52:08 +0100 Subject: [PATCH 60/63] fix: db load --- tools/indexer/core/db.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tools/indexer/core/db.ts b/tools/indexer/core/db.ts index 2b1a217c3..3f5e3cfaf 100644 --- a/tools/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -3,6 +3,7 @@ import { NomadMessage } from "./consumer"; import { Prisma, PrismaClient } from '@prisma/client' import { DbRequestType, IndexerCollector } from "./metrics"; import Logger from "bunyan"; +import pLimit from 'p-limit'; // function fromDb(m: messages): NomadMessage { // return @@ -135,17 +136,21 @@ export class DB { async updateMessage(messages: NomadMessage[]) { if (!messages.length) return; + const limit = pLimit(10); + await Promise.all(messages.map(async (m) => { - this.metrics.incDbRequests(DbRequestType.Update); - - const serialized = m.serialize(); - await this.client.messages.update({ - where: { - messageHash: m.messageHash - }, - data: serialized, - }); - m.logger.debug(`Message updated in DB. updated: ${serialized.updatedAt}, relayed: ${serialized.relayedAt}, received: ${serialized.receivedAt}, processed: ${serialized.processedAt}`); + return await limit(async () => { + this.metrics.incDbRequests(DbRequestType.Update); + + const serialized = m.serialize(); + await this.client.messages.update({ + where: { + messageHash: m.messageHash + }, + data: serialized, + }); + m.logger.debug(`Message updated in DB. updated: ${serialized.updatedAt}, relayed: ${serialized.relayedAt}, received: ${serialized.receivedAt}, processed: ${serialized.processedAt}`); + }) })); return From 1fc19190e4049ab386046c9b39a090c87a8268f9 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Wed, 2 Mar 2022 19:16:08 +0100 Subject: [PATCH 61/63] fix: call stack size, naming --- tools/indexer/api/index.ts | 23 ++++++++ tools/indexer/core/{debugApi.ts => api.ts} | 29 ++++++++- tools/indexer/core/consumer.ts | 26 ++++---- tools/indexer/core/db.ts | 9 +++ tools/indexer/core/index.ts | 4 +- tools/indexer/core/indexer.ts | 69 +++++++++++++++------- tools/indexer/core/metrics.ts | 4 +- tools/indexer/core/orchestrator.ts | 42 ++++++------- tools/indexer/core/utils.ts | 19 ++++++ tools/indexer/package-lock.json | 38 ++++++++++++ tools/indexer/package.json | 1 + 11 files changed, 206 insertions(+), 58 deletions(-) rename tools/indexer/core/{debugApi.ts => api.ts} (71%) diff --git a/tools/indexer/api/index.ts b/tools/indexer/api/index.ts index e74a95a3a..a1b9f3385 100644 --- a/tools/indexer/api/index.ts +++ b/tools/indexer/api/index.ts @@ -2,6 +2,9 @@ import express from "express"; import { DB, MsgRequest } from "../core/db"; import * as dotenv from "dotenv"; import Logger from "bunyan"; +import promBundle from 'express-prom-bundle'; +import { prefix } from "../core/metrics"; + dotenv.config({}); function fail(res: any, code: number, reason: string) { @@ -22,6 +25,26 @@ export async function run(db: DB, logger: Logger) { next(); }; + const metricsMiddleware = promBundle({ + httpDurationMetricName: prefix + '_api', + buckets: [0.1, 0.3, 0.6, 1, 1.5, 2.5, 5], + includeMethod: true, + includePath: true, + metricsPath: '/metrics', + }); + metricsMiddleware + + app.use(metricsMiddleware) + + // app.use(promMid({ + // metricsPath: '/metrics', + // collectDefaultMetrics: true, + // requestDurationBuckets: [0.1, 0.5, 1, 1.5], + // requestLengthBuckets: [512, 1024, 5120, 10240, 51200, 102400], + // responseLengthBuckets: [512, 1024, 5120, 10240, 51200, 102400], + // prefix: prefix + '_api', + // })); + app.get("/healthcheck", log, (req, res) => { res.send("OK!"); }); diff --git a/tools/indexer/core/debugApi.ts b/tools/indexer/core/api.ts similarity index 71% rename from tools/indexer/core/debugApi.ts rename to tools/indexer/core/api.ts index 335c58e46..79544a058 100644 --- a/tools/indexer/core/debugApi.ts +++ b/tools/indexer/core/api.ts @@ -1,9 +1,10 @@ import express from "express"; -import { DB, MsgRequest } from "../core/db"; +import { DB, MsgRequest } from "./db"; import * as dotenv from "dotenv"; import Logger from "bunyan"; import { Orchestrator } from "./orchestrator"; import { Processor } from "./consumer"; +import { replacer } from "./utils"; dotenv.config({}); function fail(res: any, code: number, reason: string) { @@ -48,6 +49,32 @@ export async function run(o: Orchestrator, logger: Logger) { } }); + app.get("/status", log, async (req, res) => { + const promises: Promise<[number, { + lastIndexed: number; + numMessages: number; + numRpcFailures: number; + }]>[] = Array.from(o.indexers.entries()).map(async ([domain, indexer]): Promise<[number, { + lastIndexed: number; + numMessages: number; + numRpcFailures: number; + }]> => { + return [domain, { + lastIndexed: indexer.lastIndexed.valueOf(), + numMessages: await o.db.getMessageCount(domain), + numRpcFailures: indexer.failureCounter.num() + }] + }); + const entries = await Promise.all( + promises + ); + + const x = new Map( + entries + ); + return res.json(JSON.stringify(x, replacer)); + }) + app.get("/msg/:origin/:state", log, async (req, res) => { const {origin: originStr, state: stateStr} = req.params; diff --git a/tools/indexer/core/consumer.ts b/tools/indexer/core/consumer.ts index e6c3275ec..6cee2e475 100644 --- a/tools/indexer/core/consumer.ts +++ b/tools/indexer/core/consumer.ts @@ -69,7 +69,7 @@ class StatisticsCollector { } export abstract class Consumer extends EventEmitter { - abstract consume(...evens: NomadEvent[]): Promise; + abstract consume(evens: NomadEvent[]): Promise; abstract stats(): Statistics; } @@ -606,7 +606,7 @@ export class Processor extends Consumer { this.logger = logger.child({span: 'consumer'}); } - async consume(...events: NomadEvent[]): Promise { + async consume(events: NomadEvent[]): Promise { for (const event of events) { if (event.eventType === EventType.HomeDispatch) { this.dispatched(event); @@ -675,7 +675,7 @@ export class Processor extends Consumer { this.logger.child({messageSource: 'consumer'}), ); - let logger = m.logger.child({eventName: 'Dispatched'}); + let logger = m.logger.child({eventName: "dispatched"}); m.gasUsed.dispatch = e.gasUsed; @@ -685,21 +685,21 @@ export class Processor extends Consumer { this.addToSyncQueue(m.messageHash); const gas = e.gasUsed.toNumber(); // this.logger.warn(`!Gas for dispatched from ${m.origin, m.destination} to ${m.origin, m.destination} (${e.tx}) = ${gas} (${e.gasUsed})`); - this.emit(`dispatched`, m.origin, m.destination, gas) + this.emit("dispatched", m.origin, m.destination, gas) logger.debug(`Created message`); if (!this.domains.includes(e.domain)) this.domains.push(e.domain); } homeUpdate(e: NomadEvent) { - let logger = this.logger.child({stage: MsgState.Updated}); + let logger = this.logger.child({eventName: "updated"}); const ms = this.getMsgsByOriginAndRoot(e.domain, e.eventData.oldRoot!); if (ms.length) { ms.forEach((m) => { if (m.update(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); - this.emit(`updated`, m.origin, m.destination, m.timings.toUpdate(), e.gasUsed.toNumber()); + this.emit("updated", m.origin, m.destination, m.timings.toUpdate(), e.gasUsed.toNumber()); } }); } else { @@ -708,7 +708,7 @@ export class Processor extends Consumer { } replicaUpdate(e: NomadEvent) { - let logger = this.logger.child({eventName: 'Relayed'}); + let logger = this.logger.child({eventName: "relayed"}); const ms = this.getMsgsByOriginAndRoot( e.replicaOrigin, e.eventData.oldRoot! @@ -718,7 +718,7 @@ export class Processor extends Consumer { ms.forEach((m) => { if (m.relay(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); - this.emit(`relayed`, m.origin, m.destination, m.timings.toRelay(), e.gasUsed.toNumber()); + this.emit("relayed", m.origin, m.destination, m.timings.toRelay(), e.gasUsed.toNumber()); } }); } else { @@ -727,12 +727,12 @@ export class Processor extends Consumer { } process(e: NomadEvent) { - let logger = this.logger.child({eventName: 'Processed'}); + let logger = this.logger.child({eventName: "processed"}); const m = this.getMsg(e.eventData.messageHash!); if (m) { if (m.process(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); - this.emit(`processed`, m.origin, m.destination, m.timings.toProcess(), e.gasUsed.toNumber()) + this.emit("processed", m.origin, m.destination, m.timings.toProcess(), e.gasUsed.toNumber()) } } else { logger.warn({messageHash: e.eventData.messageHash!}, `Haven't found a message for Processed event`) @@ -740,7 +740,7 @@ export class Processor extends Consumer { } bridgeRouterSend(e: NomadEvent) { - let logger = this.logger.child({eventName: 'BridgeSent',}); + let logger = this.logger.child({eventName: "bridgeSent",}); const hash = this.senderRegistry.bridgeRouterSend(e); if (hash) { logger.child({messageHash: hash}).debug(`Found dispatched message`); @@ -753,13 +753,13 @@ export class Processor extends Consumer { bridgeRouterReceive(e: NomadEvent) { const m = this.getMsgsByOriginAndNonce(...e.originAndNonce()); - let logger = this.logger.child({eventName: 'BridgeReceived'}); + let logger = this.logger.child({eventName: "bridgeReceived"}); if (m) { if (m.receive(e.ts, e.gasUsed)) { this.addToSyncQueue(m.messageHash); const gas = e.gasUsed.toNumber(); - this.emit(`received`, m.origin, m.destination, m.timings.toReceive(), gas); + this.emit("received", m.origin, m.destination, m.timings.toReceive(), gas); } } else { let [origin, nonce] = e.originAndNonce(); diff --git a/tools/indexer/core/db.ts b/tools/indexer/core/db.ts index 3f5e3cfaf..9534e49d0 100644 --- a/tools/indexer/core/db.ts +++ b/tools/indexer/core/db.ts @@ -118,6 +118,15 @@ export class DB { return messages.map(m => NomadMessage.deserialize(m, this.logger)) } + async getMessageCount(origin: number): Promise { + this.metrics.incDbRequests(DbRequestType.Select); + return await this.client.messages.count({ + where: { + origin + } + }); + } + async insertMessage(messages: NomadMessage[]) { if (!messages.length) return; diff --git a/tools/indexer/core/index.ts b/tools/indexer/core/index.ts index e61249e59..0cefa461d 100644 --- a/tools/indexer/core/index.ts +++ b/tools/indexer/core/index.ts @@ -5,7 +5,7 @@ import * as dotenv from "dotenv"; import { IndexerCollector } from "./metrics"; import { DB } from "./db"; import Logger from "bunyan"; -import {run as runDebugApi} from "./debugApi"; +import {run as runApi} from "./api"; dotenv.config({}); export async function run(db: DB, environment: string, logger: Logger, metrics: IndexerCollector) { @@ -37,7 +37,7 @@ export async function run(db: DB, environment: string, logger: Logger, metrics: const o = new Orchestrator(ctx, c, ctx.domainNumbers[0], metrics, logger, db); - if (!!process.env.DEBUG_PORT) runDebugApi(o, logger.child({span: 'debugApi'})); + if (!!process.env.DEBUG_PORT) runApi(o, logger.child({span: 'debugApi'})); await o.init(); await o.startConsuming(); diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index b50e00d06..037d2463b 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -4,7 +4,7 @@ import fs from "fs"; import { ContractType, EventType, NomadEvent, EventSource } from "./event"; import { Home, Replica } from "@nomad-xyz/contract-interfaces/core"; import { ethers } from "ethers"; -import { KVCache, replacer, retry, reviver } from "./utils"; +import { FailureCounter, KVCache, replacer, retry, reviver } from "./utils"; import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; import { RpcRequestIdentificator } from "./metrics"; @@ -88,6 +88,8 @@ export class Indexer { limit: pLimit.Limit; lastBlock: number; logger: Logger; + lastIndexed: Date; + failureCounter: FailureCounter; eventCallback: undefined | ((event: NomadEvent) => void); @@ -106,6 +108,8 @@ export class Indexer { this.limit = pLimit(100); this.lastBlock = 0; this.logger = orchestrator.logger.child({span: 'indexer', network: this.network, domain: this.domain}); + this.lastIndexed = new Date(0); + this.failureCounter = new FailureCounter(60); // 1 hour } get provider(): ethers.providers.Provider { @@ -145,7 +149,8 @@ export class Indexer { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockWithTxs, this.network, error.code); this.logger.warn( `Retrying after RPC Error... Block: ${blockNumber}, Error: ${error.code}` - ) + ); + this.failureCounter.add(); } ); if (!block) { @@ -191,6 +196,7 @@ export class Indexer { this.logger.warn( `Retrying after RPC Error... Block number: ${blockNumber}, Error: ${error.code}` ) + this.failureCounter.add(); } ); if (!block) { @@ -234,6 +240,7 @@ export class Indexer { this.logger.warn( `Retrying after RPC Error... TX hash: ${hash}, Error: ${error.code}` ) + this.failureCounter.add(); } ); if (!tx) { @@ -252,6 +259,7 @@ export class Indexer { if (!tx.blockNumber) throw new Error( `An RPC foo error occured. TX hash: ${hash} has no blockNumber. WTF?` ); + this.failureCounter.add(); timestamp = await this.getBlockTimestamp(tx.blockNumber!) } else { @@ -298,6 +306,7 @@ export class Indexer { this.logger.warn( `Retrying after RPC Error... , Error: ${error.code}` ) + this.failureCounter.add(); } ); if (!receipt) { @@ -368,6 +377,7 @@ export class Indexer { this.logger.warn( `Retrying after RPC Error on .getBlockNumber() method... Error: ${error}` ) + this.failureCounter.add(); } ); if (!to) { @@ -528,6 +538,7 @@ export class Indexer { }, RETRIES, (e) => { this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) + this.failureCounter.add(); }) if (error) { this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) @@ -567,14 +578,16 @@ export class Indexer { { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); const start = new Date().valueOf(); const r = await br.queryFilter(br.filters.Receive(), from, to); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code); - this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code); + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) + this.failureCounter.add(); + }) if (error) { this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -621,14 +634,16 @@ export class Indexer { const home = this.home(); { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); const start = new Date().valueOf(); const r = await home.queryFilter(home.filters.Dispatch(), from, to); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) - this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) + this.failureCounter.add(); + }) if (error) { this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -670,14 +685,16 @@ export class Indexer { { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); const start = new Date().valueOf(); const r = await home.queryFilter(home.filters.Update(), from, to); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) - this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) + this.failureCounter.add(); + }) if (error) { this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -722,18 +739,20 @@ export class Indexer { const replica = this.replicaForDomain(domain); { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); const start = new Date().valueOf(); const r = await replica.queryFilter( replica.filters.Update(), from, to ); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) - this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) + this.failureCounter.add(); + }) if (error) { this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error @@ -771,12 +790,22 @@ export class Indexer { { const [events, error] = await retry(async () => { - return await replica.queryFilter( + this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + const start = new Date().valueOf(); + const r = await replica.queryFilter( replica.filters.Process(), from, to ); - }, RETRIES, (e) => {this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`)}) + this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + + return r + + }, RETRIES, (e) => { + this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) + this.failureCounter.add(); + }) if (error) { this.logger.error(`Couldn't recover the error after ${RETRIES} retries`) throw error diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index c70a997d2..9695d2a1a 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -3,6 +3,8 @@ import Logger from "bunyan"; import { register } from "prom-client"; import express, { Response } from "express"; +export const prefix = `nomad_indexer`; + export enum RpcRequestIdentificator { GetBlock = 'get_block', @@ -51,6 +53,7 @@ export class MetricsCollector { throw Error(`Invalid PrometheusPort value: ${port}`); } const server = express(); + server.get("/metrics", async (_, res: Response) => { res.set("Content-Type", register.contentType); res.end(await register.metrics()); @@ -66,7 +69,6 @@ export class MetricsCollector { } } -const prefix = `nomad_indexer`; export class IndexerCollector extends MetricsCollector { private numMessages: Gauge; diff --git a/tools/indexer/core/orchestrator.ts b/tools/indexer/core/orchestrator.ts index aef97fb93..4f18f87ed 100644 --- a/tools/indexer/core/orchestrator.ts +++ b/tools/indexer/core/orchestrator.ts @@ -89,7 +89,7 @@ export class Orchestrator { ).flat(); events.sort((a, b) => a.ts - b.ts); this.logger.info(`Received ${events.length} events after reindexing`); - await this.consumer.consume(...events); + await this.consumer.consume(events); return events.length } @@ -112,11 +112,11 @@ export class Orchestrator { this.sdk.domainNumbers.forEach(async (domain: number) => { const network = this.domain2name(domain); const s = stats.forDomain(domain).counts; - this.metrics.setNumMessages('dispatched', network, s.dispatched); - this.metrics.setNumMessages('updated', network, s.updated); - this.metrics.setNumMessages('relayed', network, s.relayed); - this.metrics.setNumMessages('received', network, s.received); - this.metrics.setNumMessages('processed', network, s.processed); + this.metrics.setNumMessages("dispatched", network, s.dispatched); + this.metrics.setNumMessages("updated", network, s.updated); + this.metrics.setNumMessages("relayed", network, s.relayed); + this.metrics.setNumMessages("received", network, s.received); + this.metrics.setNumMessages("processed", network, s.processed); }) } @@ -135,7 +135,7 @@ export class Orchestrator { .map((indexer) => indexer.persistance.allEvents()) .flat(); events.sort((a, b) => a.ts - b.ts); - await this.consumer.consume(...events); + await this.consumer.consume(events); } async initIndexers() { @@ -155,38 +155,38 @@ export class Orchestrator { subscribeStatisticEvents() { - this.consumer.on('dispatched', (home: number, replica: number, gas: number) => { + this.consumer.on("dispatched", (home: number, replica: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeGasUsage('dispatched', homeName, replicaName, gas); + this.metrics.observeGasUsage("dispatched", homeName, replicaName, gas); }) - this.consumer.on('updated', (home: number, replica: number ,ms: number, gas: number) => { + this.consumer.on("updated", (home: number, replica: number ,ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeLatency('updated', homeName, replicaName, ms) - this.metrics.observeGasUsage('updated', homeName, replicaName, gas); + this.metrics.observeLatency("updated", homeName, replicaName, ms) + this.metrics.observeGasUsage("updated", homeName, replicaName, gas); }) - this.consumer.on('relayed', (home: number, replica: number ,ms: number, gas: number) => { + this.consumer.on("relayed", (home: number, replica: number ,ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeLatency('relayed', homeName, replicaName, ms) - this.metrics.observeGasUsage('relayed', homeName, replicaName, gas); + this.metrics.observeLatency("relayed", homeName, replicaName, ms) + this.metrics.observeGasUsage("relayed", homeName, replicaName, gas); }) - this.consumer.on('received', (home: number, replica: number, ms: number, gas: number) => { + this.consumer.on("received", (home: number, replica: number, ms: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeLatency('received', homeName, replicaName, ms) - this.metrics.observeGasUsage('received', homeName, replicaName, gas); + this.metrics.observeLatency("received", homeName, replicaName, ms) + this.metrics.observeGasUsage("received", homeName, replicaName, gas); }) - this.consumer.on('processed', (home: number, replica: number, e2e: number, gas: number) => { + this.consumer.on("processed", (home: number, replica: number, e2e: number, gas: number) => { const homeName = this.domain2name(home); const replicaName = this.domain2name(replica); - this.metrics.observeLatency('processed', homeName, replicaName, e2e) - this.metrics.observeGasUsage('processed', homeName, replicaName, gas); + this.metrics.observeLatency("processed", homeName, replicaName, e2e) + this.metrics.observeGasUsage("processed", homeName, replicaName, gas); }) } diff --git a/tools/indexer/core/utils.ts b/tools/indexer/core/utils.ts index b688438cd..52e8e8956 100644 --- a/tools/indexer/core/utils.ts +++ b/tools/indexer/core/utils.ts @@ -131,3 +131,22 @@ export class Padded { return this.s; } } + + +export class FailureCounter { + container: Date[]; + period: number; + constructor(periodMins=60) { + this.container = []; + this.period = periodMins; + } + add() { + this.container.push(new Date()); + } + num(): number { + let now = new Date(); + const cleanDates = this.container.filter(d => (now.valueOf() - d.valueOf()) <= 1000 * 60 * this.period); // millisec * 60 sec * period in mins + this.container = cleanDates; + return cleanDates.length; + } +} \ No newline at end of file diff --git a/tools/indexer/package-lock.json b/tools/indexer/package-lock.json index e2329400b..ca7a348f7 100644 --- a/tools/indexer/package-lock.json +++ b/tools/indexer/package-lock.json @@ -18,6 +18,7 @@ "dotenv": "^10.0.0", "ethers": "^5.4.6", "express": "^4.17.2", + "express-prom-bundle": "^6.4.1", "p-limit": "^3.1.0", "prettier": "^2.4.1", "prisma": "^3.9.1", @@ -2184,6 +2185,21 @@ "node": ">= 0.10.0" } }, + "node_modules/express-prom-bundle": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/express-prom-bundle/-/express-prom-bundle-6.4.1.tgz", + "integrity": "sha512-Sg0svLQe/SS5z1tHDTVfZVjNumobiDlXM0jmemt5Dm9K6BX8z9yCwEr93zbko6fNMR4zKav77iPfxUWi6gAjNA==", + "dependencies": { + "on-finished": "^2.3.0", + "url-value-parser": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "prom-client": ">=12.0.0" + } + }, "node_modules/ext": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", @@ -4336,6 +4352,14 @@ "node": ">= 4" } }, + "node_modules/url-value-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/url-value-parser/-/url-value-parser-2.1.0.tgz", + "integrity": "sha512-gIYPWXujdUdwd/9TGCHTf5Vvgw6lOxjE5Q/k+7WNByYyS0vW5WX0k+xuVlhvPq6gRNhzXVv/ezC+OfeAet5Kcw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/utf-8-validate": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.8.tgz", @@ -6569,6 +6593,15 @@ "vary": "~1.1.2" } }, + "express-prom-bundle": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/express-prom-bundle/-/express-prom-bundle-6.4.1.tgz", + "integrity": "sha512-Sg0svLQe/SS5z1tHDTVfZVjNumobiDlXM0jmemt5Dm9K6BX8z9yCwEr93zbko6fNMR4zKav77iPfxUWi6gAjNA==", + "requires": { + "on-finished": "^2.3.0", + "url-value-parser": "^2.0.0" + } + }, "ext": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", @@ -8186,6 +8219,11 @@ "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" }, + "url-value-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/url-value-parser/-/url-value-parser-2.1.0.tgz", + "integrity": "sha512-gIYPWXujdUdwd/9TGCHTf5Vvgw6lOxjE5Q/k+7WNByYyS0vW5WX0k+xuVlhvPq6gRNhzXVv/ezC+OfeAet5Kcw==" + }, "utf-8-validate": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.8.tgz", diff --git a/tools/indexer/package.json b/tools/indexer/package.json index d3279dfd9..2dbde9468 100644 --- a/tools/indexer/package.json +++ b/tools/indexer/package.json @@ -24,6 +24,7 @@ "dotenv": "^10.0.0", "ethers": "^5.4.6", "express": "^4.17.2", + "express-prom-bundle": "^6.4.1", "p-limit": "^3.1.0", "prettier": "^2.4.1", "prisma": "^3.9.1", From 15f0b0fb0b59afd99f9ded6875aab66fffda1ab6 Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Fri, 4 Mar 2022 19:52:56 +0100 Subject: [PATCH 62/63] fix: naming --- tools/indexer/core/metrics.ts | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tools/indexer/core/metrics.ts b/tools/indexer/core/metrics.ts index 9695d2a1a..c77a30719 100644 --- a/tools/indexer/core/metrics.ts +++ b/tools/indexer/core/metrics.ts @@ -6,13 +6,13 @@ import express, { Response } from "express"; export const prefix = `nomad_indexer`; -export enum RpcRequestIdentificator { - GetBlock = 'get_block', - GetBlockWithTxs = 'get_block_with_transactions', - GetTx = 'get_transaction', - GetTxReceipt = 'get_transaction_receipt', - GetBlockNumber = 'get_block_number', - GetLogs = 'get_logs', +export enum RpcRequestMethod { + GetBlock = 'eth_getBlock', + GetBlockWithTxs = 'eth_getBlockWithTransactions', + GetTx = 'eth_getTransaction', + GetTxReceipt = 'eth_getTransactionReceipt', + GetBlockNumber = 'eth_getBlockNumber', + GetLogs = 'eth_getLogs', }; export enum DbRequestType { @@ -109,20 +109,20 @@ export class IndexerCollector extends MetricsCollector { this.rpcRequests = new Counter({ name: prefix + "_rpc_requests", help: "Count that indicates how many PRC requests are made", - labelNames: ["identificator", "network", "environment"], + labelNames: ["method", "network", "environment"], }); this.rpcLatency = new Histogram({ name: prefix + "_rpc_latency", - help: "Histogram that tracks latency of how long does it take to make request", - labelNames: ["identificator", "network", "environment"], + help: "Histogram that tracks latency of how long does it take to make request in ms", + labelNames: ["method", "network", "environment"], buckets, }); this.rpcErrors = new Counter({ name: prefix + "_rpc_errors", help: "Counter that tracks error codes from RPC endpoint", - labelNames: ["code", "identificator", "network", "environment"], + labelNames: ["code", "method", "network", "environment"], }); @@ -131,7 +131,7 @@ export class IndexerCollector extends MetricsCollector { this.latency = new Histogram({ name: prefix + "_latency", - help: "Histogram that tracks latency of how long does it take to move between dispatch, update, relay, receive or process stages.", + help: "Histogram that tracks latency of how long does it take to move between dispatch, update, relay, receive or process stages in ms", labelNames: ["stage", "home", "replica", "environment"], buckets, }); @@ -140,7 +140,7 @@ export class IndexerCollector extends MetricsCollector { this.gasUsage = new Histogram({ name: prefix + "_gas_usage", - help: "Histogram that tracks gas usage of a transaction that initiated at dispatch, update, relay, receive or process stages.", + help: "Histogram that tracks gas usage in wei of a transaction that initiated at dispatch, update, relay, receive or process stages.", labelNames: ["stage", "home", "replica", "environment"], buckets, }); @@ -191,15 +191,15 @@ export class IndexerCollector extends MetricsCollector { this.dbRequests.labels(type, this.environment).inc(req) } - incRpcRequests(identificator: RpcRequestIdentificator, network: string, req?: number) { - this.rpcRequests.labels(identificator, network, this.environment).inc(req) + incRpcRequests(method: RpcRequestMethod, network: string, req?: number) { + this.rpcRequests.labels(method, network, this.environment).inc(req) } - observeRpcLatency(identificator: RpcRequestIdentificator, network: string, ms: number) { - this.rpcLatency.labels(identificator, network, this.environment).observe(ms) + observeRpcLatency(method: RpcRequestMethod, network: string, ms: number) { + this.rpcLatency.labels(method, network, this.environment).observe(ms) } - incRpcErrors(identificator: RpcRequestIdentificator, network: string, code: string) { - this.rpcErrors.labels(code, identificator, network, this.environment).inc() + incRpcErrors(method: RpcRequestMethod, network: string, code: string) { + this.rpcErrors.labels(code, method, network, this.environment).inc() } } From 2ac21aeaab96b84258e1dae2ec49c0e0149ad6ab Mon Sep 17 00:00:00 2001 From: Daniil Naumetc Date: Fri, 4 Mar 2022 20:14:15 +0100 Subject: [PATCH 63/63] fix: naming --- tools/indexer/core/indexer.ts | 68 +++++++++++++++++------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tools/indexer/core/indexer.ts b/tools/indexer/core/indexer.ts index 037d2463b..bba22cfce 100644 --- a/tools/indexer/core/indexer.ts +++ b/tools/indexer/core/indexer.ts @@ -7,7 +7,7 @@ import { ethers } from "ethers"; import { FailureCounter, KVCache, replacer, retry, reviver } from "./utils"; import { BridgeRouter } from "@nomad-xyz/contract-interfaces/bridge"; import pLimit from 'p-limit'; -import { RpcRequestIdentificator } from "./metrics"; +import { RpcRequestMethod } from "./metrics"; import Logger from "bunyan"; type ShortTx = { @@ -136,17 +136,17 @@ export class Indexer { const [block, error] = await retry( async () => { return await this.limit(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockWithTxs, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetBlockWithTxs, this.network); const start = new Date().valueOf(); const r = await this.provider.getBlockWithTransactions(blockNumber); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockWithTxs, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetBlockWithTxs, this.network, new Date().valueOf() - start); return r; }) }, RETRIES, (error: any) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockWithTxs, this.network, error.code); + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetBlockWithTxs, this.network, error.code); this.logger.warn( `Retrying after RPC Error... Block: ${blockNumber}, Error: ${error.code}` ); @@ -182,17 +182,17 @@ export class Indexer { const [block, error] = await retry( async () => { return await this.limit(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlock, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetBlock, this.network); const start = new Date().valueOf(); const r = await this.provider.getBlock(blockNumber) - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlock, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetBlock, this.network, new Date().valueOf() - start); return r; }) }, RETRIES, (error: any) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlock, this.network, error.code); + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetBlock, this.network, error.code); this.logger.warn( `Retrying after RPC Error... Block number: ${blockNumber}, Error: ${error.code}` ) @@ -226,17 +226,17 @@ export class Indexer { const [tx, error] = await retry( async () => { return await this.limit(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTx, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetTx, this.network); const start = new Date().valueOf(); const r = await this.provider.getTransaction(hash) - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetTx, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetTx, this.network, new Date().valueOf() - start); return r; }) }, RETRIES, (error: any) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetTx, this.network, error.code); + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetTx, this.network, error.code); this.logger.warn( `Retrying after RPC Error... TX hash: ${hash}, Error: ${error.code}` ) @@ -292,17 +292,17 @@ export class Indexer { const [receipt, error] = await retry( async () => { return await this.limit(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetTxReceipt, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetTxReceipt, this.network); const start = new Date().valueOf(); const r = await this.provider.getTransactionReceipt(hash) - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetTxReceipt, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetTxReceipt, this.network, new Date().valueOf() - start); return r; }) }, RETRIES, (error: any) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetTxReceipt, this.network, error.code) + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetTxReceipt, this.network, error.code) this.logger.warn( `Retrying after RPC Error... , Error: ${error.code}` ) @@ -364,16 +364,16 @@ export class Indexer { ); const [to, error] = await retry( async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetBlockNumber, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetBlockNumber, this.network); const start = new Date().valueOf(); const r = await this.provider.getBlockNumber() - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetBlockNumber, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetBlockNumber, this.network, new Date().valueOf() - start); return r; }, RETRIES, (error: any) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, error.code) + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetBlockNumber, this.network, error.code) this.logger.warn( `Retrying after RPC Error on .getBlockNumber() method... Error: ${error}` ) @@ -530,13 +530,13 @@ export class Indexer { const allEvents = []; { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetLogs, this.network); const start = new Date().valueOf(); const r = await br.queryFilter(br.filters.Send(), from, to); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetBlockNumber, this.network, e.code) + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetBlockNumber, this.network, e.code) this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) this.failureCounter.add(); }) @@ -578,13 +578,13 @@ export class Indexer { { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetLogs, this.network); const start = new Date().valueOf(); const r = await br.queryFilter(br.filters.Receive(), from, to); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code); + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetLogs, this.network, e.code); this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) this.failureCounter.add(); }) @@ -634,13 +634,13 @@ export class Indexer { const home = this.home(); { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetLogs, this.network); const start = new Date().valueOf(); const r = await home.queryFilter(home.filters.Dispatch(), from, to); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetLogs, this.network, e.code) this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) this.failureCounter.add(); }) @@ -685,13 +685,13 @@ export class Indexer { { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetLogs, this.network); const start = new Date().valueOf(); const r = await home.queryFilter(home.filters.Update(), from, to); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetLogs, this.network, e.code) this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) this.failureCounter.add(); }) @@ -739,17 +739,17 @@ export class Indexer { const replica = this.replicaForDomain(domain); { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetLogs, this.network); const start = new Date().valueOf(); const r = await replica.queryFilter( replica.filters.Update(), from, to ); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetLogs, this.network, new Date().valueOf() - start); return r; }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetLogs, this.network, e.code) this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) this.failureCounter.add(); }) @@ -790,19 +790,19 @@ export class Indexer { { const [events, error] = await retry(async () => { - this.orchestrator.metrics.incRpcRequests(RpcRequestIdentificator.GetLogs, this.network); + this.orchestrator.metrics.incRpcRequests(RpcRequestMethod.GetLogs, this.network); const start = new Date().valueOf(); const r = await replica.queryFilter( replica.filters.Process(), from, to ); - this.orchestrator.metrics.observeRpcLatency(RpcRequestIdentificator.GetLogs, this.network, new Date().valueOf() - start); + this.orchestrator.metrics.observeRpcLatency(RpcRequestMethod.GetLogs, this.network, new Date().valueOf() - start); return r }, RETRIES, (e) => { - this.orchestrator.metrics.incRpcErrors(RpcRequestIdentificator.GetLogs, this.network, e.code) + this.orchestrator.metrics.incRpcErrors(RpcRequestMethod.GetLogs, this.network, e.code) this.logger.warn(`Some error happened at retrying getting logs between blocks ${from} and ${to}, error: ${e.message}`) this.failureCounter.add(); })