From 6aa16cec128c46ae1012378fc79f454769a299b9 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 11 Dec 2024 13:08:55 -0500 Subject: [PATCH] feat(indexer): add coingecko price collection from tokens against usd Signed-off-by: david --- packages/error-handling/src/utils/assert.ts | 7 +- .../migrations/1733941169940-HistoricPrice.ts | 14 ++++ .../migrations/1734549610006-HistoricPrice.ts | 14 ++++ .../src/entities/HistoricPrice.ts | 35 ++++++++ .../indexer-database/src/entities/index.ts | 1 + packages/indexer/package.json | 2 + packages/indexer/src/main.ts | 8 ++ packages/indexer/src/parseEnv.ts | 15 +++- packages/indexer/src/services/index.ts | 1 + .../indexer/src/services/priceProcessor.ts | 82 +++++++++++++++++++ packages/indexer/src/utils/coingeckoClient.ts | 54 ++++++++++++ pnpm-lock.yaml | 54 +++++++----- 12 files changed, 259 insertions(+), 28 deletions(-) create mode 100644 packages/indexer-database/packages/indexer-database/src/migrations/1733941169940-HistoricPrice.ts create mode 100644 packages/indexer-database/packages/indexer-database/src/migrations/1734549610006-HistoricPrice.ts create mode 100644 packages/indexer-database/src/entities/HistoricPrice.ts create mode 100644 packages/indexer/src/services/priceProcessor.ts create mode 100644 packages/indexer/src/utils/coingeckoClient.ts diff --git a/packages/error-handling/src/utils/assert.ts b/packages/error-handling/src/utils/assert.ts index f0910b1..699cbd6 100644 --- a/packages/error-handling/src/utils/assert.ts +++ b/packages/error-handling/src/utils/assert.ts @@ -8,9 +8,12 @@ import { AssertError } from "../errors/AssertError"; * @returns An assertion of `value`. * @throws {@link AssertError} if assert's validity fails */ -export function assert(value: unknown, message: string): asserts value { +export function assert( + value: unknown, + message: string, +): asserts value is NonNullable { try { - return assertModule.ok(value, message); + return assertModule.ok(value !== null && value !== undefined, message); } catch (e: unknown) { throw new AssertError(message); } diff --git a/packages/indexer-database/packages/indexer-database/src/migrations/1733941169940-HistoricPrice.ts b/packages/indexer-database/packages/indexer-database/src/migrations/1733941169940-HistoricPrice.ts new file mode 100644 index 0000000..b6489e5 --- /dev/null +++ b/packages/indexer-database/packages/indexer-database/src/migrations/1733941169940-HistoricPrice.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class HistoricPrice1733941169940 implements MigrationInterface { + name = 'HistoricPrice1733941169940' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "historic_market_price" ("id" SERIAL NOT NULL, "baseCurrency" character varying NOT NULL, "quoteCurrency" character varying NOT NULL DEFAULT 'usd', "date" date NOT NULL, "price" numeric NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UK_historic_price_baseCurrency_quoteCurrency_date" UNIQUE ("baseCurrency", "quoteCurrency", "date"), CONSTRAINT "PK_b0a22436b47e742187aa7408561" PRIMARY KEY ("id"))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "historic_market_price"`); + } + +} diff --git a/packages/indexer-database/packages/indexer-database/src/migrations/1734549610006-HistoricPrice.ts b/packages/indexer-database/packages/indexer-database/src/migrations/1734549610006-HistoricPrice.ts new file mode 100644 index 0000000..b4986e5 --- /dev/null +++ b/packages/indexer-database/packages/indexer-database/src/migrations/1734549610006-HistoricPrice.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class HistoricPrice1734549610006 implements MigrationInterface { + name = 'HistoricPrice1734549610006' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "historic_price" ("id" SERIAL NOT NULL, "baseCurrency" character varying NOT NULL, "quoteCurrency" character varying NOT NULL DEFAULT 'usd', "date" character varying NOT NULL, "price" numeric NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UK_historic_price_baseCurrency_quoteCurrency_date" UNIQUE ("baseCurrency", "quoteCurrency", "date"), CONSTRAINT "PK_77dc3f4978cdfb03f1bb3a7444b" PRIMARY KEY ("id"))`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "historic_price"`); + } + +} diff --git a/packages/indexer-database/src/entities/HistoricPrice.ts b/packages/indexer-database/src/entities/HistoricPrice.ts new file mode 100644 index 0000000..f11c4b7 --- /dev/null +++ b/packages/indexer-database/src/entities/HistoricPrice.ts @@ -0,0 +1,35 @@ +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + Unique, +} from "typeorm"; + +@Entity() +@Unique("UK_historic_price_baseCurrency_quoteCurrency_date", [ + "baseCurrency", + "quoteCurrency", + "date", +]) +export class HistoricPrice { + @PrimaryGeneratedColumn() + id: number; + + // bear in mind we are using coingecko symbols directly here, for all intents and purposes this is coingecko historic market price + @Column() + baseCurrency: string; + + @Column({ default: "usd" }) + quoteCurrency: string; + + // yyyy-LL-dd + @Column() + date: string; + + @Column({ type: "decimal" }) + price: string; + + @CreateDateColumn() + createdAt: Date; +} diff --git a/packages/indexer-database/src/entities/index.ts b/packages/indexer-database/src/entities/index.ts index 19148c7..0b04606 100644 --- a/packages/indexer-database/src/entities/index.ts +++ b/packages/indexer-database/src/entities/index.ts @@ -22,3 +22,4 @@ export * from "./RelayHashInfo"; export * from "./WebhookRequest"; export * from "./WebhookClient"; +export * from "./HistoricPrice"; diff --git a/packages/indexer/package.json b/packages/indexer/package.json index 867d4f9..123980a 100644 --- a/packages/indexer/package.json +++ b/packages/indexer/package.json @@ -28,12 +28,14 @@ "@repo/webhooks": "workspace:*", "@types/express": "^4.17.21", "@types/lodash": "^4.17.7", + "@types/luxon": "^3.4.2", "bullmq": "^5.12.12", "ethers": "^5.7.2", "express": "^4.19.2", "express-bearer-token": "^3.0.0", "ioredis": "^5.4.1", "lodash": "^4.17.21", + "luxon": "^3.5.0", "redis": "^4.7.0", "superstruct": "^2.0.3-1", "winston": "^3.13.1" diff --git a/packages/indexer/src/main.ts b/packages/indexer/src/main.ts index b896f93..2c8b3dd 100644 --- a/packages/indexer/src/main.ts +++ b/packages/indexer/src/main.ts @@ -19,6 +19,7 @@ import { IndexerQueuesService } from "./messaging/service"; import { IntegratorIdWorker } from "./messaging/IntegratorIdWorker"; import { AcrossIndexerManager } from "./data-indexing/service/AcrossIndexerManager"; import { BundleServicesManager } from "./services/BundleServicesManager"; +import { CoingeckoPriceProcessor } from "./services"; async function initializeRedis( config: parseEnv.RedisConfig, @@ -55,6 +56,10 @@ export async function Main(config: parseEnv.Config, logger: winston.Logger) { const redis = await initializeRedis(redisConfig, logger); const redisCache = new RedisCache(redis); const postgres = await connectToDatabase(postgresConfig, logger); + const priceProcessor = new CoingeckoPriceProcessor( + { symbols: config.coingeckoSymbols }, + { logger, postgres }, + ); // Call write to kick off webhook calls const { write } = await WebhookFactory(config.webhookConfig, { postgres, @@ -127,6 +132,7 @@ export async function Main(config: parseEnv.Config, logger: winston.Logger) { integratorIdWorker.close(); acrossIndexerManager.stopGracefully(); bundleServicesManager.stop(); + priceProcessor.stop(); } else { integratorIdWorker.close(); logger.info({ at: "Indexer#Main", message: "Forcing exit..." }); @@ -146,6 +152,8 @@ export async function Main(config: parseEnv.Config, logger: winston.Logger) { await Promise.allSettled([ bundleServicesManager.start(), acrossIndexerManager.start(), + // run prices call to check every minute or so. it will only cache once a day + priceProcessor.start(60), ]); logger.info({ diff --git a/packages/indexer/src/parseEnv.ts b/packages/indexer/src/parseEnv.ts index 2b2a750..376948f 100644 --- a/packages/indexer/src/parseEnv.ts +++ b/packages/indexer/src/parseEnv.ts @@ -7,6 +7,7 @@ import { WebhookTypes, parseWebhookClientsFromString, } from "@repo/webhooks"; +import { CoingeckoSymbol } from "./utils/coingeckoClient"; export type Config = { redisConfig: RedisConfig; @@ -18,6 +19,7 @@ export type Config = { enableBundleIncludedEventsService: boolean; enableBundleBuilder: boolean; webhookConfig: WebhooksConfig; + coingeckoSymbols: CoingeckoSymbol[]; }; export type RedisConfig = { host: string; @@ -29,11 +31,12 @@ export type ProviderConfig = [providerUrl: string, chainId: number]; export type Env = Record; export function parseRedisConfig(env: Env): RedisConfig { - assert(env.REDIS_HOST, "requires REDIS_HOST"); - assert(env.REDIS_PORT, "requires REDIS_PORT"); - const port = parseNumber(env.REDIS_PORT); + const { REDIS_HOST, REDIS_PORT } = env; + assert(REDIS_HOST, "requires REDIS_HOST"); + assert(REDIS_PORT, "requires REDIS_PORT"); + const port = parseNumber(REDIS_PORT); return { - host: env.REDIS_HOST, + host: REDIS_HOST, port, // @dev: this retry config is needed for bullmq workers maxRetriesPerRequest: null, @@ -193,6 +196,9 @@ export function envToConfig(env: Env): Config { enabledWebhookRequestWorkers: true, clients: parseWebhookClientsFromString(env.WEBHOOK_CLIENTS ?? "[]"), }; + const coingeckoSymbols = parseArray(env.COINGECKO_SYMBOLS).map((symbol) => + CoingeckoSymbol.create(symbol), + ); return { redisConfig, postgresConfig, @@ -203,5 +209,6 @@ export function envToConfig(env: Env): Config { enableBundleIncludedEventsService, enableBundleBuilder, webhookConfig, + coingeckoSymbols, }; } diff --git a/packages/indexer/src/services/index.ts b/packages/indexer/src/services/index.ts index 0a11129..553ec4c 100644 --- a/packages/indexer/src/services/index.ts +++ b/packages/indexer/src/services/index.ts @@ -1,3 +1,4 @@ export * from "./bundles"; export * from "./spokePoolProcessor"; export * from "./BundleBuilderService"; +export * from "./priceProcessor"; diff --git a/packages/indexer/src/services/priceProcessor.ts b/packages/indexer/src/services/priceProcessor.ts new file mode 100644 index 0000000..aac2695 --- /dev/null +++ b/packages/indexer/src/services/priceProcessor.ts @@ -0,0 +1,82 @@ +import { CoingeckoSymbol, CoingeckoClient } from "../utils/coingeckoClient"; +import { Logger } from "winston"; +import { DataSource, entities } from "@repo/indexer-database"; +import { BaseIndexer } from "../generics"; +import { DateTime } from "luxon"; + +type Config = { + symbols: CoingeckoSymbol[]; + // not used currently + quoteCurrency?: string; +}; + +type Deps = { + logger: Logger; + postgres: DataSource; +}; + +export class CoingeckoPriceProcessor extends BaseIndexer { + private coingeckoClient: CoingeckoClient; + constructor( + private config: Config, + private deps: Deps, + ) { + super(deps.logger, "CoingeckoPriceProcessor"); + this.coingeckoClient = new CoingeckoClient(); + } + + protected async indexerLogic(): Promise { + const now = Date.now(); + const dbFormattedDate = DateTime.fromMillis(now).toFormat("yyyy-LL-dd"); + const quoteCurrency = this.config.quoteCurrency ?? "usd"; + const historicPriceRepository = this.deps.postgres.getRepository( + entities.HistoricPrice, + ); + + for (const symbol of this.config.symbols) { + const existingPrice = await historicPriceRepository.findOne({ + where: { + date: dbFormattedDate, + baseCurrency: symbol, + quoteCurrency, + }, + }); + // do nothing, we have a price for this day + if (existingPrice) return; + + try { + const historicPriceData = + await this.coingeckoClient.getHistoricDailyPrice(now, symbol); + const price = + historicPriceData.market_data?.current_price[quoteCurrency]; + // wasnt able to get a price + if (price === undefined) { + this.deps.logger.error( + `Unable to find ${quoteCurrency} for ${symbol}`, + ); + return; + } + await historicPriceRepository.insert({ + date: dbFormattedDate, + baseCurrency: symbol, + quoteCurrency, + price: price.toString(), + }); + this.logger.info({ + at: "CoingeckoPriceProcessor#indexerLogic", + message: `Inserted historic price for ${symbol} on ${dbFormattedDate}`, + }); + } catch (error) { + this.logger.error({ + at: "CoingeckoPriceProcessor#indexerLogic", + message: `Failed to fetch or insert historic price for ${symbol} on ${dbFormattedDate}`, + error: (error as Error).message, + }); + } + } + } + + protected async initialize(): Promise { + // Initialization logic if needed + } +} diff --git a/packages/indexer/src/utils/coingeckoClient.ts b/packages/indexer/src/utils/coingeckoClient.ts new file mode 100644 index 0000000..9c292fb --- /dev/null +++ b/packages/indexer/src/utils/coingeckoClient.ts @@ -0,0 +1,54 @@ +import * as s from "superstruct"; +import { DateTime } from "luxon"; + +export const CoingeckoSymbol = s.enums([ + "ethereum", + "matic-network", + "wrapped-bitcoin", + "usd-coin", + "uma", + "badger-dao", + "weth", + "boba-network", + "dai", + "balancer", + "tether", + "across-protocol", + "havven", + "pooltogether", + "bridged-usd-coin-base", + "optimism", + "usd-coin-ethereum-bridged", +]); +export type CoingeckoSymbol = s.Infer; +export const CGHistoricPriceBase = s.object({ + id: s.string(), + symbol: s.string(), + name: s.string(), + market_data: s.optional( + s.object({ + current_price: s.record(s.string(), s.number()), + }), + ), +}); +export type CGHistoricPriceBase = s.Infer; + +export class CoingeckoClient { + constructor(private baseUrl: string = "https://api.coingecko.com/api/v3") {} + + // rounds timestamp to the current day + public async getHistoricDailyPrice( + timestamp: number, + symbol: CoingeckoSymbol, + ): Promise { + const cgFormattedDate = + DateTime.fromMillis(timestamp).toFormat("dd-LL-yyyy"); + const response = await fetch( + `${this.baseUrl}/coins/${symbol}/history?date=${cgFormattedDate}&localization=false`, + ); + if (!response.ok) { + throw new Error(`Error fetching historic price: ${response.statusText}`); + } + return s.create(await response.json(), CGHistoricPriceBase); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1861e18..66306ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -166,6 +166,9 @@ importers: '@types/lodash': specifier: ^4.17.7 version: 4.17.7 + '@types/luxon': + specifier: ^3.4.2 + version: 3.4.2 bullmq: specifier: ^5.12.12 version: 5.12.14 @@ -184,6 +187,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + luxon: + specifier: ^3.5.0 + version: 3.5.0 redis: specifier: ^4.7.0 version: 4.7.0 @@ -1822,6 +1828,9 @@ packages: '@types/lru-cache@5.1.1': resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==} + '@types/luxon@3.4.2': + resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} + '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} @@ -9773,7 +9782,7 @@ snapshots: dependencies: '@ledgerhq/cryptoassets': 5.53.0 '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.26.0 + '@ledgerhq/hw-transport': 5.51.1 bignumber.js: 9.1.2 rlp: 2.2.7 @@ -9790,7 +9799,7 @@ snapshots: dependencies: '@ledgerhq/devices': 5.51.1 '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.26.0 + '@ledgerhq/hw-transport': 5.51.1 '@ledgerhq/hw-transport-node-hid-noevents': 5.51.1 '@ledgerhq/logs': 5.50.0 lodash: 4.17.21 @@ -9801,7 +9810,7 @@ snapshots: '@ledgerhq/hw-transport-u2f@5.26.0': dependencies: '@ledgerhq/errors': 5.50.0 - '@ledgerhq/hw-transport': 5.26.0 + '@ledgerhq/hw-transport': 5.51.1 '@ledgerhq/logs': 5.50.0 u2f-api: 0.2.7 @@ -9816,7 +9825,6 @@ snapshots: '@ledgerhq/devices': 5.51.1 '@ledgerhq/errors': 5.50.0 events: 3.3.0 - optional: true '@ledgerhq/logs@5.50.0': {} @@ -10687,6 +10695,8 @@ snapshots: '@types/lru-cache@5.1.1': {} + '@types/luxon@3.4.2': {} + '@types/markdown-it@14.1.2': dependencies: '@types/linkify-it': 5.0.0 @@ -10704,7 +10714,7 @@ snapshots: '@types/mkdirp@0.5.2': dependencies: - '@types/node': 22.7.3 + '@types/node': 16.18.104 '@types/mocha@10.0.7': {} @@ -10747,7 +10757,7 @@ snapshots: '@types/resolve@0.0.8': dependencies: - '@types/node': 22.7.3 + '@types/node': 16.18.104 '@types/responselike@1.0.3': dependencies: @@ -11126,9 +11136,9 @@ snapshots: '@ethersproject/constants': 5.7.0 '@google-cloud/kms': 3.8.0(encoding@0.1.13) '@google-cloud/storage': 6.12.0(encoding@0.1.13) - '@nomicfoundation/hardhat-verify': 1.1.1(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.7.3)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.7.3)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-web3': 2.0.0(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.7.3)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-verify': 1.1.1(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@16.18.104)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@16.18.104)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@nomiclabs/hardhat-web3': 2.0.0(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@16.18.104)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@truffle/contract': 4.6.17(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) '@truffle/hdwallet-provider': 1.5.1-alpha.1(@babel/core@7.25.2)(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) '@types/ethereum-protocol': 1.0.5 @@ -11142,7 +11152,7 @@ snapshots: dotenv: 9.0.2 eth-crypto: 2.6.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) hardhat-deploy: 0.9.1(@ethersproject/hardware-wallets@5.7.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.7.3)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) - hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.7.3)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@16.18.104)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) hardhat-typechain: 0.3.5(hardhat@2.22.12(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.7.3)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(ts-generator@0.1.1)(typechain@4.0.3(typescript@5.5.4)) lodash.uniqby: 4.7.0 minimist: 1.2.8 @@ -11693,7 +11703,7 @@ snapshots: asn1.js@4.10.1: dependencies: - bn.js: 4.12.0 + bn.js: 4.12.1 inherits: 2.0.4 minimalistic-assert: 1.0.1 @@ -11996,7 +12006,7 @@ snapshots: browserify-rsa: 4.1.0 create-hash: 1.2.0 create-hmac: 1.1.7 - elliptic: 6.5.7 + elliptic: 6.6.1 hash-base: 3.0.4 inherits: 2.0.4 parse-asn1: 5.1.7 @@ -12495,8 +12505,8 @@ snapshots: create-ecdh@4.0.4: dependencies: - bn.js: 4.12.0 - elliptic: 6.5.7 + bn.js: 4.12.1 + elliptic: 6.6.1 create-hash@1.2.0: dependencies: @@ -12774,7 +12784,7 @@ snapshots: diffie-hellman@5.0.3: dependencies: - bn.js: 4.12.0 + bn.js: 4.12.1 miller-rabin: 4.0.1 randombytes: 2.1.0 @@ -12934,7 +12944,7 @@ snapshots: elliptic@6.5.7: dependencies: - bn.js: 4.12.0 + bn.js: 4.12.1 brorand: 1.1.0 hash.js: 1.1.7 hmac-drbg: 1.0.1 @@ -13588,7 +13598,7 @@ snapshots: ethereumjs-abi@0.6.8: dependencies: - bn.js: 4.12.0 + bn.js: 4.12.1 ethereumjs-util: 6.2.1 ethereumjs-account@2.0.5: @@ -13638,9 +13648,9 @@ snapshots: ethereumjs-util@6.2.1: dependencies: '@types/bn.js': 4.11.6 - bn.js: 4.12.0 + bn.js: 4.12.1 create-hash: 1.2.0 - elliptic: 6.5.7 + elliptic: 6.6.1 ethereum-cryptography: 0.1.3 ethjs-util: 0.1.6 rlp: 2.2.7 @@ -15909,7 +15919,7 @@ snapshots: miller-rabin@4.0.1: dependencies: - bn.js: 4.12.0 + bn.js: 4.12.1 brorand: 1.1.0 mime-db@1.52.0: {} @@ -16999,7 +17009,7 @@ snapshots: public-encrypt@4.0.3: dependencies: - bn.js: 4.12.0 + bn.js: 4.12.1 browserify-rsa: 4.1.0 create-hash: 1.2.0 parse-asn1: 5.1.7 @@ -19489,7 +19499,7 @@ snapshots: wide-align@1.1.5: dependencies: - string-width: 1.0.2 + string-width: 4.2.3 optional: true widest-line@3.1.0: