From faffdfd5b9384d6c775f92f9940e5d237ce9f502 Mon Sep 17 00:00:00 2001 From: 0xyaco Date: Fri, 26 Jul 2024 15:04:45 -0300 Subject: [PATCH] feat: search block by timestamp with binsearch (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 🤖 Linear Closes GRT-39 ## Description Implements the `EvmProvider.getEpochBlockNumber` using a binary search with an optimization that takes into account that the current epoch begins near the last block. --- package.json | 5 +- packages/blocknumber/package.json | 5 +- packages/blocknumber/src/exceptions/index.ts | 6 + .../src/exceptions/invalidTimestamp.ts | 7 + .../src/exceptions/lastBlockEpoch.ts | 11 + .../src/exceptions/timestampNotFound.ts | 7 + .../src/exceptions/unexpectedSearchRange.ts | 9 + .../src/exceptions/unsupportedBlockNumber.ts | 7 + .../exceptions/unsupportedBlockTimestamps.ts | 7 + .../src/providers/blockNumberProvider.ts | 13 + .../src/providers/evmBlockNumberProvider.ts | 236 ++++++++++ packages/blocknumber/src/utils/index.ts | 1 + packages/blocknumber/src/utils/logger.ts | 15 + .../providers/evmBlockNumberProvider.spec.ts | 165 +++++++ pnpm-lock.yaml | 405 +++++++++++++++++- 15 files changed, 896 insertions(+), 3 deletions(-) create mode 100644 packages/blocknumber/src/exceptions/invalidTimestamp.ts create mode 100644 packages/blocknumber/src/exceptions/lastBlockEpoch.ts create mode 100644 packages/blocknumber/src/exceptions/timestampNotFound.ts create mode 100644 packages/blocknumber/src/exceptions/unexpectedSearchRange.ts create mode 100644 packages/blocknumber/src/exceptions/unsupportedBlockNumber.ts create mode 100644 packages/blocknumber/src/exceptions/unsupportedBlockTimestamps.ts create mode 100644 packages/blocknumber/src/providers/blockNumberProvider.ts create mode 100644 packages/blocknumber/src/providers/evmBlockNumberProvider.ts create mode 100644 packages/blocknumber/src/utils/logger.ts create mode 100644 packages/blocknumber/test/providers/evmBlockNumberProvider.spec.ts diff --git a/package.json b/package.json index abfffdc..92defad 100644 --- a/package.json +++ b/package.json @@ -37,5 +37,8 @@ "*": "prettier --write --ignore-unknown", "*.js,*.ts": "eslint --fix" }, - "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903" + "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903", + "dependencies": { + "winston": "3.13.1" + } } diff --git a/packages/blocknumber/package.json b/packages/blocknumber/package.json index 90949f5..4e8339b 100644 --- a/packages/blocknumber/package.json +++ b/packages/blocknumber/package.json @@ -15,5 +15,8 @@ }, "keywords": [], "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "viem": "2.17.10" + } } diff --git a/packages/blocknumber/src/exceptions/index.ts b/packages/blocknumber/src/exceptions/index.ts index 0b490bb..0354a89 100644 --- a/packages/blocknumber/src/exceptions/index.ts +++ b/packages/blocknumber/src/exceptions/index.ts @@ -1 +1,7 @@ export * from "./invalidChain.js"; +export * from "./invalidTimestamp.js"; +export * from "./lastBlockEpoch.js"; +export * from "./timestampNotFound.js"; +export * from "./unexpectedSearchRange.js"; +export * from "./unsupportedBlockNumber.js"; +export * from "./unsupportedBlockTimestamps.js"; diff --git a/packages/blocknumber/src/exceptions/invalidTimestamp.ts b/packages/blocknumber/src/exceptions/invalidTimestamp.ts new file mode 100644 index 0000000..1eac699 --- /dev/null +++ b/packages/blocknumber/src/exceptions/invalidTimestamp.ts @@ -0,0 +1,7 @@ +export class InvalidTimestamp extends Error { + constructor(timestamp: number | bigint) { + super(`Timestamp ${timestamp} is prior the timestamp of the first block.`); + + this.name = "InvalidTimestamp"; + } +} diff --git a/packages/blocknumber/src/exceptions/lastBlockEpoch.ts b/packages/blocknumber/src/exceptions/lastBlockEpoch.ts new file mode 100644 index 0000000..40ff8bd --- /dev/null +++ b/packages/blocknumber/src/exceptions/lastBlockEpoch.ts @@ -0,0 +1,11 @@ +import { Block } from "viem"; + +export class LastBlockEpoch extends Error { + constructor(block: Block) { + super( + `Cannot specify the start of the epoch with the last block only (number: ${block.number}), wait for it to be finalized.`, + ); + + this.name = "LastBlockEpoch"; + } +} diff --git a/packages/blocknumber/src/exceptions/timestampNotFound.ts b/packages/blocknumber/src/exceptions/timestampNotFound.ts new file mode 100644 index 0000000..ad3251b --- /dev/null +++ b/packages/blocknumber/src/exceptions/timestampNotFound.ts @@ -0,0 +1,7 @@ +export class TimestampNotFound extends Error { + constructor(timestamp: number | bigint) { + super(`No block was processed during ${timestamp}.`); + + this.name = "TimestampNotFound"; + } +} diff --git a/packages/blocknumber/src/exceptions/unexpectedSearchRange.ts b/packages/blocknumber/src/exceptions/unexpectedSearchRange.ts new file mode 100644 index 0000000..1811c18 --- /dev/null +++ b/packages/blocknumber/src/exceptions/unexpectedSearchRange.ts @@ -0,0 +1,9 @@ +export class UnexpectedSearchRange extends Error { + constructor(low: bigint, high: bigint) { + super( + `Lower bound of search range (${low}) must be less than or equal to upper bound (${high})`, + ); + + this.name = "UnexpectedSearchRange"; + } +} diff --git a/packages/blocknumber/src/exceptions/unsupportedBlockNumber.ts b/packages/blocknumber/src/exceptions/unsupportedBlockNumber.ts new file mode 100644 index 0000000..ace46c2 --- /dev/null +++ b/packages/blocknumber/src/exceptions/unsupportedBlockNumber.ts @@ -0,0 +1,7 @@ +export class UnsupportedBlockNumber extends Error { + constructor(timestamp: bigint) { + super(`Block with null block number at ${timestamp}`); + + this.name = "UnsupportedBlockNumber"; + } +} diff --git a/packages/blocknumber/src/exceptions/unsupportedBlockTimestamps.ts b/packages/blocknumber/src/exceptions/unsupportedBlockTimestamps.ts new file mode 100644 index 0000000..91c0f29 --- /dev/null +++ b/packages/blocknumber/src/exceptions/unsupportedBlockTimestamps.ts @@ -0,0 +1,7 @@ +export class UnsupportedBlockTimestamps extends Error { + constructor(timestamp: number | bigint) { + super(`Found multiple blocks at ${timestamp}.`); + + this.name = "UnsupportedBlockTimestamps"; + } +} diff --git a/packages/blocknumber/src/providers/blockNumberProvider.ts b/packages/blocknumber/src/providers/blockNumberProvider.ts new file mode 100644 index 0000000..0b3b74e --- /dev/null +++ b/packages/blocknumber/src/providers/blockNumberProvider.ts @@ -0,0 +1,13 @@ +export interface BlockNumberProvider { + /** + * Get the block number corresponding to the beginning of the epoch. + * + * The input timestamp falls between the timestamps of the found block and + * the immediately following block. + * + * @param timestamp UTC timestamp in ms since UNIX epoch + * + * @returns the corresponding block number of a chain at a specific timestamp + */ + getEpochBlockNumber(timestamp: number): Promise; +} diff --git a/packages/blocknumber/src/providers/evmBlockNumberProvider.ts b/packages/blocknumber/src/providers/evmBlockNumberProvider.ts new file mode 100644 index 0000000..8bbf88f --- /dev/null +++ b/packages/blocknumber/src/providers/evmBlockNumberProvider.ts @@ -0,0 +1,236 @@ +import { Block, PublicClient } from "viem"; + +import { + InvalidTimestamp, + LastBlockEpoch, + TimestampNotFound, + UnexpectedSearchRange, + UnsupportedBlockNumber, + UnsupportedBlockTimestamps, +} from "../exceptions/index.js"; +import logger from "../utils/logger.js"; +import { BlockNumberProvider } from "./blockNumberProvider.js"; + +const BINARY_SEARCH_BLOCKS_LOOKBACK = 10_000n; +const BINARY_SEARCH_DELTA_MULTIPLIER = 2n; + +type BlockWithNumber = Omit & { number: bigint }; + +interface SearchConfig { + /** + * Indicates how many blocks should be used for estimating the chain's block time + */ + blocksLookback: bigint; + + /** + * Multiplier to apply to the step, used while scanning blocks backwards, to find a + * lower bound block. + */ + deltaMultiplier: bigint; +} + +export class EvmBlockNumberProvider implements BlockNumberProvider { + private client: PublicClient; + private searchConfig: SearchConfig; + private firstBlock: Block | null; + + /** + * Creates a new instance of PublicClient. + * + * @param client the viem client to use for EVM compatible RPC node calls. + * @param searchConfig.blocksLookback amount of blocks that should be used for + * estimating the chain's block time. Defaults to 10.000 blocks. + * @param searchConfig.deltaMultiplier multiplier to apply to the step, used + * while scanning blocks backwards during lower bound search. Defaults to 2. + */ + constructor( + client: PublicClient, + searchConfig: { blocksLookback?: bigint; deltaMultiplier?: bigint }, + ) { + this.client = client; + this.searchConfig = { + blocksLookback: searchConfig.blocksLookback ?? BINARY_SEARCH_BLOCKS_LOOKBACK, + deltaMultiplier: searchConfig.deltaMultiplier ?? BINARY_SEARCH_DELTA_MULTIPLIER, + }; + this.firstBlock = null; + } + + async getEpochBlockNumber(timestamp: number): Promise { + // An optimized binary search is used to look for the epoch block. + const _timestamp = BigInt(timestamp); + + // The EBO agent looks only for finalized blocks to avoid handling reorgs + const upperBoundBlock = await this.client.getBlock({ blockTag: "finalized" }); + + this.validateBlockNumber(upperBoundBlock); + + logger.info( + `Working with latest block (number: ${upperBoundBlock.number}, timestamp: ${upperBoundBlock.timestamp})...`, + ); + + const firstBlock = await this.getFirstBlock(); + + if (_timestamp < firstBlock.timestamp) throw new InvalidTimestamp(_timestamp); + if (_timestamp >= upperBoundBlock.timestamp) throw new LastBlockEpoch(upperBoundBlock); + + // Reduces the search space by estimating a lower bound for the binary search. + // + // Performing a binary search between block 0 and last block is not efficient. + const lowerBoundBlock = await this.calculateLowerBoundBlock(_timestamp, upperBoundBlock); + + // Searches for the timestamp with a binary search + return this.searchTimestamp(_timestamp, { + fromBlock: lowerBoundBlock.number, + toBlock: upperBoundBlock.number, + }); + } + + /** + * Fetches and caches the first block. Cached block will be returned if the cache is hit. + * + * @returns the chain's first block + */ + private async getFirstBlock(): Promise { + if (this.firstBlock !== null) return this.firstBlock; + + this.firstBlock = await this.client.getBlock({ blockNumber: 0n }); + + return this.firstBlock; + } + + /** + * Validates that a block contains a non-null number + * + * @param block viem block + * @throws {UnsupportedBlockNumber} when block contains a null number + * @returns true if the block contains a non-null number + */ + private validateBlockNumber(block: Block): block is BlockWithNumber { + if (block.number === null) throw new UnsupportedBlockNumber(block.timestamp); + + return true; + } + + /** + * Searches for an efficient lower bound to run the binary search, leveraging that + * the epoch start tends to be relatively near the last block. + * + * The amount of blocks to look back from the last block is estimated, using an + * estimated block-time based on the last `searchConfig.blocksLookback` blocks. + * + * Until a block with a timestamp before the input timestamp is found, backward + * exponentially grown steps are performed. + * + * @param timestamp timestamp of the epoch start + * @param lastBlock last block of the chain + * @returns an optimized lower bound for a binary search space + */ + private async calculateLowerBoundBlock(timestamp: bigint, lastBlock: BlockWithNumber) { + const { blocksLookback, deltaMultiplier } = this.searchConfig; + + const estimatedBlockTime = await this.estimateBlockTime(lastBlock, blocksLookback); + const timestampDelta = lastBlock.timestamp - timestamp; + let candidateBlockNumber = lastBlock.number - timestampDelta / estimatedBlockTime; + + const baseStep = (lastBlock.number - candidateBlockNumber) * deltaMultiplier; + + logger.info("Calculating lower bound for binary search..."); + + let searchCount = 0n; + while (candidateBlockNumber >= 0) { + const candidate = await this.client.getBlock({ blockNumber: candidateBlockNumber }); + + if (candidate.timestamp < timestamp) { + logger.info(`Estimated lower bound at block ${candidate.number}.`); + + return candidate; + } + + searchCount++; + candidateBlockNumber = lastBlock.number - baseStep * 2n ** searchCount; + } + + const firstBlock = await this.client.getBlock({ blockNumber: 0n }); + + if (firstBlock.timestamp <= timestamp) { + return firstBlock; + } + + throw new TimestampNotFound(timestamp); + } + + /** + * Estimates the chain's block time based on the last `blocksLookback` blocks. + * + * @param lastBlock last chain block + * @param blocksLookback amount of blocks to look back + * @returns the estimated block time + */ + private async estimateBlockTime(lastBlock: BlockWithNumber, blocksLookback: bigint) { + logger.info("Estimating block time..."); + + const pastBlock = await this.client.getBlock({ + blockNumber: lastBlock.number - BigInt(blocksLookback), + }); + + const estimatedBlockTime = (lastBlock.timestamp - pastBlock.timestamp) / blocksLookback; + + logger.info(`Estimated block time: ${estimatedBlockTime}.`); + + return estimatedBlockTime; + } + + /** + * Performs a binary search in the specified block range to find the block corresponding to a timestamp. + * + * @param timestamp timestamp to find the block for + * @param between blocks search space + * @throws {UnsupportedBlockTimestamps} when two consecutive blocks with the same timestamp are found + * during the search. These chains are not supported at the moment. + * @throws {TimestampNotFound} when the search is finished and no block includes the searched timestamp + * @returns the block number + */ + private async searchTimestamp( + timestamp: bigint, + between: { fromBlock: bigint; toBlock: bigint }, + ) { + let currentBlockNumber: bigint; + let { fromBlock: low, toBlock: high } = between; + + if (low > high) throw new UnexpectedSearchRange(low, high); + + logger.debug(`Starting block binary search for timestamp ${timestamp}...`); + + while (low <= high) { + currentBlockNumber = (high + low) / 2n; + + const currentBlock = await this.client.getBlock({ blockNumber: currentBlockNumber }); + const nextBlock = await this.client.getBlock({ blockNumber: currentBlockNumber + 1n }); + + logger.debug( + `Analyzing block number #${currentBlock.number} with timestamp ${currentBlock.timestamp}`, + ); + + // We do not support blocks with equal timestamps (nor non linear or non sequential chains). + // We could support same timestamps blocks by defining a criteria based on block height + // apart from their timestamps. + if (nextBlock.timestamp <= currentBlock.timestamp) + throw new UnsupportedBlockTimestamps(timestamp); + + const blockContainsTimestamp = + currentBlock.timestamp <= timestamp && nextBlock.timestamp > timestamp; + + if (blockContainsTimestamp) { + logger.debug(`Block #${currentBlock.number} contains timestamp.`); + + return currentBlock.number; + } else if (currentBlock.timestamp <= timestamp) { + low = currentBlockNumber + 1n; + } else { + high = currentBlockNumber - 1n; + } + } + + throw new TimestampNotFound(timestamp); + } +} diff --git a/packages/blocknumber/src/utils/index.ts b/packages/blocknumber/src/utils/index.ts index 8e507d6..2697322 100644 --- a/packages/blocknumber/src/utils/index.ts +++ b/packages/blocknumber/src/utils/index.ts @@ -1 +1,2 @@ export * from "./chainId.js"; +export * from "./logger.js"; diff --git a/packages/blocknumber/src/utils/logger.ts b/packages/blocknumber/src/utils/logger.ts new file mode 100644 index 0000000..c18686d --- /dev/null +++ b/packages/blocknumber/src/utils/logger.ts @@ -0,0 +1,15 @@ +import winston from "winston"; + +const logger = winston.createLogger({ + level: "info", + format: winston.format.json(), + defaultMeta: { service: "blocknumber" }, + transports: [ + new winston.transports.Console({ + format: winston.format.simple(), + silent: process.env.NODE_ENV == "test", + }), + ], +}); + +export default logger; diff --git a/packages/blocknumber/test/providers/evmBlockNumberProvider.spec.ts b/packages/blocknumber/test/providers/evmBlockNumberProvider.spec.ts new file mode 100644 index 0000000..e2ed814 --- /dev/null +++ b/packages/blocknumber/test/providers/evmBlockNumberProvider.spec.ts @@ -0,0 +1,165 @@ +import { Block, createPublicClient, GetBlockParameters, http } from "viem"; +import { mainnet } from "viem/chains"; +import { describe, expect, it, vi } from "vitest"; + +import { + InvalidTimestamp, + LastBlockEpoch, + UnsupportedBlockNumber, + UnsupportedBlockTimestamps, +} from "../../src/exceptions/index.js"; +import { EvmBlockNumberProvider } from "../../src/providers/evmBlockNumberProvider.js"; + +describe("EvmBlockNumberProvider", () => { + describe("getEpochBlockNumber", () => { + const searchConfig = { blocksLookback: 2n, deltaMultiplier: 2n }; + let evmProvider: EvmBlockNumberProvider; + + it("returns the first of two consecutive blocks when their timestamp contains the searched timestamp", async () => { + const blockNumber = 10n; + const startTimestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const endTimestamp = Date.UTC(2024, 1, 11, 0, 0, 0, 0); + const rpcProvider = mockRpcProvider(blockNumber, startTimestamp, endTimestamp); + + evmProvider = new EvmBlockNumberProvider(rpcProvider, searchConfig); + + const day5 = Date.UTC(2024, 1, 5, 2, 0, 0, 0); + const epochBlockNumber = await evmProvider.getEpochBlockNumber(day5); + + expect(epochBlockNumber).toEqual(4n); + }); + + it("returns the block number when the timestamp is equal to block's timestamp", async () => { + const lastBlockNumber = 10n; + const startTimestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const endTimestamp = Date.UTC(2024, 1, 1, 0, 0, 11, 0); + const rpcProvider = mockRpcProvider(lastBlockNumber, startTimestamp, endTimestamp); + + evmProvider = new EvmBlockNumberProvider(rpcProvider, searchConfig); + + const exactDay5 = Date.UTC(2024, 1, 1, 0, 0, 5, 0); + const epochBlockNumber = await evmProvider.getEpochBlockNumber(exactDay5); + + expect(epochBlockNumber).toEqual(4n); + }); + + it("throws if the search timestamp is after the last block's timestamp", async () => { + const lastBlockNumber = 10n; + const startTimestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const endTimestamp = Date.UTC(2024, 1, 1, 0, 0, 11, 0); + const rpcProvider = mockRpcProvider(lastBlockNumber, startTimestamp, endTimestamp); + + evmProvider = new EvmBlockNumberProvider(rpcProvider, searchConfig); + + const futureTimestamp = Date.UTC(2025, 1, 1, 0, 0, 0, 0); + + expect(evmProvider.getEpochBlockNumber(futureTimestamp)).rejects.toBeInstanceOf( + LastBlockEpoch, + ); + }); + + it("fails if the timestamp is before the first block", async () => { + const lastBlockNumber = 10n; + const startTimestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const endTimestamp = Date.UTC(2024, 1, 1, 0, 0, 11, 0); + const rpcProvider = mockRpcProvider(lastBlockNumber, startTimestamp, endTimestamp); + + evmProvider = new EvmBlockNumberProvider(rpcProvider, searchConfig); + + const futureTimestamp = Date.UTC(1970, 1, 1, 0, 0, 0, 0); + + expect(evmProvider.getEpochBlockNumber(futureTimestamp)).rejects.toBeInstanceOf( + InvalidTimestamp, + ); + }); + + it("fails when finding multiple blocks with the same timestamp", () => { + const timestamp = BigInt(Date.UTC(2024, 1, 1, 0, 0, 0, 0)); + const afterTimestamp = BigInt(Date.UTC(2024, 1, 2, 0, 0, 0, 0)); + const rpcProvider = mockRpcProviderBlocks([ + { number: 0n, timestamp: timestamp }, + { number: 1n, timestamp: timestamp }, + { number: 2n, timestamp: timestamp }, + { number: 3n, timestamp: timestamp }, + { number: 4n, timestamp: afterTimestamp }, + ]); + + evmProvider = new EvmBlockNumberProvider(rpcProvider, searchConfig); + + expect(evmProvider.getEpochBlockNumber(Number(timestamp))).rejects.toBeInstanceOf( + UnsupportedBlockTimestamps, + ); + }); + + it("fails when finding a block with no number", () => { + const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + const rpcProvider = mockRpcProviderBlocks([ + { number: null, timestamp: BigInt(timestamp) }, + ]); + + evmProvider = new EvmBlockNumberProvider(rpcProvider, searchConfig); + + expect(evmProvider.getEpochBlockNumber(Number(timestamp))).rejects.toBeInstanceOf( + UnsupportedBlockNumber, + ); + }); + + it("fails when the data provider fails", () => { + const client = createPublicClient({ chain: mainnet, transport: http() }); + + client.getBlock = vi.fn().mockRejectedValue(null); + + evmProvider = new EvmBlockNumberProvider(client, searchConfig); + const timestamp = Date.UTC(2024, 1, 1, 0, 0, 0, 0); + + expect(evmProvider.getEpochBlockNumber(timestamp)).rejects.toBeDefined(); + }); + }); +}); + +function mockRpcProvider(lastBlock: bigint, startTimestamp: number, endTimestamp: number) { + const chainDuration = endTimestamp - startTimestamp; + const blockDuration = BigInt(chainDuration) / lastBlock; + + const rpcProvider = createPublicClient({ chain: mainnet, transport: http() }); + + rpcProvider.getBlock = vi + .fn() + .mockImplementation((args?: GetBlockParameters | undefined) => { + if (args?.blockTag == "finalized") { + return Promise.resolve({ + timestamp: BigInt(endTimestamp), + number: lastBlock, + }); + } else if (args?.blockNumber !== undefined) { + const blockNumber = args.blockNumber; + const blockTimestamp = BigInt(startTimestamp) + blockNumber * blockDuration; + + return Promise.resolve({ timestamp: blockTimestamp, number: blockNumber }); + } + + throw new Error("Unhandled getBlock mock case"); + }); + + return rpcProvider; +} + +function mockRpcProviderBlocks(blocks: Pick[]) { + const rpcProvider = createPublicClient({ chain: mainnet, transport: http() }); + + rpcProvider.getBlock = vi + .fn() + .mockImplementation((args?: GetBlockParameters | undefined) => { + if (args?.blockTag == "finalized") { + return Promise.resolve(blocks[blocks.length - 1]); + } else if (args?.blockNumber !== undefined) { + const blockNumber = Number(args.blockNumber); + + return Promise.resolve(blocks[blockNumber]); + } + + throw new Error("Unhandled getBlock mock case"); + }); + + return rpcProvider; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56d01ae..86ac1c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,10 @@ settings: importers: .: + dependencies: + winston: + specifier: 3.13.1 + version: 3.13.1 devDependencies: "@commitlint/cli": specifier: 19.3.0 @@ -53,9 +57,19 @@ importers: specifier: 2.0.3 version: 2.0.3(@types/node@20.14.11) - packages/blocknumber: {} + packages/blocknumber: + dependencies: + viem: + specifier: 2.17.10 + version: 2.17.10(typescript@5.5.3) packages: + "@adraffy/ens-normalize@1.10.0": + resolution: + { + integrity: sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==, + } + "@ampproject/remapping@2.3.0": resolution: { @@ -219,6 +233,13 @@ packages: integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, } + "@colors/colors@1.6.0": + resolution: + { + integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==, + } + engines: { node: ">=0.1.90" } + "@commitlint/cli@19.3.0": resolution: { @@ -339,6 +360,12 @@ packages: } engines: { node: ">=v18" } + "@dabh/diagnostics@2.0.3": + resolution: + { + integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==, + } + "@esbuild/aix-ppc64@0.21.5": resolution: { @@ -657,6 +684,19 @@ packages: integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==, } + "@noble/curves@1.4.0": + resolution: + { + integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==, + } + + "@noble/hashes@1.4.0": + resolution: + { + integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==, + } + engines: { node: ">= 16" } + "@nodelib/fs.scandir@2.1.5": resolution: { @@ -820,6 +860,24 @@ packages: cpu: [x64] os: [win32] + "@scure/base@1.1.7": + resolution: + { + integrity: sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==, + } + + "@scure/bip32@1.4.0": + resolution: + { + integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==, + } + + "@scure/bip39@1.3.0": + resolution: + { + integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==, + } + "@types/conventional-commits-parser@5.0.0": resolution: { @@ -838,6 +896,12 @@ packages: integrity: sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==, } + "@types/triple-beam@1.3.5": + resolution: + { + integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==, + } + "@typescript-eslint/eslint-plugin@7.16.1": resolution: { @@ -977,6 +1041,20 @@ packages: } hasBin: true + abitype@1.0.5: + resolution: + { + integrity: sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==, + } + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + acorn-jsx@5.3.2: resolution: { @@ -1073,6 +1151,12 @@ packages: } engines: { node: ">=12" } + async@3.2.5: + resolution: + { + integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==, + } + balanced-match@1.0.2: resolution: { @@ -1207,12 +1291,30 @@ packages: integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, } + color-string@1.9.1: + resolution: + { + integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==, + } + + color@3.2.1: + resolution: + { + integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==, + } + colorette@2.0.20: resolution: { integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==, } + colorspace@1.1.4: + resolution: + { + integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==, + } + commander@12.1.0: resolution: { @@ -1373,6 +1475,12 @@ packages: integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, } + enabled@2.0.0: + resolution: + { + integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==, + } + env-paths@2.2.1: resolution: { @@ -1560,6 +1668,12 @@ packages: integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==, } + fecha@4.2.3: + resolution: + { + integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==, + } + file-entry-cache@6.0.1: resolution: { @@ -1601,6 +1715,12 @@ packages: integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==, } + fn.name@1.1.0: + resolution: + { + integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==, + } + foreground-child@3.2.1: resolution: { @@ -1814,6 +1934,12 @@ packages: integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, } + is-arrayish@0.3.2: + resolution: + { + integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==, + } + is-extglob@2.1.1: resolution: { @@ -1870,6 +1996,13 @@ packages: } engines: { node: ">=8" } + is-stream@2.0.1: + resolution: + { + integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, + } + engines: { node: ">=8" } + is-stream@3.0.0: resolution: { @@ -1890,6 +2023,14 @@ packages: integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, } + isows@1.0.4: + resolution: + { + integrity: sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==, + } + peerDependencies: + ws: "*" + istanbul-lib-coverage@3.2.2: resolution: { @@ -2009,6 +2150,12 @@ packages: integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, } + kuler@2.0.0: + resolution: + { + integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==, + } + levn@0.4.1: resolution: { @@ -2119,6 +2266,13 @@ packages: } engines: { node: ">=18" } + logform@2.6.1: + resolution: + { + integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==, + } + engines: { node: ">= 12.0.0" } + loupe@3.1.1: resolution: { @@ -2262,6 +2416,12 @@ packages: integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, } + one-time@1.0.0: + resolution: + { + integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==, + } + onetime@5.1.2: resolution: { @@ -2456,6 +2616,13 @@ packages: integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, } + readable-stream@3.6.2: + resolution: + { + integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, + } + engines: { node: ">= 6" } + require-directory@2.1.1: resolution: { @@ -2526,6 +2693,19 @@ packages: integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, } + safe-buffer@5.2.1: + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, + } + + safe-stable-stringify@2.4.3: + resolution: + { + integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==, + } + engines: { node: ">=10" } + semver@6.3.1: resolution: { @@ -2574,6 +2754,12 @@ packages: } engines: { node: ">=14" } + simple-swizzle@0.2.2: + resolution: + { + integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==, + } + slash@3.0.0: resolution: { @@ -2609,6 +2795,12 @@ packages: } engines: { node: ">= 10.x" } + stack-trace@0.0.10: + resolution: + { + integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==, + } + stackback@0.0.2: resolution: { @@ -2649,6 +2841,12 @@ packages: } engines: { node: ">=18" } + string_decoder@1.3.0: + resolution: + { + integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, + } + strip-ansi@6.0.1: resolution: { @@ -2718,6 +2916,12 @@ packages: } engines: { node: ">=8" } + text-hex@1.0.0: + resolution: + { + integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==, + } + text-table@0.2.0: resolution: { @@ -2771,6 +2975,13 @@ packages: } engines: { node: ">=8.0" } + triple-beam@1.4.1: + resolution: + { + integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==, + } + engines: { node: ">= 14.0.0" } + ts-api-utils@1.3.0: resolution: { @@ -2891,6 +3102,23 @@ packages: integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, } + util-deprecate@1.0.2: + resolution: + { + integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, + } + + viem@2.17.10: + resolution: + { + integrity: sha512-nubTeRBI3wsmYGemlg9PsbhunTSJSYUlq8VjzIQkbowCSPSd1bqzVeUU0qEOXsGtgRiFfBzXkkxFknZtBOAx/Q==, + } + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + vite-node@2.0.3: resolution: { @@ -2974,6 +3202,20 @@ packages: engines: { node: ">=8" } hasBin: true + winston-transport@4.7.1: + resolution: + { + integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==, + } + engines: { node: ">= 12.0.0" } + + winston@3.13.1: + resolution: + { + integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==, + } + engines: { node: ">= 12.0.0" } + word-wrap@1.2.5: resolution: { @@ -3008,6 +3250,21 @@ packages: integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, } + ws@8.17.1: + resolution: + { + integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==, + } + engines: { node: ">=10.0.0" } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + y18n@5.0.8: resolution: { @@ -3058,6 +3315,8 @@ packages: engines: { node: ">=12.20" } snapshots: + "@adraffy/ens-normalize@1.10.0": {} + "@ampproject/remapping@2.3.0": dependencies: "@jridgewell/gen-mapping": 0.3.5 @@ -3198,6 +3457,8 @@ snapshots: "@bcoe/v8-coverage@0.2.3": {} + "@colors/colors@1.6.0": {} + "@commitlint/cli@19.3.0(@types/node@20.14.11)(typescript@5.5.3)": dependencies: "@commitlint/format": 19.3.0 @@ -3309,6 +3570,12 @@ snapshots: "@types/conventional-commits-parser": 5.0.0 chalk: 5.3.0 + "@dabh/diagnostics@2.0.3": + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + "@esbuild/aix-ppc64@0.21.5": optional: true @@ -3453,6 +3720,12 @@ snapshots: "@jridgewell/resolve-uri": 3.1.2 "@jridgewell/sourcemap-codec": 1.5.0 + "@noble/curves@1.4.0": + dependencies: + "@noble/hashes": 1.4.0 + + "@noble/hashes@1.4.0": {} + "@nodelib/fs.scandir@2.1.5": dependencies: "@nodelib/fs.stat": 2.0.5 @@ -3518,6 +3791,19 @@ snapshots: "@rollup/rollup-win32-x64-msvc@4.18.1": optional: true + "@scure/base@1.1.7": {} + + "@scure/bip32@1.4.0": + dependencies: + "@noble/curves": 1.4.0 + "@noble/hashes": 1.4.0 + "@scure/base": 1.1.7 + + "@scure/bip39@1.3.0": + dependencies: + "@noble/hashes": 1.4.0 + "@scure/base": 1.1.7 + "@types/conventional-commits-parser@5.0.0": dependencies: "@types/node": 20.14.11 @@ -3528,6 +3814,8 @@ snapshots: dependencies: undici-types: 5.26.5 + "@types/triple-beam@1.3.5": {} + "@typescript-eslint/eslint-plugin@7.16.1(@typescript-eslint/parser@7.16.1(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)": dependencies: "@eslint-community/regexpp": 4.11.0 @@ -3668,6 +3956,10 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 + abitype@1.0.5(typescript@5.5.3): + optionalDependencies: + typescript: 5.5.3 + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 @@ -3712,6 +4004,8 @@ snapshots: assertion-error@2.0.1: {} + async@3.2.5: {} + balanced-match@1.0.2: {} brace-expansion@1.1.11: @@ -3790,8 +4084,23 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + colorette@2.0.20: {} + colorspace@1.1.4: + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + commander@12.1.0: {} compare-func@2.0.0: @@ -3872,6 +4181,8 @@ snapshots: emoji-regex@9.2.2: {} + enabled@2.0.0: {} + env-paths@2.2.1: {} error-ex@1.3.2: @@ -4031,6 +4342,8 @@ snapshots: dependencies: reusify: 1.0.4 + fecha@4.2.3: {} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -4058,6 +4371,8 @@ snapshots: flatted@3.3.1: {} + fn.name@1.1.0: {} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 @@ -4163,6 +4478,8 @@ snapshots: is-arrayish@0.2.1: {} + is-arrayish@0.3.2: {} + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -4183,6 +4500,8 @@ snapshots: is-path-inside@3.0.3: {} + is-stream@2.0.1: {} + is-stream@3.0.0: {} is-text-path@2.0.0: @@ -4191,6 +4510,10 @@ snapshots: isexe@2.0.0: {} + isows@1.0.4(ws@8.17.1): + dependencies: + ws: 8.17.1 + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -4248,6 +4571,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kuler@2.0.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -4315,6 +4640,15 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 + logform@2.6.1: + dependencies: + "@colors/colors": 1.6.0 + "@types/triple-beam": 1.3.5 + fecha: 4.2.3 + ms: 2.1.2 + safe-stable-stringify: 2.4.3 + triple-beam: 1.4.1 + loupe@3.1.1: dependencies: get-func-name: 2.0.2 @@ -4382,6 +4716,10 @@ snapshots: dependencies: wrappy: 1.0.2 + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -4473,6 +4811,12 @@ snapshots: queue-microtask@1.2.3: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -4520,6 +4864,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 + safe-buffer@5.2.1: {} + + safe-stable-stringify@2.4.3: {} + semver@6.3.1: {} semver@7.6.3: {} @@ -4536,6 +4884,10 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + slash@3.0.0: {} slice-ansi@5.0.0: @@ -4552,6 +4904,8 @@ snapshots: split2@4.2.0: {} + stack-trace@0.0.10: {} + stackback@0.0.2: {} std-env@3.7.0: {} @@ -4576,6 +4930,10 @@ snapshots: get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -4613,6 +4971,8 @@ snapshots: text-extensions@2.4.0: {} + text-hex@1.0.0: {} + text-table@0.2.0: {} through@2.3.8: {} @@ -4631,6 +4991,8 @@ snapshots: dependencies: is-number: 7.0.0 + triple-beam@1.4.1: {} + ts-api-utils@1.3.0(typescript@5.5.3): dependencies: typescript: 5.5.3 @@ -4686,6 +5048,25 @@ snapshots: dependencies: punycode: 2.3.1 + util-deprecate@1.0.2: {} + + viem@2.17.10(typescript@5.5.3): + dependencies: + "@adraffy/ens-normalize": 1.10.0 + "@noble/curves": 1.4.0 + "@noble/hashes": 1.4.0 + "@scure/bip32": 1.4.0 + "@scure/bip39": 1.3.0 + abitype: 1.0.5(typescript@5.5.3) + isows: 1.0.4(ws@8.17.1) + ws: 8.17.1 + optionalDependencies: + typescript: 5.5.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + vite-node@2.0.3(@types/node@20.14.11): dependencies: cac: 6.7.14 @@ -4753,6 +5134,26 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + winston-transport@4.7.1: + dependencies: + logform: 2.6.1 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.13.1: + dependencies: + "@colors/colors": 1.6.0 + "@dabh/diagnostics": 2.0.3 + async: 3.2.5 + is-stream: 2.0.1 + logform: 2.6.1 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.7.1 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: @@ -4775,6 +5176,8 @@ snapshots: wrappy@1.0.2: {} + ws@8.17.1: {} + y18n@5.0.8: {} yallist@3.1.1: {}