From eba80c0b271a1109bc26fbcd0e3d7ddfc8b52689 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 02:02:20 +0200 Subject: [PATCH 01/11] feat(minter-guard): verify the 2% vote quorum before denying a minter denyMinter is a shareholder veto gated on Equity.checkQualified inside the finite application period from suggestMinter, not an admin call. The guard never modelled that precondition: it retried a permanently rejected deny on every cycle, raised a critical alert per attempt, and logged the raw error message instead of the actual rejection reason. - Pre-check per cycle before any send: build the helper set, read totalVotes and votesDelegated (the values the contract itself uses) and skip loudly when under quorum or short on gas. The gas floor is checked against a worst-case ceiling before estimateGas, because a node that verifies the balance inside eth_estimateGas would otherwise turn a funding shortfall into a generic revert instead of a gas page. - Startup probe reads additive voting power (votes(signer) + sum votes(helper), which cannot revert on a stale graph) and pages once when not qualified. It does not abort bootstrap: a governance state that a delegation fixes at runtime must not take all monitoring down, and GET /guard exposes it continuously. - New pure module minter-guard.logic.ts: computeHelpers rebuilds the delegation graph from indexed Delegation events (latest-wins, transitive, cycle-safe, signer excluded, sorted strictly ascending by numeric address value as _checkDuplicatesAndSorted demands); GUARD_HELPER_ADDRESS becomes an optional seed unioned into that set. - classifyDenyError separates a permanent TooLate from transient causes and names the empty-revert-data case: the bare requires in votesDelegated carry no data and reach the client as "missing revert data", which reads like an RPC fault. A data-less error without any revert marker is classified separately so a nonce or network failure is never blamed on the helper list. - Retry policy per minter: TooLate stops immediately, anything else stops after three attempts, and the FAILED alert is sent once on the terminal state. Skip pages are rate-limited to one per hour per kind. - Just-in-time window check against applicationTimestamp + applicationPeriod with a 60s buffer, so a closed window is recorded instead of burning gas on a guaranteed revert. - tx.wait is bounded at 180s: an unbounded wait leaves the cycle flag set, so no later cycle and no sibling watcher runs, and nothing throws to alert on. - A guard init failure no longer aborts the whole monitoring process; only a missing or invalid GUARD_PRIVATE_KEY does. An empty whitelist is logged as a warning naming deny-by-default, so a truncated or unmounted file is no longer indistinguishable from the intended configuration. - GET /guard returns the live guard status (signer, voting power, quorum, qualification, helper count, gas), fail-loud on a read error rather than reporting a fabricated zero. --- shared/types.ts | 19 +- src/monitoringV2/api/api.controller.ts | 11 + src/monitoringV2/events.config.ts | 4 + src/monitoringV2/minter-guard.logic.ts | 183 ++++++ src/monitoringV2/minter-guard.service.ts | 548 ++++++++++++++++-- src/monitoringV2/monitoring.module.ts | 2 +- src/monitoringV2/monitoring.service.ts | 19 +- .../prisma/repositories/events.repository.ts | 35 ++ 8 files changed, 780 insertions(+), 41 deletions(-) create mode 100644 src/monitoringV2/minter-guard.logic.ts diff --git a/shared/types.ts b/shared/types.ts index dcc130e..1e4e038 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -144,4 +144,21 @@ export interface MinterResponse { bridgeLimit?: string; bridgeMinted?: string; bridgeHorizon?: string; // Unix timestamp in milliseconds as string -} \ No newline at end of file +} + +// Minter-guard delegation status (GET /guard). All bigints are stringified. The private key never +// leaves the backend — only the derived signer address is exposed. This shape is the backend<->frontend +// contract and MUST stay byte-identical on both sides. +export interface GuardResponse { + enabled: boolean; + signerAddress: string; + votingPowerPct: string; // percent, e.g. "1.85" + quorumPct: number; // qualification threshold in percent (2) + qualified: boolean; + helperCount: number; // helpers contributed by the Delegation graph + the optional static seed + gasBalance: string; // signer balance in native units (Citrea: cBTC, 18 decimals) + estimatedDenyCost: string; // modelled denyMinter() cost in native units + gasEnough: boolean; + equityAddress: string; + chainId: number; +} diff --git a/src/monitoringV2/api/api.controller.ts b/src/monitoringV2/api/api.controller.ts index 7f7914e..361c323 100644 --- a/src/monitoringV2/api/api.controller.ts +++ b/src/monitoringV2/api/api.controller.ts @@ -5,6 +5,7 @@ import { ChallengeResponse, ChallengeStatus, CollateralResponse, + GuardResponse, HealthResponse, PositionResponse, PositionStatus, @@ -17,6 +18,7 @@ import type { Token, PositionState } from '@prisma/client'; import { Decimal } from '@prisma/client/runtime/library'; import { ProviderService } from '../provider.service'; import { MonitoringService } from '../monitoring.service'; +import { MinterGuardService } from '../minter-guard.service'; import { AppConfigService } from 'src/config/config.service'; const jusdDecimals = 18; @@ -28,6 +30,7 @@ export class ApiController { private readonly prisma: PrismaClientService, private readonly providerService: ProviderService, private readonly monitoringService: MonitoringService, + private readonly minterGuardService: MinterGuardService, private readonly config: AppConfigService ) {} @@ -53,6 +56,14 @@ export class ApiController { }; } + @Get('guard') + @ApiOperation({ summary: 'Get minter-guard delegation status' }) + @ApiResponse({ status: 200, description: 'Guard signer address, live voting-power %, qualification and gas status' }) + async getGuardStatus(): Promise { + // Fail-loud: getStatus() throws on a genuine on-chain read failure (5xx) rather than faking 0%/false. + return this.minterGuardService.getStatus(); + } + @Get('positions') @ApiOperation({ summary: 'Get all positions' }) @ApiResponse({ status: 200, description: 'List of all positions with current state' }) diff --git a/src/monitoringV2/events.config.ts b/src/monitoringV2/events.config.ts index 08687b8..f632afd 100644 --- a/src/monitoringV2/events.config.ts +++ b/src/monitoringV2/events.config.ts @@ -26,6 +26,10 @@ export const EVENT_CONFIG: Record = { // Trading events Trade: { severity: EventSeverity.LOW, enabled: false }, + // Delegation rows feed the minter-guard helper set (computeHelpers). They are persisted regardless of + // this flag — ingestion is driven by EVENT_SIGNATURES in constants.ts, while `enabled` only gates + // Telegram alerting (telegram.service.notifyEvent). Keep it false: a delegation is routine and must + // not page. Do not remove the entry — the guard depends on these rows being indexed. Delegation: { severity: EventSeverity.LOW, enabled: false }, // Roll event diff --git a/src/monitoringV2/minter-guard.logic.ts b/src/monitoringV2/minter-guard.logic.ts new file mode 100644 index 0000000..0ebb1a8 --- /dev/null +++ b/src/monitoringV2/minter-guard.logic.ts @@ -0,0 +1,183 @@ +import { ethers } from 'ethers'; + +/** Equity QUORUM = 200 bps (2%). PRIVATE constant in Equity.sol, absent from EquityABI -> hardcoded. */ +export const QUORUM_BPS = 200n; + +export interface DenyErrorClass { + kind: 'permanent' | 'transient'; + label: string; // 'TooLate' | 'NotQualified' | 'EmptyRevert' | 'NoRevertData' | a decoded error name | 'Unknown' + detail: string; // human-readable diagnosis for logs + Telegram +} + +/** + * Pure, side-effect-free helper-set derivation for the minter-guard — the on-chain revert surface. + * + * Rebuilds the Equity delegation graph from the indexed Delegation(from, to) events and returns the set + * of addresses that must be passed to JuiceDollar.denyMinter()/Equity.votesDelegated() as `helpers` so + * the signer is Equity-qualified via its delegators. Reproduces Equity._canVoteFor (recursive, transitive): + * - latest-wins per `from` (delegateVoteTo overwrites the on-chain mapping; input MUST be ordered + * ascending by block/logIndex so the last occurrence wins), + * - walk the REVERSE graph from the signer (every address that transitively delegates to the signer), + * INCLUDING multi-hop intermediates (a -> b -> signer yields both a and b), + * - cycle-safe via a visited set (Equity allows legal delegation cycles), + * - EXCLUDE the signer itself, dedupe, + * - UNION the optional static seed helpers (GUARD_HELPER_ADDRESS) so an operator can name a helper + * explicitly without depending on the indexer (fresh/reset database, in-progress backfill, or an + * indexing gap), which also preserves the pre-existing single-helper configuration, + * - sort STRICTLY ASCENDING by BigInt(address) (uint160 order) to satisfy Equity._checkDuplicatesAndSorted + * — a plain string sort on hex misorders vs the contract's numeric comparison and would revert. + * + * All addresses are lowercased so a checksummed signer compares equal to the lowercased event args. + */ +export function computeHelpers(delegations: Array<{ from: string; to: string }>, signer: string, seedHelpers?: string[]): string[] { + const signerLc = signer.toLowerCase(); + + // Fold to the latest delegate per `from` (input already ordered ascending -> last write wins). + const latest = new Map(); + for (const d of delegations) { + latest.set(d.from.toLowerCase(), d.to.toLowerCase()); + } + + // Invert to the reverse graph: delegate `to` -> [delegators `from`...]. + const incoming = new Map(); + for (const [from, to] of latest) { + const sources = incoming.get(to); + if (sources) sources.push(from); + else incoming.set(to, [from]); + } + + // DFS from the signer over incoming edges, collecting every address that reaches the signer. + const visited = new Set([signerLc]); + const stack = [signerLc]; + while (stack.length > 0) { + const node = stack.pop() as string; + const sources = incoming.get(node); + if (!sources) continue; + for (const src of sources) { + if (!visited.has(src)) { + visited.add(src); + stack.push(src); + } + } + } + + visited.delete(signerLc); // the signer is msg.sender, never a helper + + // Union optional static seed helpers (lowercased, signer filtered out, empty/undefined tolerated). + if (seedHelpers) { + for (const seed of seedHelpers) { + if (!seed) continue; + const seedLc = seed.toLowerCase(); + if (seedLc !== signerLc) visited.add(seedLc); + } + } + + return [...visited].sort((a, b) => (BigInt(a) < BigInt(b) ? -1 : 1)); +} + +/** + * Classifies a failed denyMinter/votesDelegated error into permanent vs transient with a diagnosis. + * + * Permanent (TooLate): the application window has closed — retrying forever is useless and would only + * burn gas + page. Transient: under-quorum, helper-list rejection, RPC blips, unknown — may recover + * next cycle (or after operator action) within the attempt cap. + */ +export function classifyDenyError(error: unknown, iface: ethers.Interface): DenyErrorClass { + const err = error as any; + const message = typeof err?.message === 'string' && err.message ? err.message : String(error); + + // Extract candidate revert data from the common ethers v6 / provider nesting shapes. + const dataCandidates = [err?.data, err?.info?.error?.data, err?.error?.data]; + let data: string | undefined; + for (const candidate of dataCandidates) { + if (typeof candidate === 'string' && candidate.startsWith('0x')) { + data = candidate; + break; + } + } + + // Empty-data on-chain revert diagnosis (shared by steps 1–2 below). + // Equity.votesDelegated uses BARE requires that revert with NO data: + // require(_checkDuplicatesAndSorted(helpers)) + // require(current != sender) + // require(_canVoteFor(sender, current)) + // So this is NOT an RPC fault: the helper list was rejected on-chain. + const emptyRevert: DenyErrorClass = { + kind: 'transient', + label: 'EmptyRevert', + detail: + 'Helper list rejected on-chain with empty revert data — a helper is unsorted/duplicated, ' + + 'equals the signer, or does NOT delegate to the signer (this is NOT an RPC fault).', + }; + + // 1. A data candidate exists and is exactly empty hex ('0x' / '0X') -> real empty-data revert. + if (data === '0x' || data === '0X') { + return emptyRevert; + } + + // 2. No data candidate, but still recognisably an on-chain revert -> EmptyRevert. + // ethers v6 surfaces a data-less CALL_EXCEPTION / "missing revert data" this way, which is what + // distinguishes a real empty-data revert from a client/transport failure (no data, no revert marker). + if (data === undefined) { + const code = err?.code; + const isOnChainEmptyRevert = message.toLowerCase().includes('missing revert data') || code === 'CALL_EXCEPTION'; + if (isOnChainEmptyRevert) { + return emptyRevert; + } + + // 3. No data candidate and no revert marker -> client/transport/account failure, not a helper-list + // rejection. This class exists so we do NOT attribute network/nonce/timeout faults to the helper + // list (which would send the operator after GUARD_HELPER_ADDRESS and trip the pre-check seed-drop). + const codeSuffix = typeof code === 'string' && code.length > 0 ? ` code=${code}` : ''; + return { + kind: 'transient', + label: 'NoRevertData', + detail: + 'Not a contract rejection: client/transport/account-level failure ' + + '(nonce, funds at send time, timeout, network, or user rejection). ' + + `Raw error: ${message}${codeSuffix}`, + }; + } + + // 4. Decodable custom error (TooLate on JuiceDollar, NotQualified on Equity, …). + // Reached only when a non-empty data candidate exists — TooLate/NotQualified can never be + // swallowed by steps 1–3 above. + try { + const parsed = iface.parseError(data); + if (parsed) { + const name = parsed.name; + if (name === 'TooLate') { + return { + kind: 'permanent', + label: 'TooLate', + detail: + 'The application period has expired; denyMinter is impossible for anyone now — ' + + 'the minter will pass unless it is a bridge that can be handled otherwise.', + }; + } + if (name === 'NotQualified') { + return { + kind: 'transient', + label: 'NotQualified', + detail: + 'The signer is under the 2% Equity quorum; delegation can fix this at runtime ' + + '(delegateVoteTo the guard signer, or fund the signer with JUICE).', + }; + } + const args = parsed.args?.length ? ` args=${parsed.args.map((a) => String(a)).join(',')}` : ''; + return { + kind: 'transient', + label: name, + detail: `Decoded on-chain error ${name}${args}`, + }; + } + } catch { + // Not a decodable custom error — fall through to Unknown. + } + + return { + kind: 'transient', + label: 'Unknown', + detail: message, + }; +} diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index 90f3914..df66521 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -1,22 +1,64 @@ import { Injectable, Logger } from '@nestjs/common'; import { ethers } from 'ethers'; import * as fs from 'fs'; -import { JuiceDollarABI, ADDRESS } from '@juicedollar/jusd'; +import { JuiceDollarABI, EquityABI, ADDRESS } from '@juicedollar/jusd'; import { AppConfigService } from '../config/config.service'; import { ProviderService } from './provider.service'; import { MinterRepository } from './prisma/repositories/minter.repository'; +import { EventsRepository } from './prisma/repositories/events.repository'; import { TelegramService } from './telegram.service'; import { MinterStatus } from './types'; +import { computeHelpers, classifyDenyError, QUORUM_BPS } from './minter-guard.logic'; +import { GuardResponse } from '../../shared/types'; + +// Cap the confirmation wait so a stuck/underpriced deny tx cannot wedge the monitoring cycle: an +// unbounded tx.wait() would block processBlocks, leaving isRunning=true so no later cycle (and none of +// the sibling alert watchers) ever runs, and — because nothing throws — no stuck-alert fires. Must be +// shorter than the EVERY_5_MINUTES cron. On timeout the throw is caught, the minter is NOT marked done, +// and it retries next cycle within the attempt cap. +const DENY_CONFIRM_TIMEOUT_MS = 180_000; + +// Cooldown between repeated skip pages (in-memory only, reset on restart). Two independent timers so a +// votes-skip page and a gas-skip page never suppress each other. +const SKIP_ALERT_COOLDOWN_MS = 60 * 60 * 1000; + +// Rough denyMinter() gas ceiling used for the balance floor (pre-check) and the read-only /guard status +// display (gasEnough / estimated cost). Worst-case so an underfunded signer always hits the dedicated +// gas page rather than a silent estimateGas "insufficient funds" catch. +const DENY_GAS_ESTIMATE = 300_000n; + +// Safety buffer (seconds) for the just-in-time TooLate pre-check: a minter whose live block timestamp is +// already within this margin of its application deadline is skipped, since denyMinter would need to be +// mined before the deadline and one that lands this close is likely to revert TooLate and waste gas. +const DENY_TOOLATE_BUFFER_SECONDS = 60n; + +// Attempt cap per minter for this process lifetime, so a non-permanent failure (RPC blip, underpriced +// gas, transient EmptyRevert on a momentarily stale helper graph) cannot retry forever and page on every +// 5-minute cycle. Permanent failures (TooLate) and the cap both set done=true. +const MAX_DENY_ATTEMPTS = 3; interface Whitelist { minters: string[]; } /** - * Watches for newly proposed minters and automatically denies any that are not - * in the configured whitelist. The deny window is the application period - * specified in the suggestMinter call (typically days), so an hourly cadence is - * sufficient. A bricked or wrong-network signer is a startup error and exits. + * Raised for a guard CONFIG error that must fail loud and abort bootstrap (missing/invalid + * GUARD_PRIVATE_KEY) — as opposed to a recoverable init error (e.g. a bad whitelist file), which + * disables the guard and pages once without killing the whole monitoring process. + */ +export class GuardConfigError extends Error {} + +/** + * Watches for newly proposed minters and automatically denies any that are not in the configured + * whitelist. denyMinter is a shareholder veto gated on a 2% Equity vote quorum (Equity.checkQualified), + * inside a finite application window from suggestMinter — it is NOT an admin call. + * + * The watcher runs on the same EVERY_5_MINUTES cadence as the rest of monitoring (via monitoring.service). + * That is harmless: the per-minter attempt cap, the per-minter alert dedup, and the rate-limited skip + * pages stop the retry/alert amplification that an unbounded retry-on-every-cycle design would produce. + * The signer must be Equity-qualified (>=2% pool-share votes, own votes plus delegators) or denyMinter + * is skipped (not attempted). Helpers come from the indexed Delegation graph plus an optional static + * seed (GUARD_HELPER_ADDRESS). */ @Injectable() export class MinterGuardService { @@ -26,14 +68,26 @@ export class MinterGuardService { private signerKey?: string; private signerAddress?: string; private jusdAddress?: string; + private equityAddress?: string; private whitelist = new Set(); - private helperAddress?: string; - private alreadyDenied = new Set(); + // 0 or 1 entries from the optional GUARD_HELPER_ADDRESS seed: an explicitly named helper that does not + // depend on the indexer (fresh/reset database, in-progress backfill, indexing gap). + private helperSeed: string[] = []; + // Per-minter attempt/terminal state for this process lifetime. done=true means stop attempting + // (confirmed deny, permanent rejection, or attempt cap reached). alerted=true means the terminal + // FAILED page was already sent. + private readonly denyState = new Map(); + // In-memory skip-alert rate limiting (see SKIP_ALERT_COOLDOWN_MS). + private lastVotesSkipAlertAt = 0; + private lastGasSkipAlertAt = 0; + // Built once from JuiceDollar + Equity ABIs so both TooLate and NotQualified decode. + private denyErrorInterface?: ethers.Interface; constructor( private readonly config: AppConfigService, private readonly providerService: ProviderService, private readonly minterRepo: MinterRepository, + private readonly eventsRepo: EventsRepository, private readonly telegramService: TelegramService ) {} @@ -43,32 +97,117 @@ export class MinterGuardService { return; } + // Missing/invalid GUARD_PRIVATE_KEY is a hard CONFIG error: fail loud (GuardConfigError) so + // bootstrap aborts. No silent fallback — an enabled guard with an unusable key is never masked. const pk = this.config.guardPrivateKey; + if (!pk) throw new GuardConfigError('GUARD_ENABLED=true but GUARD_PRIVATE_KEY is missing'); + + // Store the key, not a cached signer: the wallet + contract are rebuilt fresh per deny from the + // live provider, so a provider recycle can't leave the guard on a wedged connection. + // `new ethers.Wallet(pk)` also validates the key here (a bricked key = config error). + let signerAddress: string; + try { + signerAddress = new ethers.Wallet(pk).address; + } catch (error) { + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + throw new GuardConfigError(`GUARD_PRIVATE_KEY is invalid: ${errorMsg}`); + } + this.signerKey = pk; + this.signerAddress = signerAddress; + + // GUARD_HELPER_ADDRESS is OPTIONAL: when set it seeds computeHelpers with an explicitly named helper, + // independent of the indexer. A typo is a config error (checksum/format), not a silent ignore. const helper = this.config.guardHelperAddress; - const whitelistFile = this.config.guardWhitelistFile; + if (helper) { + try { + this.helperSeed = [ethers.getAddress(helper)]; + } catch (error) { + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + throw new GuardConfigError(`GUARD_HELPER_ADDRESS is invalid: ${errorMsg}`); + } + } else { + this.logger.log('MinterGuard: GUARD_HELPER_ADDRESS unset — helper set comes from the Delegation graph alone'); + } - if (!pk) throw new Error('GUARD_ENABLED=true but GUARD_PRIVATE_KEY is missing'); - if (!helper) throw new Error('GUARD_ENABLED=true but GUARD_HELPER_ADDRESS is missing'); + // A bad/missing whitelist file is a RECOVERABLE init error (not a config-key error): plain Error, + // which the caller (monitoring.service) turns into disable+alert rather than an abort. + const whitelistFile = this.config.guardWhitelistFile; if (!whitelistFile) throw new Error('GUARD_ENABLED=true but GUARD_WHITELIST_FILE is missing'); - const jusdAddress = ADDRESS[this.config.blockchainId]?.juiceDollar; - if (!jusdAddress) throw new Error(`No JUSD address configured for chain ${this.config.blockchainId}`); + const chainId = this.config.blockchainId; + const jusdAddress = ADDRESS[chainId]?.juiceDollar; + if (!jusdAddress) throw new Error(`No JUSD address configured for chain ${chainId}`); + const equityAddress = ADDRESS[chainId]?.equity; + if (!equityAddress) throw new Error(`No Equity address configured for chain ${chainId}`); - // Store the key/addresses, not a cached signer: the wallet + contract are rebuilt fresh per - // denyMinter from the live provider, so a provider recycle (ProviderService) can't leave the - // guard bound to a wedged connection. `new ethers.Wallet(pk)` also validates the key here. - this.signerKey = pk; - this.signerAddress = new ethers.Wallet(pk).address; this.jusdAddress = jusdAddress; - this.helperAddress = ethers.getAddress(helper); + this.equityAddress = equityAddress; + // Interface covering both JuiceDollar.TooLate and Equity.NotQualified for classifyDenyError. + this.denyErrorInterface = new ethers.Interface([...JuiceDollarABI, ...EquityABI]); this.loadWhitelist(whitelistFile); this.enabled = true; this.logger.log( - `MinterGuard ENABLED: signer=${this.signerAddress}, helper=${this.helperAddress}, ` + - `whitelist=${this.whitelist.size} entries, jusd=${jusdAddress}` + `MinterGuard ENABLED: signer=${this.signerAddress}, helperSeed=${JSON.stringify(this.helperSeed)}, ` + + `whitelist=${this.whitelist.size} entries, jusd=${jusdAddress}, equity=${equityAddress}` + ); + // Loud, deliberate startup warnings: denyMinter is a shareholder veto, not an admin call. + this.logger.warn( + 'MinterGuard signer must be Equity-qualified (>=2% of totalVotes, own votes plus delegators ' + + 'aggregated from Delegation events / GUARD_HELPER_ADDRESS) or denyMinter is skipped, not attempted.' + ); + this.logger.warn( + 'MinterGuard deny window is the application period from suggestMinter and is finite — once it ' + + 'closes, denyMinter reverts TooLate for everyone and the minter will pass unchallenged.' ); + + await this.probeQualification(); + } + + /** + * Startup preflight: read additive voting power (votes(signer)+Σvotes(helper)) and page once if the + * signer is under the 2% quorum. Deliberately does NOT abort bootstrap on not-qualified: taking the + * whole monitoring process down over a governance state that delegation can fix at runtime would be + * strictly worse than running loud-but-degraded; the state is also exposed continuously via GET /guard. + * A read/RPC failure is warn-only (no page, no throw) so a transient blip at boot cannot page or kill. + */ + private async probeQualification(): Promise { + const signerAddress = this.signerAddress; + const equityAddress = this.equityAddress; + if (!signerAddress || !equityAddress) return; + + try { + const equity = new ethers.Contract(equityAddress, EquityABI, this.providerService.multicallProvider); + const delegations = await this.eventsRepo.getDelegations(); + const helpers = computeHelpers(delegations, signerAddress, this.helperSeed); + const voteResults = await this.providerService.callBatch([ + () => equity.totalVotes(), + ...[signerAddress, ...helpers].map((a) => () => equity.votes(a)), + ]); + const totalVotes: bigint = BigInt(voteResults[0]); + const votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); + const bps = totalVotes > 0n ? (votingPower * 10000n) / totalVotes : 0n; + + if (votingPower * 10000n < QUORUM_BPS * totalVotes) { + this.logger.error( + `MinterGuard startup: signer ${signerAddress} under quorum ` + + `(${bps} bps < ${QUORUM_BPS} bps / 2%). denyMinter will be skipped until qualified.` + ); + await this.telegramService.sendCriticalAlert( + `⚠️ *Minter guard under 2% quorum at startup*\n\n` + + `Signer: \`${signerAddress}\`\n` + + `Voting power: ${bps} bps (needs >= ${QUORUM_BPS} bps / 2%)\n\n` + + `Remedy: delegateVoteTo(${signerAddress}) on Equity, or fund the signer with JUICE.` + ); + } else { + this.logger.log(`MinterGuard startup: signer ${signerAddress} qualified at ${bps} bps (>= ${QUORUM_BPS} bps)`); + } + } catch (error) { + // Transient RPC blip at boot must not page and must not throw (see method docstring). + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + this.logger.warn(`MinterGuard startup qualification probe failed (non-fatal): ${errorMsg}`); + } } private loadWhitelist(path: string): void { @@ -77,19 +216,30 @@ export class MinterGuardService { const parsed = JSON.parse(raw) as Whitelist; if (!Array.isArray(parsed.minters)) throw new Error('whitelist.minters must be an array'); this.whitelist = new Set(parsed.minters.map((a) => a.toLowerCase())); - this.logger.log(`Loaded whitelist with ${this.whitelist.size} entries from ${path}`); + // Empty whitelist = deny-by-default: EVERY new minter proposal will be denied. An unmounted + // or truncated file looks exactly like this — make the empty state visible instead of + // indistinguishable from a normal load. + if (this.whitelist.size === 0) { + this.logger.warn( + `Loaded EMPTY whitelist from ${path} — deny-by-default is active; every new minter proposal will be denied. ` + + `An unmounted or truncated file looks exactly like this.` + ); + } else { + this.logger.log(`Loaded whitelist with ${this.whitelist.size} entries from ${path}`); + } } catch (error) { throw new Error(`Failed to load whitelist from ${path}: ${error.message}`); } } /** - * Called by MonitoringService after syncMinters(). Iterates PROPOSED minters - * and denies any not on the whitelist that haven't been denied yet. + * Called by MonitoringService after syncMinters(). Iterates PROPOSED minters and denies any not on + * the whitelist that are not already terminal in denyState. Runs a signer-global votes/gas pre-check + * once per cycle before any send; permanent rejections and the attempt cap stop retry amplification. */ async checkAndDeny(): Promise { - const { signerKey, jusdAddress, helperAddress } = this; - if (!this.enabled || !signerKey || !jusdAddress || !helperAddress) return; + const { signerKey, signerAddress, jusdAddress } = this; + if (!this.enabled || !signerKey || !signerAddress || !jusdAddress || !this.denyErrorInterface) return; const minters = await this.minterRepo.findAll(); // Denies BRIDGE-typed proposals too: bridge type is inferred from a single @@ -99,7 +249,7 @@ export class MinterGuardService { (m) => m.status === MinterStatus.PROPOSED && !this.whitelist.has(m.address.toLowerCase()) && - !this.alreadyDenied.has(m.address.toLowerCase()) + !this.denyState.get(m.address.toLowerCase())?.done ); if (candidates.length === 0) return; @@ -109,34 +259,358 @@ export class MinterGuardService { // Build the signer + contract fresh from the live provider for this run, so a recycled // provider is picked up rather than a stale connection captured at initialize(). const wallet = new ethers.Wallet(signerKey, this.providerService.provider); + + // Signer-global pre-check (once per cycle, before any deny): verify quorum + gas and build helpers. + const precheck = await this.runDenyPrecheck(signerAddress, wallet, candidates); + if (!precheck.ok) return; + const helpers = precheck.helpers; + const juiceDollar = new ethers.Contract(jusdAddress, JuiceDollarABI, wallet); for (const minter of candidates) { const address = ethers.getAddress(minter.address); + const addrLc = address.toLowerCase(); + + // Just-in-time TooLate guard. denyMinter reverts TooLate once block.timestamp > + // minters[_minter] (applicationTimestamp + applicationPeriod). Sending into that margin only + // burns gas — mark done so we never retry a window that can never succeed again. + let latestBlock: ethers.Block | null = null; + try { + latestBlock = await this.providerService.provider.getBlock('latest'); + } catch (error) { + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + this.logger.error(`MinterGuard skip ${address}: failed to read latest block for TooLate pre-check: ${errorMsg}`); + continue; + } + if (!latestBlock) { + this.logger.error(`MinterGuard skip ${address}: provider.getBlock('latest') returned null for TooLate pre-check`); + continue; + } + const deadline = BigInt(minter.applicationTimestamp) + BigInt(minter.applicationPeriod); + if (BigInt(latestBlock.timestamp) + DENY_TOOLATE_BUFFER_SECONDS >= deadline) { + this.logger.warn( + `MinterGuard skip ${address}: deny window closed or about to close ` + + `(block ts ${latestBlock.timestamp} + ${DENY_TOOLATE_BUFFER_SECONDS}s buffer >= deadline ${deadline})` + ); + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + this.denyState.set(addrLc, { ...prev, done: true }); + // Alert ONCE that an unwhitelisted minter is passing unchallenged. + if (!prev.alerted) { + this.denyState.set(addrLc, { ...prev, done: true, alerted: true }); + await this.telegramService.sendCriticalAlert( + `⚠️ *Unwhitelisted minter passing unchallenged*\n\n` + + `Address: \`${address}\`\n` + + `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + + `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.` + ); + } + continue; + } + + const message = `Auto-deny by minter-guard: not in whitelist (${this.config.environment ?? 'unknown'}/${this.config.chain ?? 'unknown'})`; + let confirmed = false; + let txHash: string | undefined; try { - const message = `Auto-deny by minter-guard: not in whitelist (${this.config.environment ?? 'unknown'}/${this.config.chain ?? 'unknown'})`; - const tx = await juiceDollar.denyMinter(address, [helperAddress], message); + const tx = await juiceDollar.denyMinter(address, helpers, message); + txHash = tx.hash; this.logger.warn(`Submitted denyMinter for ${address}: tx=${tx.hash}`); - const receipt = await tx.wait(); - this.alreadyDenied.add(address.toLowerCase()); + // Bounded wait: on timeout this throws and the minter is left unmarked to retry next cycle. + // A retry sends a fresh-nonce tx (it does not replace a stuck one); under sustained mempool/gas + // pathology the deny may not land, but the terminal FAILED alert then pages a human — an accepted + // limitation of the opt-in guard, deliberately not carrying nonce/replacement state. + const receipt = await tx.wait(1, DENY_CONFIRM_TIMEOUT_MS); + confirmed = true; + // Confirmed on-chain from here — mark before alerting so a Telegram hiccup cannot cause a double deny. + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + this.denyState.set(addrLc, { attempts: prev.attempts, done: true }); this.logger.warn(`denyMinter confirmed for ${address}: block=${receipt.blockNumber}`); await this.telegramService.sendCriticalAlert( `🛡️ *Minter auto-denied*\n\n` + `Address: \`${address}\`\n` + - `Tx: \`${tx.hash}\`\n` + + `Tx: \`${txHash}\`\n` + `Block: ${receipt.blockNumber}\n` + `Message: ${message}` ); } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); - this.logger.error(`Failed to deny minter ${address}: ${errorMsg}`, error?.stack || error); - await this.telegramService.sendCriticalAlert( - `⚠️ *Minter auto-deny FAILED*\n\n` + - `Address: \`${address}\`\n` + - `Error: ${errorMsg}\n\n` + - `Manual denyMinter() required before application period expires.` + if (confirmed) { + // deny already landed on-chain; only post-confirmation bookkeeping/alert failed — NOT a deny failure. + this.logger.error( + `denyMinter confirmed but post-processing failed for ${address} (tx=${txHash}): ${errorMsg}`, + error?.stack || error + ); + } else { + const classification = classifyDenyError(error, this.denyErrorInterface); + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + const attempts = prev.attempts + 1; + const done = classification.kind === 'permanent' || attempts >= MAX_DENY_ATTEMPTS; + this.denyState.set(addrLc, { attempts, done, alerted: prev.alerted }); + + this.logger.error( + `Failed to deny minter ${address} (attempt ${attempts}/${MAX_DENY_ATTEMPTS}, ` + + `${classification.kind}/${classification.label}): ${classification.detail}`, + error?.stack || error + ); + + // FAILED critical alert ONLY on the terminal state for this minter, and only once. + // NotQualified must not produce a per-attempt page — the precheck owns that page. + // Non-terminal transient failures log at error level and stay silent on Telegram. + if (done && !prev.alerted) { + this.denyState.set(addrLc, { attempts, done: true, alerted: true }); + const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= deadline; + const remedy = windowClosed + ? 'The application period has ended — denyMinter is impossible; challenge/handle the minter otherwise if needed.' + : 'Manual denyMinter() required before the application period ends.'; + await this.telegramService.sendCriticalAlert( + `⚠️ *Minter auto-deny FAILED*\n\n` + + `Address: \`${address}\`\n` + + `Class: ${classification.label} (${classification.kind})\n` + + `Detail: ${classification.detail}\n` + + `Attempts: ${attempts}/${MAX_DENY_ATTEMPTS}\n\n` + + remedy + ); + } + } + } + } + } + + /** + * Signer-global deny pre-check, run once per cycle before any denyMinter(). Returns { ok, helpers }: + * - ok=false => SKIP all denies this cycle (under quorum, out of gas, or a transient read error). + * - Any thrown chain error is caught here and turned into a skip — nothing escapes to the cycle and + * no doomed reverting tx is ever sent. Votes/gas skips page a human via rate-limited critical alerts. + */ + private async runDenyPrecheck( + signerAddress: string, + wallet: ethers.Wallet, + candidates: Array<{ address: string }> + ): Promise<{ ok: boolean; helpers: string[] }> { + try { + const provider = this.providerService.provider; + const chainId = this.config.blockchainId; + const equity = new ethers.Contract(ADDRESS[chainId].equity, EquityABI, provider); + const denyErrorInterface = this.denyErrorInterface as ethers.Interface; + + // Dynamic helper set from the indexed Delegation graph (+ optional static seed), sorted ascending. + const delegations = await this.eventsRepo.getDelegations(); + let helpers = computeHelpers(delegations, signerAddress, this.helperSeed); + + // Votes check FIRST: votesDelegated is the exact value denyMinter/checkQualified use on-chain, so it + // both validates the helper list and measures qualification. estimateGas below would itself revert + // on NotQualified, so checking votes first keeps the two skip causes distinct. + // + // SEED-DROP RETRY: a stale GUARD_HELPER_ADDRESS (does not delegate to the signer / equals the + // signer) poisons every votesDelegated read with an empty-data revert that reads like an RPC + // fault. If EmptyRevert fires and we have a seed, retry once seed-less so a config typo cannot + // silently disable the guard forever. + let totalVotes: bigint; + let delegatedVotes: bigint; + try { + totalVotes = BigInt(await equity.totalVotes()); + delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, helpers)); + } catch (votesError) { + const classification = classifyDenyError(votesError, denyErrorInterface); + if (classification.label === 'EmptyRevert' && this.helperSeed.length > 0) { + const seedLess = computeHelpers(delegations, signerAddress); + try { + totalVotes = BigInt(await equity.totalVotes()); + delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, seedLess)); + helpers = seedLess; + this.logger.error( + `MinterGuard: GUARD_HELPER_ADDRESS ${this.helperSeed[0]} rejected by votesDelegated ` + + `(EmptyRevert — does not delegate to the signer, or equals the signer). ` + + `Continuing this cycle with the seed-less helper set; fix the env value.` + ); + await this.maybeAlertSkip( + 'votes', + `⚠️ *Minter guard GUARD_HELPER_ADDRESS rejected*\n\n` + + `Seed: \`${this.helperSeed[0]}\`\n` + + `Signer: \`${signerAddress}\`\n\n` + + `votesDelegated reverted with empty data on the seed helper (it does not delegate to ` + + `the signer, or equals the signer). Cycle continues with the Delegation-graph helpers only. ` + + `Fix GUARD_HELPER_ADDRESS.` + ); + } catch { + // Retry also failed — rethrow the original so the outer catch skips the cycle. + throw votesError; + } + } else { + throw votesError; + } + } + + if (delegatedVotes * 10000n < QUORUM_BPS * totalVotes) { + const bps = totalVotes > 0n ? (delegatedVotes * 10000n) / totalVotes : 0n; + this.logger.warn( + `MinterGuard SKIP: signer ${signerAddress} under quorum ` + + `(${bps} bps < ${QUORUM_BPS} bps), ${candidates.length} candidate(s) not denied` + ); + await this.maybeAlertSkip( + 'votes', + `⚠️ *Minter guard under 2% quorum — deny skipped*\n\n` + + `Signer: \`${signerAddress}\`\n` + + `Voting power: ${bps} bps (needs >= ${QUORUM_BPS} bps / 2%)\n` + + `${candidates.length} unwhitelisted PROPOSED minter(s) left undenied.\n\n` + + `Delegate JUICE votes to the signer: delegateVoteTo(${signerAddress}).` ); + return { ok: false, helpers }; } + + const feeData = await provider.getFeeData(); + const gasPrice = feeData.maxFeePerGas ?? feeData.gasPrice; + if (gasPrice === null || gasPrice === undefined) throw new Error('feeData has neither maxFeePerGas nor gasPrice'); + const balance: bigint = await provider.getBalance(signerAddress); + + // Worst-case gas floor FIRST, independent of estimateGas: some nodes verify balance inside + // eth_estimateGas, so an underfunded signer would revert there ("insufficient funds") and fall + // into the generic catch below as a SILENT skip instead of this dedicated gas page. Checking the + // balance against a fixed worst-case ceiling (DENY_GAS_ESTIMATE * fee) up front guarantees a gas + // shortfall ALWAYS pages, before estimateGas is ever attempted. Native unit on Citrea is cBTC + // (18 decimals — ethers.formatEther is still correct). + const worstCaseCost = DENY_GAS_ESTIMATE * gasPrice; + if (balance < worstCaseCost) { + this.logger.warn( + `MinterGuard SKIP: signer ${signerAddress} low on gas ` + + `(balance ${ethers.formatEther(balance)} cBTC < worst-case deny cost ${ethers.formatEther(worstCaseCost)} cBTC)` + ); + await this.maybeAlertSkip( + 'gas', + `⚠️ *Minter guard low on cBTC — deny skipped*\n\n` + + `Signer: \`${signerAddress}\`\n` + + `Balance: ${ethers.formatEther(balance)} cBTC\n` + + `Worst-case deny cost: ${ethers.formatEther(worstCaseCost)} cBTC\n` + + `${candidates.length} unwhitelisted PROPOSED minter(s) left undenied.\n\n` + + `Fund the signer with cBTC.` + ); + return { ok: false, helpers }; + } + + // Precise estimate against a representative denyMinter() (cost is minter-independent to first + // order), only after the balance floor above rules out an "insufficient funds" revert and after + // the votes check rules out a NotQualified revert. This is BEST-EFFORT on top of the worst-case + // floor: a sample-specific revert must NOT drop every candidate this cycle (e.g. sample just + // crossed its own deadline). The worst-case floor already guarantees gas, and the per-candidate + // TooLate guard + try/catch handle each send — so on estimate failure we simply proceed. + try { + const jusd = new ethers.Contract(ADDRESS[chainId].juiceDollar, JuiceDollarABI, wallet); + const gasEstimate: bigint = BigInt( + await jusd.denyMinter.estimateGas(candidates[0].address, helpers, 'minter-guard gas estimate') + ); + const estimatedCost = gasEstimate * gasPrice; + if (balance < estimatedCost) { + this.logger.warn( + `MinterGuard SKIP: signer ${signerAddress} low on gas ` + + `(balance ${ethers.formatEther(balance)} cBTC < est. deny cost ${ethers.formatEther(estimatedCost)} cBTC)` + ); + await this.maybeAlertSkip( + 'gas', + `⚠️ *Minter guard low on cBTC — deny skipped*\n\n` + + `Signer: \`${signerAddress}\`\n` + + `Balance: ${ethers.formatEther(balance)} cBTC\n` + + `Est. deny cost: ${ethers.formatEther(estimatedCost)} cBTC\n` + + `${candidates.length} unwhitelisted PROPOSED minter(s) left undenied.\n\n` + + `Fund the signer with cBTC.` + ); + return { ok: false, helpers }; + } + } catch (estimateError) { + const em = + typeof estimateError?.message === 'string' && estimateError.message ? estimateError.message : String(estimateError); + this.logger.warn( + `MinterGuard: sample denyMinter gas estimate on ${candidates[0].address} reverted (${em}); ` + + `proceeding on the worst-case gas floor — the per-candidate TooLate guard and try/catch handle each send.` + ); + } + + return { ok: true, helpers }; + } catch (error) { + // Transient RPC error / votesDelegated revert on a momentarily stale helper graph: skip this + // cycle (logged, not silently swallowed). It retries next cycle. runWatcher also isolates this, + // but the explicit catch guarantees no doomed tx is sent this cycle. + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + this.logger.error(`MinterGuard pre-check failed, skipping deny this cycle: ${errorMsg}`, error?.stack || error); + return { ok: false, helpers: [] }; } } + + /** Rate-limited skip page: at most one per kind per SKIP_ALERT_COOLDOWN_MS (in-memory, reset on restart). */ + private async maybeAlertSkip(kind: 'votes' | 'gas', message: string): Promise { + const nowMs = Date.now(); + const lastAt = kind === 'votes' ? this.lastVotesSkipAlertAt : this.lastGasSkipAlertAt; + if (nowMs - lastAt < SKIP_ALERT_COOLDOWN_MS) return; + if (kind === 'votes') this.lastVotesSkipAlertAt = nowMs; + else this.lastGasSkipAlertAt = nowMs; + await this.telegramService.sendCriticalAlert(message); + } + + /** + * Read-only status for the GET /guard endpoint. Fail-LOUD: a genuine on-chain read error throws (5xx) + * rather than faking 0%/false — the skip+alert graceful path lives only in the deny flow, never here. + * The private key never leaves the backend; only the derived signer address is exposed. + */ + async getStatus(): Promise { + const chainId = this.config.blockchainId; + const equityAddress = ADDRESS[chainId].equity; + + if (!this.enabled || !this.signerAddress) { + return { + enabled: false, + signerAddress: ethers.ZeroAddress, + votingPowerPct: '0', + quorumPct: Number(QUORUM_BPS) / 100, + qualified: false, + helperCount: 0, + gasBalance: '0', + estimatedDenyCost: '0', + gasEnough: false, + equityAddress, + chainId, + }; + } + + const signerAddress = this.signerAddress; + const provider = this.providerService.provider; + const equity = new ethers.Contract(equityAddress, EquityABI, this.providerService.multicallProvider); + + // Additive, revert-proof voting power for DISPLAY: votes(signer) + Σ votes(helper) equals + // votesDelegated for a valid helper set, but plain votes() never reverts on a momentarily stale graph. + const delegations = await this.eventsRepo.getDelegations(); + const helpers = computeHelpers(delegations, signerAddress, this.helperSeed); + const voteResults = await this.providerService.callBatch([ + () => equity.totalVotes(), + ...[signerAddress, ...helpers].map((a) => () => equity.votes(a)), + ]); + const totalVotes: bigint = BigInt(voteResults[0]); + const votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); + const qualified = votingPower * 10000n >= QUORUM_BPS * totalVotes; + + // Gas status: model denyMinter() cost with a fixed gas ceiling * live fee (see DENY_GAS_ESTIMATE). + const balance: bigint = await provider.getBalance(signerAddress); + const feeData = await provider.getFeeData(); + const gasPrice = feeData.maxFeePerGas ?? feeData.gasPrice; + if (gasPrice === null || gasPrice === undefined) throw new Error('feeData has neither maxFeePerGas nor gasPrice'); + const estimatedDenyCost = DENY_GAS_ESTIMATE * gasPrice; + + return { + enabled: true, + signerAddress, + votingPowerPct: formatVotingPowerPct(votingPower, totalVotes), + quorumPct: Number(QUORUM_BPS) / 100, + qualified, + helperCount: helpers.length, + gasBalance: ethers.formatEther(balance), + estimatedDenyCost: ethers.formatEther(estimatedDenyCost), + gasEnough: balance >= estimatedDenyCost, + equityAddress, + chainId, + }; + } +} + +/** votingPower/totalVotes as a percent string with up to 4-decimal precision (safe when total is 0). */ +function formatVotingPowerPct(part: bigint, total: bigint): string { + if (total <= 0n) return '0'; + const ppm = (part * 1_000_000n) / total; // integer parts-per-million + return (Number(ppm) / 10_000).toString(); // -> percent } diff --git a/src/monitoringV2/monitoring.module.ts b/src/monitoringV2/monitoring.module.ts index 8767497..b3b088d 100644 --- a/src/monitoringV2/monitoring.module.ts +++ b/src/monitoringV2/monitoring.module.ts @@ -53,6 +53,6 @@ import { ApiModule } from './api/api.module'; TelegramService, MonitoringService, ], - exports: [MonitoringService, ContractService, EventService, ApiModule], + exports: [MonitoringService, ContractService, EventService, MinterGuardService, ApiModule], }) export class MonitoringV2Module {} diff --git a/src/monitoringV2/monitoring.service.ts b/src/monitoringV2/monitoring.service.ts index 943f55b..f0fee19 100644 --- a/src/monitoringV2/monitoring.service.ts +++ b/src/monitoringV2/monitoring.service.ts @@ -10,7 +10,7 @@ import { PositionService } from './position.service'; import { ChallengeService } from './challenge.service'; import { CollateralService } from './collateral.service'; import { MinterService } from './minter.service'; -import { MinterGuardService } from './minter-guard.service'; +import { MinterGuardService, GuardConfigError } from './minter-guard.service'; import { JusdService } from './jusd.service'; import { TelegramService } from './telegram.service'; @@ -44,7 +44,22 @@ export class MonitoringService implements OnModuleInit { await this.positionService.initialize(); await this.challengeService.initialize(); await this.minterService.initialize(); - await this.minterGuardService.initialize(); + // Never let a guard init failure abort the whole monitoring process. A CONFIG error + // (missing/invalid GUARD_PRIVATE_KEY) still fails loud and aborts bootstrap — no silent + // fallback. Any other init failure (e.g. a bad whitelist file) leaves the guard disabled + // and pages once, while the rest of monitoring keeps running. + try { + await this.minterGuardService.initialize(); + } catch (error) { + if (error instanceof GuardConfigError) throw error; + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + this.logger.error(`MinterGuard init failed — guard DISABLED, monitoring continues: ${errorMsg}`, error?.stack || error); + await this.telegramService.sendCriticalAlert( + `⚠️ *Minter guard init failed — guard DISABLED*\n\n` + + `The auto-deny guard is OFF for this run; monitoring continues.\n` + + `Error: ${errorMsg}` + ); + } await this.jusdService.initialize(); setTimeout(() => this.runMonitoring(), 5000); } diff --git a/src/monitoringV2/prisma/repositories/events.repository.ts b/src/monitoringV2/prisma/repositories/events.repository.ts index 7da8f79..36b6255 100644 --- a/src/monitoringV2/prisma/repositories/events.repository.ts +++ b/src/monitoringV2/prisma/repositories/events.repository.ts @@ -106,6 +106,41 @@ export class EventsRepository { } } + /** + * All Equity Delegation(from, to) events, ordered ascending by (blockNumber, logIndex) so a later + * event overwrites an earlier one when folded to the latest delegate per `from`. Both addresses are + * lowercased defensively. Feeds the pure computeHelpers() that builds the minter-guard's dynamic + * helper set. + */ + async getDelegations(): Promise> { + try { + const events = await this.prisma.rawEvent.findMany({ + where: { topic: 'Delegation' }, + select: { args: true }, + orderBy: [{ blockNumber: 'asc' }, { logIndex: 'asc' }], + }); + + return events + .map((e) => { + const data = e.args as any; + return { from: data?.from?.toLowerCase(), to: data?.to?.toLowerCase() }; + }) + .filter((d) => { + // A Delegation row missing from/to is a data anomaly (malformed args at ingest). Drop it — + // an incomplete edge cannot be folded into the graph — but warn so the anomaly is visible + // instead of silently swallowed. + if (!d.from || !d.to) { + this.logger.warn(`Discarding Delegation event with missing from/to (from=${d.from}, to=${d.to})`); + return false; + } + return true; + }); + } catch (error) { + this.logger.error(`Failed to get delegations from Delegation events: ${error.message}`); + throw error; + } + } + async getDeniedMinters(): Promise { try { const events = await this.prisma.rawEvent.findMany({ From 27cd5063c64bb4cf7cbc1328e1c00046591ab093 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 02:02:35 +0200 Subject: [PATCH 02/11] feat(frontend): add GUARD DELEGATION section with delegate action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard gave no way to tell whether the minter guard is armed. It now renders the /guard status — signer, live voting power, the Qualified (>= 2%) verdict, helper count and gas readiness — and, when a live signer exists, offers a wallet-backed delegateVoteTo(signer) call so JUICE holders can lend the guard their votes. Delegation is non-custodial and additive: the delegator keeps their JUICE and their own voting power, and the guard may only count the votes toward the quorum in addition. The wallet stack mounts lazily inside that section only. VITE_RPC_URL and VITE_WAGMI_ID are read fail-loud, but the failure is contained: without them the read-only panel still renders and the button shows an inline "wallet delegation unavailable" notice instead of white-screening the dashboard. Unset build secrets therefore do not break the build. Citrea mainnet has no chain definition in viem, so the chain is built locally from the chain id the backend reports, which keeps the frontend from drifting away from the network Equity actually lives on. @wagmi/core and @wagmi/connectors are pinned via overrides: @web3modal/wagmi declares them as peers without an upper bound, so a fresh install otherwise pulls @wagmi/core 3.x, which needs a newer TypeScript than this project pins and does not match the wagmi 2.x the modal was built against. The frontend image additionally needs python3/make/g++ in the build stage, because the walletconnect tree compiles ws' native addons. --- .github/workflows/frontend-dev.yaml | 8 +- .github/workflows/frontend-prd.yaml | 8 +- frontend/.env.example | 13 +- frontend/Dockerfile | 8 + frontend/package-lock.json | 10670 +++++++++++++++--- frontend/package.json | 10 +- frontend/src/App.tsx | 4 +- frontend/src/components/GuardDelegation.tsx | 154 + frontend/src/components/WalletProvider.tsx | 56 + frontend/src/lib/api.hook.ts | 16 +- frontend/src/lib/wagmi.ts | 83 + frontend/src/vite-env.d.ts | 2 + 12 files changed, 9530 insertions(+), 1502 deletions(-) create mode 100644 frontend/src/components/GuardDelegation.tsx create mode 100644 frontend/src/components/WalletProvider.tsx create mode 100644 frontend/src/lib/wagmi.ts diff --git a/.github/workflows/frontend-dev.yaml b/.github/workflows/frontend-dev.yaml index 4854337..4bd6e93 100644 --- a/.github/workflows/frontend-dev.yaml +++ b/.github/workflows/frontend-dev.yaml @@ -26,7 +26,9 @@ jobs: uses: actions/checkout@v4 - name: Build Docker image - run: docker build --build-arg VITE_DEPLOYMENT_ENV=dev -f frontend/Dockerfile -t ${{ env.DOCKER_TAGS }} . + # Unset secrets are not a build failure: wallet config is read lazily inside the guard section, + # so the dashboard still renders and the delegate button shows "wallet delegation unavailable". + run: docker build --build-arg VITE_DEPLOYMENT_ENV=dev --build-arg VITE_RPC_URL=${{ secrets.VITE_RPC_URL }} --build-arg VITE_WAGMI_ID=${{ secrets.VITE_WAGMI_ID }} -f frontend/Dockerfile -t ${{ env.DOCKER_TAGS }} . deploy: name: Deploy Frontend to DEV @@ -54,8 +56,12 @@ jobs: push: true tags: ${{ env.DOCKER_TAGS }} platforms: linux/arm64 + # Unset secrets are not a build failure: wallet config is read lazily inside the guard section, + # so the dashboard still renders and the delegate button shows "wallet delegation unavailable". build-args: | VITE_DEPLOYMENT_ENV=dev + VITE_RPC_URL=${{ secrets.VITE_RPC_URL }} + VITE_WAGMI_ID=${{ secrets.VITE_WAGMI_ID }} - name: Install cloudflared run: | diff --git a/.github/workflows/frontend-prd.yaml b/.github/workflows/frontend-prd.yaml index 8327de0..9eaf5a0 100644 --- a/.github/workflows/frontend-prd.yaml +++ b/.github/workflows/frontend-prd.yaml @@ -26,7 +26,9 @@ jobs: uses: actions/checkout@v4 - name: Build Docker image - run: docker build --build-arg VITE_DEPLOYMENT_ENV=prd -f frontend/Dockerfile -t ${{ env.DOCKER_TAGS }} . + # Unset secrets are not a build failure: wallet config is read lazily inside the guard section, + # so the dashboard still renders and the delegate button shows "wallet delegation unavailable". + run: docker build --build-arg VITE_DEPLOYMENT_ENV=prd --build-arg VITE_RPC_URL=${{ secrets.VITE_RPC_URL }} --build-arg VITE_WAGMI_ID=${{ secrets.VITE_WAGMI_ID }} -f frontend/Dockerfile -t ${{ env.DOCKER_TAGS }} . deploy: name: Deploy Frontend to PRD @@ -54,8 +56,12 @@ jobs: push: true tags: ${{ env.DOCKER_TAGS }} platforms: linux/arm64 + # Unset secrets are not a build failure: wallet config is read lazily inside the guard section, + # so the dashboard still renders and the delegate button shows "wallet delegation unavailable". build-args: | VITE_DEPLOYMENT_ENV=prd + VITE_RPC_URL=${{ secrets.VITE_RPC_URL }} + VITE_WAGMI_ID=${{ secrets.VITE_WAGMI_ID }} - name: Install cloudflared run: | diff --git a/frontend/.env.example b/frontend/.env.example index 71a48fc..5c02ea5 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1,3 +1,14 @@ VITE_API_BASE_URL=http://localhost:3001 # or for remote backend -# VITE_API_BASE_URL=https://dev.monitoring.juicedollar.com/api \ No newline at end of file +# VITE_API_BASE_URL=https://dev.monitoring.juicedollar.com/api + +# Wallet / Guard Delegation section (required for the Delegate button). Read fail-loud in +# src/lib/wagmi.ts — a missing value throws inside the lazy WalletProvider boundary (no silent +# fallback), so the rest of the read-only dashboard keeps running. Only the Delegate button uses +# these; the read path (signer / voting-power % / gas / helpers) comes from the backend /guard +# endpoint and works without them. + +# Citrea RPC URL for the wagmi http() transport (browser-visible — use a public/rate-limited endpoint). +VITE_RPC_URL=https://your-citrea-rpc-provider.com +# WalletConnect / Web3Modal project id (reuse the dapp's id). +VITE_WAGMI_ID=your-walletconnect-project-id diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 1ebabdb..d599bbd 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,5 +1,9 @@ FROM node:lts-alpine AS build WORKDIR /app/frontend +# The walletconnect tree (wallet delegation in the guard section) pulls ws' native addons +# bufferutil / utf-8-validate; node-gyp needs python3/make/g++ to build them on alpine, or npm ci +# fails. Build stage only — none of this reaches the nginx image below. +RUN apk add --no-cache python3 make g++ COPY frontend/package*.json ./ RUN npm ci COPY frontend/ . @@ -8,6 +12,10 @@ ARG VITE_API_BASE_URL=/api ENV VITE_API_BASE_URL=$VITE_API_BASE_URL ARG VITE_DEPLOYMENT_ENV ENV VITE_DEPLOYMENT_ENV=$VITE_DEPLOYMENT_ENV +ARG VITE_RPC_URL +ENV VITE_RPC_URL=$VITE_RPC_URL +ARG VITE_WAGMI_ID +ENV VITE_WAGMI_ID=$VITE_WAGMI_ID RUN npm run build FROM nginx:alpine diff --git a/frontend/package-lock.json b/frontend/package-lock.json index eef0d3f..b73802a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,9 +9,13 @@ "version": "0.0.0", "dependencies": { "@tailwindcss/vite": "^4.1.11", + "@tanstack/react-query": "^5.101.2", + "@web3modal/wagmi": "4.2.3", "react": "^19.1.0", "react-dom": "^19.1.0", - "tailwindcss": "^4.1.11" + "tailwindcss": "^4.1.11", + "viem": "^2.54.3", + "wagmi": "2.19.5" }, "devDependencies": { "@eslint/js": "^9.30.1", @@ -27,25 +31,20 @@ "vite": "^7.0.4" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -54,30 +53,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -93,13 +94,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -109,13 +111,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -125,36 +128,39 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -164,61 +170,67 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -228,12 +240,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -243,12 +256,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -257,32 +271,43 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -290,25 +315,114 @@ } }, "node_modules/@babel/types": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", - "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@base-org/account": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@base-org/account/-/account-2.4.0.tgz", + "integrity": "sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==", + "license": "Apache-2.0", + "dependencies": { + "@coinbase/cdp-sdk": "^1.0.0", + "@noble/hashes": "1.4.0", + "clsx": "1.2.1", + "eventemitter3": "5.0.1", + "idb-keyval": "6.2.1", + "ox": "0.6.9", + "preact": "10.24.2", + "viem": "^2.31.7", + "zustand": "5.0.3" + } + }, + "node_modules/@coinbase/cdp-sdk": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/@coinbase/cdp-sdk/-/cdp-sdk-1.54.0.tgz", + "integrity": "sha512-FfIJVEKAXgmr+dkXn/NBQO04/pps+oIgqEIdcmG67WZh+m07OsVnvSBEqEvbFO+VjSdaQAKu69EpO3NdHJd2Iw==", + "license": "MIT", + "dependencies": { + "@solana-program/system": "^0.10.0", + "@solana-program/token": "^0.9.0", + "@solana/kit": "^5.5.1", + "abitype": "1.0.6", + "axios": "1.16.0", + "axios-retry": "^4.5.0", + "bs58": "^6.0.0", + "jose": "^6.2.0", + "md5": "^2.3.0", + "uncrypto": "^0.1.3", + "viem": "^2.47.0", + "zod": "^3.25.76" + }, + "peerDependencies": { + "@x402/core": "^2.19.0", + "@x402/evm": "^2.19.0", + "@x402/extensions": "^2.19.0", + "@x402/svm": "^2.19.0" + }, + "peerDependenciesMeta": { + "@x402/core": { + "optional": true + }, + "@x402/evm": { + "optional": true + }, + "@x402/extensions": { + "optional": true + }, + "@x402/svm": { + "optional": true + } + } + }, + "node_modules/@coinbase/wallet-sdk": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@coinbase/wallet-sdk/-/wallet-sdk-4.3.6.tgz", + "integrity": "sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "1.4.0", + "clsx": "1.2.1", + "eventemitter3": "5.0.1", + "idb-keyval": "6.2.1", + "ox": "0.6.9", + "preact": "10.24.2", + "viem": "^2.27.2", + "zustand": "5.0.3" + } + }, + "node_modules/@ecies/ciphers": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.6.tgz", + "integrity": "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==", + "license": "MIT", + "engines": { + "bun": ">=1", + "deno": ">=2.7.10", + "node": ">=16" + }, + "peerDependencies": { + "@noble/ciphers": "^1.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "aix" @@ -318,12 +432,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -333,12 +448,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -348,12 +464,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "android" @@ -363,12 +480,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -378,12 +496,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -393,12 +512,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -408,12 +528,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -423,12 +544,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -438,12 +560,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -453,12 +576,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -468,12 +592,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -483,12 +608,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -498,12 +624,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -513,12 +640,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -528,12 +656,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -543,12 +672,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -558,12 +688,13 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -573,12 +704,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -588,12 +720,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -603,12 +736,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -618,12 +752,13 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "openharmony" @@ -633,12 +768,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "sunos" @@ -648,12 +784,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -663,12 +800,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -678,12 +816,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -693,10 +832,11 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -715,6 +855,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -723,42 +864,49 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -767,19 +915,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -794,6 +943,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -802,10 +952,11 @@ } }, "node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -814,60 +965,129 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz", - "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@ethereumjs/common": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz", + "integrity": "sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==", + "license": "MIT", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", + "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/common": "^3.2.0", + "@ethereumjs/rlp": "^4.0.1", + "@ethereumjs/util": "^8.1.0", + "ethereum-cryptography": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@gemini-wallet/core": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@gemini-wallet/core/-/core-0.3.2.tgz", + "integrity": "sha512-Z4aHi3ECFf5oWYWM3F1rW83GJfB9OvhBYPTmb5q+VyK3uvzvS48lwo+jwh2eOoCRWEuT/crpb9Vwp2QaS5JqgQ==", + "license": "MIT", + "dependencies": { + "@metamask/rpc-errors": "7.0.2", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "viem": ">=2.0.0" + } + }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -875,6 +1095,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -888,6 +1109,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -896,23 +1118,23 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, @@ -920,904 +1142,5621 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, + "node_modules/@metamask/eth-json-rpc-provider": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-json-rpc-provider/-/eth-json-rpc-provider-1.0.1.tgz", + "integrity": "sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@metamask/json-rpc-engine": "^7.0.0", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^5.0.1" }, "engines": { - "node": ">= 8" + "node": ">=14.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/json-rpc-engine": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.3.tgz", + "integrity": "sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==", + "license": "ISC", + "dependencies": { + "@metamask/rpc-errors": "^6.2.1", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.3.0" + }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/json-rpc-engine/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.19", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", - "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", - "dev": true - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ] + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/rpc-errors": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", + "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^9.0.0", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ] + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ] + "node_modules/@metamask/eth-json-rpc-provider/node_modules/@metamask/utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-5.0.2.tgz", + "integrity": "sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.1.2", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "semver": "^7.3.8", + "superstruct": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ] + "node_modules/@metamask/eth-json-rpc-provider/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", - "cpu": [ - "arm64" + "node_modules/@metamask/eth-json-rpc-provider/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "optional": true, - "os": [ - "freebsd" - ] + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ] + "node_modules/@metamask/json-rpc-engine": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-8.0.2.tgz", + "integrity": "sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA==", + "license": "ISC", + "dependencies": { + "@metamask/rpc-errors": "^6.2.1", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.3.0" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/json-rpc-engine/node_modules/@metamask/rpc-errors": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", + "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^9.0.0", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/json-rpc-engine/node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/json-rpc-engine/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/json-rpc-engine/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", - "cpu": [ - "loong64" + "node_modules/@metamask/json-rpc-engine/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/json-rpc-middleware-stream": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@metamask/json-rpc-middleware-stream/-/json-rpc-middleware-stream-7.0.2.tgz", + "integrity": "sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg==", + "license": "ISC", + "dependencies": { + "@metamask/json-rpc-engine": "^8.0.2", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.3.0", + "readable-stream": "^3.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/json-rpc-middleware-stream/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/json-rpc-middleware-stream/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", - "cpu": [ - "s390x" + "node_modules/@metamask/json-rpc-middleware-stream/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/object-multiplex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@metamask/object-multiplex/-/object-multiplex-2.1.0.tgz", + "integrity": "sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==", + "license": "ISC", + "dependencies": { + "once": "^1.4.0", + "readable-stream": "^3.6.2" + }, + "engines": { + "node": "^16.20 || ^18.16 || >=20" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] + "node_modules/@metamask/onboarding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@metamask/onboarding/-/onboarding-1.0.1.tgz", + "integrity": "sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==", + "license": "MIT", + "dependencies": { + "bowser": "^2.9.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ] + "node_modules/@metamask/providers": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@metamask/providers/-/providers-16.1.0.tgz", + "integrity": "sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g==", + "license": "MIT", + "dependencies": { + "@metamask/json-rpc-engine": "^8.0.1", + "@metamask/json-rpc-middleware-stream": "^7.0.1", + "@metamask/object-multiplex": "^2.0.0", + "@metamask/rpc-errors": "^6.2.1", + "@metamask/safe-event-emitter": "^3.1.1", + "@metamask/utils": "^8.3.0", + "detect-browser": "^5.2.0", + "extension-port-stream": "^3.0.0", + "fast-deep-equal": "^3.1.3", + "is-stream": "^2.0.0", + "readable-stream": "^3.6.2", + "webextension-polyfill": "^0.10.0" + }, + "engines": { + "node": "^18.18 || >=20" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ] + "node_modules/@metamask/providers/node_modules/@metamask/rpc-errors": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", + "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^9.0.0", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", - "cpu": [ - "x64" + "node_modules/@metamask/providers/node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/providers/node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/providers/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@metamask/providers/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "optional": true, - "os": [ - "win32" - ] + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.30.1", - "magic-string": "^0.30.17", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" + "node_modules/@metamask/rpc-errors": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-7.0.2.tgz", + "integrity": "sha512-YYYHsVYd46XwY2QZzpGeU4PSdRhHdxnzkB8piWGvJW2xbikZ3R+epAYEL4q/K8bh9JPTucsUdwRFnACor1aOYw==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^11.0.1", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": "^18.20 || ^20.17 || >=22" } }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", - "hasInstallScript": true, + "node_modules/@metamask/safe-event-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-3.1.2.tgz", + "integrity": "sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==", + "license": "ISC", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@metamask/sdk": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@metamask/sdk/-/sdk-0.33.1.tgz", + "integrity": "sha512-1mcOQVGr9rSrVcbKPNVzbZ8eCl1K0FATsYH3WJ/MH4WcZDWGECWrXJPNMZoEAkLxWiMe8jOQBumg2pmcDa9zpQ==", + "deprecated": "No longer maintained, superseded by https://docs.metamask.io/metamask-connect", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@metamask/onboarding": "^1.0.1", + "@metamask/providers": "16.1.0", + "@metamask/sdk-analytics": "0.0.5", + "@metamask/sdk-communication-layer": "0.33.1", + "@metamask/sdk-install-modal-web": "0.32.1", + "@paulmillr/qr": "^0.2.1", + "bowser": "^2.9.0", + "cross-fetch": "^4.0.0", + "debug": "4.3.4", + "eciesjs": "^0.4.11", + "eth-rpc-errors": "^4.0.3", + "eventemitter2": "^6.4.9", + "obj-multiplex": "^1.0.0", + "pump": "^3.0.0", + "readable-stream": "^3.6.2", + "socket.io-client": "^4.5.1", + "tslib": "^2.6.0", + "util": "^0.12.4", + "uuid": "^8.3.2" + } + }, + "node_modules/@metamask/sdk-analytics": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@metamask/sdk-analytics/-/sdk-analytics-0.0.5.tgz", + "integrity": "sha512-fDah+keS1RjSUlC8GmYXvx6Y26s3Ax1U9hGpWb6GSY5SAdmTSIqp2CvYy6yW0WgLhnYhW+6xERuD0eVqV63QIQ==", + "deprecated": "No longer maintained, superseded by @metamask/connect-analytics", + "license": "MIT", + "dependencies": { + "openapi-fetch": "^0.13.5" + } + }, + "node_modules/@metamask/sdk-communication-layer": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@metamask/sdk-communication-layer/-/sdk-communication-layer-0.33.1.tgz", + "integrity": "sha512-0bI9hkysxcfbZ/lk0T2+aKVo1j0ynQVTuB3sJ5ssPWlz+Z3VwveCkP1O7EVu1tsVVCb0YV5WxK9zmURu2FIiaA==", + "deprecated": "No longer maintained, superseded by https://docs.metamask.io/metamask-connect", + "dependencies": { + "@metamask/sdk-analytics": "0.0.5", + "bufferutil": "^4.0.8", + "date-fns": "^2.29.3", + "debug": "4.3.4", + "utf-8-validate": "^5.0.2", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "cross-fetch": "^4.0.0", + "eciesjs": "*", + "eventemitter2": "^6.4.9", + "readable-stream": "^3.6.2", + "socket.io-client": "^4.5.1" + } + }, + "node_modules/@metamask/sdk-communication-layer/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" + "ms": "2.1.2" }, "engines": { - "node": ">= 10" + "node": ">=6.0" }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], + "node_modules/@metamask/sdk-communication-layer/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/@metamask/sdk-install-modal-web": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@metamask/sdk-install-modal-web/-/sdk-install-modal-web-0.32.1.tgz", + "integrity": "sha512-MGmAo6qSjf1tuYXhCu2EZLftq+DSt5Z7fsIKr2P+lDgdTPWgLfZB1tJKzNcwKKOdf6q9Qmmxn7lJuI/gq5LrKw==", + "deprecated": "No longer maintained, superseded by https://docs.metamask.io/metamask-connect", + "dependencies": { + "@paulmillr/qr": "^0.2.1" + } + }, + "node_modules/@metamask/sdk/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, "engines": { - "node": ">= 10" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@metamask/sdk/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/@metamask/superstruct": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@metamask/superstruct/-/superstruct-3.4.1.tgz", + "integrity": "sha512-caTaaBUcwBGbUNf3r0uT48upX4nECRbKhQ9pPOfW4sIkfcIUUDV4S9DZxq/5fuNPVt5KWpyd5xIIz0sP+iWLlg==", + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=16.0.0" } }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@metamask/utils": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-11.11.0.tgz", + "integrity": "sha512-0nF2CWjWQr/m0Y2t2lJnBTU1/CZPPTvKvcESLplyWe/tyeb8zFOi/FeneDmaFnML6LYRIGZU6f+xR0jKAIUZfw==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "@types/lodash": "^4.17.20", + "debug": "^4.3.4", + "lodash": "^4.17.21", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, "engines": { - "node": ">= 10" + "node": "^18.18 || ^20.14 || >=22" } }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@metamask/utils/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">= 10" + "node": ">=10" } }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" + "node_modules/@metamask/utils/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@motionone/animation": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "license": "MIT", + "dependencies": { + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.18.0.tgz", + "integrity": "sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A==", + "license": "MIT", + "dependencies": { + "@motionone/animation": "^10.18.0", + "@motionone/generators": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "license": "MIT", + "dependencies": { + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/svelte": { + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/@motionone/svelte/-/svelte-10.16.4.tgz", + "integrity": "sha512-zRVqk20lD1xqe+yEDZhMYgftsuHc25+9JSo+r0a0OWUJFocjSV9D/+UGhX4xgJsuwB9acPzXLr20w40VnY2PQA==", + "license": "MIT", + "dependencies": { + "@motionone/dom": "^10.16.4", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==", + "license": "MIT" + }, + "node_modules/@motionone/utils": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/vue": { + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/@motionone/vue/-/vue-10.16.4.tgz", + "integrity": "sha512-z10PF9JV6SbjFq+/rYabM+8CVlMokgl8RFGvieSGNTmrkQanfHn+15XBrhG3BgUfvmTeSeyShfOHpG0i9zEdcg==", + "deprecated": "Motion One for Vue is deprecated. Use Oku Motion instead https://oku-ui.com/motion", + "license": "MIT", + "dependencies": { + "@motionone/dom": "^10.16.4", + "tslib": "^2.3.1" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.2.1.tgz", + "integrity": "sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.0.tgz", + "integrity": "sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.0" + }, "engines": { - "node": ">= 10" + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "license": "MIT", "engines": { - "node": ">= 10" + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paulmillr/qr": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@paulmillr/qr/-/qr-0.2.1.tgz", + "integrity": "sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==", + "deprecated": "Switch to \"qr\" (new package name) for security updates: npm install qr", + "license": "(MIT OR Apache-2.0)", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit/-/appkit-1.7.8.tgz", + "integrity": "sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit-common": "1.7.8", + "@reown/appkit-controllers": "1.7.8", + "@reown/appkit-pay": "1.7.8", + "@reown/appkit-polyfills": "1.7.8", + "@reown/appkit-scaffold-ui": "1.7.8", + "@reown/appkit-ui": "1.7.8", + "@reown/appkit-utils": "1.7.8", + "@reown/appkit-wallet": "1.7.8", + "@walletconnect/types": "2.21.0", + "@walletconnect/universal-provider": "2.21.0", + "bs58": "6.0.0", + "valtio": "1.13.2", + "viem": ">=2.29.0" + } + }, + "node_modules/@reown/appkit-common": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-common/-/appkit-common-1.7.8.tgz", + "integrity": "sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==", + "license": "Apache-2.0", + "dependencies": { + "big.js": "6.2.2", + "dayjs": "1.11.13", + "viem": ">=2.29.0" + } + }, + "node_modules/@reown/appkit-controllers": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-controllers/-/appkit-controllers-1.7.8.tgz", + "integrity": "sha512-IdXlJlivrlj6m63VsGLsjtPHHsTWvKGVzWIP1fXZHVqmK+rZCBDjCi9j267Rb9/nYRGHWBtlFQhO8dK35WfeDA==", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit-common": "1.7.8", + "@reown/appkit-wallet": "1.7.8", + "@walletconnect/universal-provider": "2.21.0", + "valtio": "1.13.2", + "viem": ">=2.29.0" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@walletconnect/core": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.21.0.tgz", + "integrity": "sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.16", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "@walletconnect/window-getters": "1.0.1", + "es-toolkit": "1.33.0", + "events": "3.3.0", + "uint8arrays": "3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@walletconnect/sign-client": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.21.0.tgz", + "integrity": "sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "2.21.0", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@walletconnect/types": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.21.0.tgz", + "integrity": "sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@walletconnect/universal-provider": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.21.0.tgz", + "integrity": "sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/sign-client": "2.21.0", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "es-toolkit": "1.33.0", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@walletconnect/utils": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.21.0.tgz", + "integrity": "sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==", + "license": "Apache-2.0", + "dependencies": { + "@noble/ciphers": "1.2.1", + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", + "bs58": "6.0.0", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "3.1.0", + "viem": "2.23.2" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/@walletconnect/utils/node_modules/viem": { + "version": "2.23.2", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.2.tgz", + "integrity": "sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.7", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@reown/appkit-controllers/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@reown/appkit-controllers/node_modules/isows": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/@reown/appkit-controllers/node_modules/ox": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", + "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@reown/appkit-controllers/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "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 + } + } + }, + "node_modules/@reown/appkit-pay": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-pay/-/appkit-pay-1.7.8.tgz", + "integrity": "sha512-OSGQ+QJkXx0FEEjlpQqIhT8zGJKOoHzVnyy/0QFrl3WrQTjCzg0L6+i91Ad5Iy1zb6V5JjqtfIFpRVRWN4M3pw==", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit-common": "1.7.8", + "@reown/appkit-controllers": "1.7.8", + "@reown/appkit-ui": "1.7.8", + "@reown/appkit-utils": "1.7.8", + "lit": "3.3.0", + "valtio": "1.13.2" + } + }, + "node_modules/@reown/appkit-polyfills": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-polyfills/-/appkit-polyfills-1.7.8.tgz", + "integrity": "sha512-W/kq786dcHHAuJ3IV2prRLEgD/2iOey4ueMHf1sIFjhhCGMynMkhsOhQMUH0tzodPqUgAC494z4bpIDYjwWXaA==", + "license": "Apache-2.0", + "dependencies": { + "buffer": "6.0.3" + } + }, + "node_modules/@reown/appkit-scaffold-ui": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-scaffold-ui/-/appkit-scaffold-ui-1.7.8.tgz", + "integrity": "sha512-RCeHhAwOrIgcvHwYlNWMcIDibdI91waaoEYBGw71inE0kDB8uZbE7tE6DAXJmDkvl0qPh+DqlC4QbJLF1FVYdQ==", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit-common": "1.7.8", + "@reown/appkit-controllers": "1.7.8", + "@reown/appkit-ui": "1.7.8", + "@reown/appkit-utils": "1.7.8", + "@reown/appkit-wallet": "1.7.8", + "lit": "3.3.0" + } + }, + "node_modules/@reown/appkit-ui": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-ui/-/appkit-ui-1.7.8.tgz", + "integrity": "sha512-1hjCKjf6FLMFzrulhl0Y9Vb9Fu4royE+SXCPSWh4VhZhWqlzUFc7kutnZKx8XZFVQH4pbBvY62SpRC93gqoHow==", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit-common": "1.7.8", + "@reown/appkit-controllers": "1.7.8", + "@reown/appkit-wallet": "1.7.8", + "lit": "3.3.0", + "qrcode": "1.5.3" + } + }, + "node_modules/@reown/appkit-utils": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-utils/-/appkit-utils-1.7.8.tgz", + "integrity": "sha512-8X7UvmE8GiaoitCwNoB86pttHgQtzy4ryHZM9kQpvjQ0ULpiER44t1qpVLXNM4X35O0v18W0Dk60DnYRMH2WRw==", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit-common": "1.7.8", + "@reown/appkit-controllers": "1.7.8", + "@reown/appkit-polyfills": "1.7.8", + "@reown/appkit-wallet": "1.7.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/universal-provider": "2.21.0", + "valtio": "1.13.2", + "viem": ">=2.29.0" + }, + "peerDependencies": { + "valtio": "1.13.2" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@walletconnect/core": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.21.0.tgz", + "integrity": "sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.16", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "@walletconnect/window-getters": "1.0.1", + "es-toolkit": "1.33.0", + "events": "3.3.0", + "uint8arrays": "3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@walletconnect/sign-client": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.21.0.tgz", + "integrity": "sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "2.21.0", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@walletconnect/types": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.21.0.tgz", + "integrity": "sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@walletconnect/universal-provider": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.21.0.tgz", + "integrity": "sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/sign-client": "2.21.0", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "es-toolkit": "1.33.0", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@walletconnect/utils": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.21.0.tgz", + "integrity": "sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==", + "license": "Apache-2.0", + "dependencies": { + "@noble/ciphers": "1.2.1", + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", + "bs58": "6.0.0", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "3.1.0", + "viem": "2.23.2" + } + }, + "node_modules/@reown/appkit-utils/node_modules/@walletconnect/utils/node_modules/viem": { + "version": "2.23.2", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.2.tgz", + "integrity": "sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.7", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@reown/appkit-utils/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@reown/appkit-utils/node_modules/isows": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/@reown/appkit-utils/node_modules/ox": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", + "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@reown/appkit-utils/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "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 + } + } + }, + "node_modules/@reown/appkit-wallet": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/@reown/appkit-wallet/-/appkit-wallet-1.7.8.tgz", + "integrity": "sha512-kspz32EwHIOT/eg/ZQbFPxgXq0B/olDOj3YMu7gvLEFz4xyOFd/wgzxxAXkp5LbG4Cp++s/elh79rVNmVFdB9A==", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit-common": "1.7.8", + "@reown/appkit-polyfills": "1.7.8", + "@walletconnect/logger": "2.1.2", + "zod": "3.22.4" + } + }, + "node_modules/@reown/appkit-wallet/node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@reown/appkit/node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit/node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit/node_modules/@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit/node_modules/@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@reown/appkit/node_modules/@walletconnect/core": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.21.0.tgz", + "integrity": "sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.16", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "@walletconnect/window-getters": "1.0.1", + "es-toolkit": "1.33.0", + "events": "3.3.0", + "uint8arrays": "3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@reown/appkit/node_modules/@walletconnect/sign-client": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.21.0.tgz", + "integrity": "sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "2.21.0", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit/node_modules/@walletconnect/types": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.21.0.tgz", + "integrity": "sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit/node_modules/@walletconnect/universal-provider": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.21.0.tgz", + "integrity": "sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/sign-client": "2.21.0", + "@walletconnect/types": "2.21.0", + "@walletconnect/utils": "2.21.0", + "es-toolkit": "1.33.0", + "events": "3.3.0" + } + }, + "node_modules/@reown/appkit/node_modules/@walletconnect/utils": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.21.0.tgz", + "integrity": "sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==", + "license": "Apache-2.0", + "dependencies": { + "@noble/ciphers": "1.2.1", + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.0", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", + "bs58": "6.0.0", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "3.1.0", + "viem": "2.23.2" + } + }, + "node_modules/@reown/appkit/node_modules/@walletconnect/utils/node_modules/viem": { + "version": "2.23.2", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.2.tgz", + "integrity": "sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.7", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@reown/appkit/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@reown/appkit/node_modules/isows": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/@reown/appkit/node_modules/ox": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", + "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@reown/appkit/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "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 + } + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.3.tgz", + "integrity": "sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.3.tgz", + "integrity": "sha512-3YjElDdWN+qXAFbJ/CzPV+0wspLqh54k/I6GfdYtEJRqg7buSgc1yPM3B+93j1M4neobtkATHZTmxK2AMVGfnA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.3.tgz", + "integrity": "sha512-Pch2pFNOxxz1hTjypIdPyRTR6riiwRl84+VcN9djS680fw+Co1nAJINrdpqp7KV0NvyuU8ilZXZCjd7ykJl1GQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.3.tgz", + "integrity": "sha512-LEuncFUHFiF8t4yZVZvvZA1wk0pjAscRnsrn1EfTEmN4HXotBi2YtcnLRyaK6UbuczW7xZS5ES+81Rdz8Z0T6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.3.tgz", + "integrity": "sha512-zvBUvsQUpOWALdDsk6qbS8bXf2VxmPisuudNDrY7x0p0jBdsoZl8HsHczIOgkQiZldmcacMKtBzpoGVNeIe2bQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.3.tgz", + "integrity": "sha512-C2KmNrcSem/AMg984H/dev+si0lieQGdXdR/lYGJnuumXnFb9Y7QdiI62obFdLlxRYLBv4P0eUVIDbD4c1vVvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.3.tgz", + "integrity": "sha512-ggXnsTAEzNQx74XpunRsiZ9aBZDsI7XIa0hm2nzR9f4WzH5/f/d73ZSDaC5ejJ8YLY4NW+V3wr0tjOaeCq8hqA==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.3.tgz", + "integrity": "sha512-2vng+FlzNUhKZxtej3IUqJgbZoQk2M/dwQM20+ULV0R/E/8tr9/P6uEf2iiGIk4HL0zMKh5Jry7mUHdUOvyGgA==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.3.tgz", + "integrity": "sha512-LLLFZKt4/Nraf9rxDkhiU8QVgLF4WmCkfr0L4fj0fPfIZFBib0DeiFk1hhaYKd03LFAFJcxHslhDFlNJLylf5Q==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.3.tgz", + "integrity": "sha512-WJkdQCvS9sWNOUBJZfQRKpZGFBztRzcowI+nndmflKgU4XY+3a420FgTOSKTsVqJbnzSxeT4vaJalpOaPo2YCQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.3.tgz", + "integrity": "sha512-PwHXCCS2n64/1Ot6rP1YEYA02MGYBcQlr8CSZZyrUG2O7NH6NklYmvr9v3Jy+5e/eDeNchc/ukmKJi9LuflMIQ==", + "cpu": [ + "loong64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.3.tgz", + "integrity": "sha512-vUjxINQu3RC8NZS3ykk1gN65gIz8pAopOq2HXuZhiIxHdx7TFvDG+jgrdSgInu1Eza4/Rfi2VzZgyIgEH4WOaw==", + "cpu": [ + "loong64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.3.tgz", + "integrity": "sha512-wzko4aJ13+0G3kGnviCg5gnXFKd40izKsrf2uOw12US4XqprkDrmwOpeW14aSNa37V8bfPcz5Fkob6LZ3BAPmA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.3.tgz", + "integrity": "sha512-8120ue0JUMSwy11stlwnfdX3pPd+WZYGCDBwEHWtIHi6pOpZmsEF5QKB7a/UN+XFdqvobxz98kv8RTqikyCEBw==", + "cpu": [ + "ppc64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.3.tgz", + "integrity": "sha512-XLFHnR3tXMjbOCh2vtVJHmxt+995uJsTERQyseFDRA0xxMxyTZPLa3OIUlyFaO4mF/Lu0FjmWHCuPXJT1n/IOg==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.3.tgz", + "integrity": "sha512-se6yXvNGMIl0f+RQzyh7XAmia8/9kplQx424wnG2w0C1oi6XgO6Y8otKhdXFHbHs88Ihavzmvh1NWjuovE76BQ==", + "cpu": [ + "riscv64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.3.tgz", + "integrity": "sha512-gNoxRefktVIiGflpONuxWWXZAzIQG++z9qHO3xKwk4WdDMuQja3JHGfE1u0i3PfPDyvhypdk+WrgIJqLhGG7sg==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.3.tgz", + "integrity": "sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.3.tgz", + "integrity": "sha512-LBx9LYXvj2CBkMkjLdNAWLwH0MLMin7do2VcVo9kVPibGLkY0BQQut2fv7NVqkXqZ/CrAu9LqDHVV1xHCMpCPw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.3.tgz", + "integrity": "sha512-ABVf3Q0RCu7NcyCCOZQI0pJ3GuSdfSl8EXcy88QtdceIMIoCUdfhsJChZ64L9zVM2aJHjde1Bhn5uqSRcX9ySA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.3.tgz", + "integrity": "sha512-+2Cy/ldweGBLlPIKsQLF8U5N44a0KDdbrk1rAjHOM9M2K+kGdIVjHLmmrZIcx+9Ny3ke/1JomCsDI1ocb11+sg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.3.tgz", + "integrity": "sha512-dtZvzc8BedpSaFNy75x6uiWwAGTH+aZHDtdrqP6qk+WcLJrfti6sGje1ZJ9UxyzDLF23d/mV+PaMwuC0hL7UVA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.3.tgz", + "integrity": "sha512-Rj8Ra4noo+aYy7sKBggCx0407mws34kAb1ySyWuq5DAtFBQdkSwnsjCgPrhPe9cvgBKZIukpE+CVHvORCS93kQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.3.tgz", + "integrity": "sha512-vp7N084ew/odXn2gi/mzm9mUkQu9l6AiN6dt4IeUM2Uvm9o+cVmP+YkqbMOteLbiGgqBBlJZjIMYVCfOOIVbVQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.3.tgz", + "integrity": "sha512-MOG/3gTOn4Fwf574RVOaY61I5o6P90legkFADiTyn1hyjNydT+cerU2rLUwPdZkKKyJ+iT+K9p7WXK4LM1Ka6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@safe-global/safe-apps-provider": { + "version": "0.18.6", + "resolved": "https://registry.npmjs.org/@safe-global/safe-apps-provider/-/safe-apps-provider-0.18.6.tgz", + "integrity": "sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==", + "license": "MIT", + "dependencies": { + "@safe-global/safe-apps-sdk": "^9.1.0", + "events": "^3.3.0" + } + }, + "node_modules/@safe-global/safe-apps-sdk": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@safe-global/safe-apps-sdk/-/safe-apps-sdk-9.1.0.tgz", + "integrity": "sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==", + "license": "MIT", + "dependencies": { + "@safe-global/safe-gateway-typescript-sdk": "^3.5.3", + "viem": "^2.1.1" + } + }, + "node_modules/@safe-global/safe-gateway-typescript-sdk": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.23.1.tgz", + "integrity": "sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@solana-program/system": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@solana-program/system/-/system-0.10.0.tgz", + "integrity": "sha512-Go+LOEZmqmNlfr+Gjy5ZWAdY5HbYzk2RBewD9QinEU/bBSzpFfzqDRT55JjFRBGJUvMgf3C2vfXEGT4i8DSI4g==", + "license": "Apache-2.0", + "peerDependencies": { + "@solana/kit": "^5.0" + } + }, + "node_modules/@solana-program/token": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@solana-program/token/-/token-0.9.0.tgz", + "integrity": "sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==", + "license": "Apache-2.0", + "peerDependencies": { + "@solana/kit": "^5.0" + } + }, + "node_modules/@solana/accounts": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/accounts/-/accounts-5.5.1.tgz", + "integrity": "sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/addresses": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/addresses/-/addresses-5.5.1.tgz", + "integrity": "sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==", + "license": "MIT", + "dependencies": { + "@solana/assertions": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/assertions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/assertions/-/assertions-5.5.1.tgz", + "integrity": "sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/codecs": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs/-/codecs-5.5.1.tgz", + "integrity": "sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/options": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/codecs-core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-5.5.1.tgz", + "integrity": "sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/codecs-data-structures": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-5.5.1.tgz", + "integrity": "sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/codecs-numbers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-5.5.1.tgz", + "integrity": "sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/codecs-strings": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-5.5.1.tgz", + "integrity": "sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "fastestsmallesttextencoderdecoder": "^1.0.22", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "fastestsmallesttextencoderdecoder": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/errors": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-5.5.1.tgz", + "integrity": "sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==", + "license": "MIT", + "dependencies": { + "chalk": "5.6.2", + "commander": "14.0.2" + }, + "bin": { + "errors": "bin/cli.mjs" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/fast-stable-stringify": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/fast-stable-stringify/-/fast-stable-stringify-5.5.1.tgz", + "integrity": "sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/functional": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/functional/-/functional-5.5.1.tgz", + "integrity": "sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/instruction-plans": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/instruction-plans/-/instruction-plans-5.5.1.tgz", + "integrity": "sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/instructions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/instructions/-/instructions-5.5.1.tgz", + "integrity": "sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/keys": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/keys/-/keys-5.5.1.tgz", + "integrity": "sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==", + "license": "MIT", + "dependencies": { + "@solana/assertions": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/kit": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/kit/-/kit-5.5.1.tgz", + "integrity": "sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==", + "license": "MIT", + "dependencies": { + "@solana/accounts": "5.5.1", + "@solana/addresses": "5.5.1", + "@solana/codecs": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instruction-plans": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/offchain-messages": "5.5.1", + "@solana/plugin-core": "5.5.1", + "@solana/programs": "5.5.1", + "@solana/rpc": "5.5.1", + "@solana/rpc-api": "5.5.1", + "@solana/rpc-parsed-types": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-subscriptions": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/signers": "5.5.1", + "@solana/sysvars": "5.5.1", + "@solana/transaction-confirmation": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/nominal-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/nominal-types/-/nominal-types-5.5.1.tgz", + "integrity": "sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/offchain-messages": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/offchain-messages/-/offchain-messages-5.5.1.tgz", + "integrity": "sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/options": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/options/-/options-5.5.1.tgz", + "integrity": "sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==", + "license": "MIT", + "dependencies": { + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/plugin-core": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/plugin-core/-/plugin-core-5.5.1.tgz", + "integrity": "sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/programs": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/programs/-/programs-5.5.1.tgz", + "integrity": "sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/promises": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/promises/-/promises-5.5.1.tgz", + "integrity": "sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc/-/rpc-5.5.1.tgz", + "integrity": "sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/fast-stable-stringify": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/rpc-api": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-transport-http": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-api": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-api/-/rpc-api-5.5.1.tgz", + "integrity": "sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/rpc-parsed-types": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-parsed-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-parsed-types/-/rpc-parsed-types-5.5.1.tgz", + "integrity": "sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-spec": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec/-/rpc-spec-5.5.1.tgz", + "integrity": "sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/rpc-spec-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-spec-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-spec-types/-/rpc-spec-types-5.5.1.tgz", + "integrity": "sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==", + "license": "MIT", + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions/-/rpc-subscriptions-5.5.1.tgz", + "integrity": "sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/fast-stable-stringify": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-subscriptions-api": "5.5.1", + "@solana/rpc-subscriptions-channel-websocket": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/subscribable": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions-api": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-5.5.1.tgz", + "integrity": "sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/rpc-transformers": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions-channel-websocket": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-5.5.1.tgz", + "integrity": "sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/rpc-subscriptions-spec": "5.5.1", + "@solana/subscribable": "5.5.1", + "ws": "^8.19.0" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-subscriptions-spec": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-5.5.1.tgz", + "integrity": "sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/subscribable": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-transformers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-transformers/-/rpc-transformers-5.5.1.tgz", + "integrity": "sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-transport-http": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-transport-http/-/rpc-transport-http-5.5.1.tgz", + "integrity": "sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1", + "@solana/rpc-spec": "5.5.1", + "@solana/rpc-spec-types": "5.5.1", + "undici-types": "^7.19.2" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/rpc-types": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/rpc-types/-/rpc-types-5.5.1.tgz", + "integrity": "sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/nominal-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/signers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/signers/-/signers-5.5.1.tgz", + "integrity": "sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/offchain-messages": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/subscribable": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/subscribable/-/subscribable-5.5.1.tgz", + "integrity": "sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==", + "license": "MIT", + "dependencies": { + "@solana/errors": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/sysvars": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/sysvars/-/sysvars-5.5.1.tgz", + "integrity": "sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==", + "license": "MIT", + "dependencies": { + "@solana/accounts": "5.5.1", + "@solana/codecs": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/transaction-confirmation": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transaction-confirmation/-/transaction-confirmation-5.5.1.tgz", + "integrity": "sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/promises": "5.5.1", + "@solana/rpc": "5.5.1", + "@solana/rpc-subscriptions": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1", + "@solana/transactions": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/transaction-messages": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transaction-messages/-/transaction-messages-5.5.1.tgz", + "integrity": "sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-types": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@solana/transactions": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@solana/transactions/-/transactions-5.5.1.tgz", + "integrity": "sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==", + "license": "MIT", + "dependencies": { + "@solana/addresses": "5.5.1", + "@solana/codecs-core": "5.5.1", + "@solana/codecs-data-structures": "5.5.1", + "@solana/codecs-numbers": "5.5.1", + "@solana/codecs-strings": "5.5.1", + "@solana/errors": "5.5.1", + "@solana/functional": "5.5.1", + "@solana/instructions": "5.5.1", + "@solana/keys": "5.5.1", + "@solana/nominal-types": "5.5.1", + "@solana/rpc-types": "5.5.1", + "@solana/transaction-messages": "5.5.1" + }, + "engines": { + "node": ">=20.18.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@stablelib/aead": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz", + "integrity": "sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==", + "license": "MIT" + }, + "node_modules/@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "license": "MIT", + "dependencies": { + "@stablelib/int": "^1.0.1" + } + }, + "node_modules/@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==", + "license": "MIT" + }, + "node_modules/@stablelib/chacha": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz", + "integrity": "sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/chacha20poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz", + "integrity": "sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==", + "license": "MIT", + "dependencies": { + "@stablelib/aead": "^1.0.1", + "@stablelib/binary": "^1.0.1", + "@stablelib/chacha": "^1.0.1", + "@stablelib/constant-time": "^1.0.1", + "@stablelib/poly1305": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", + "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==", + "license": "MIT" + }, + "node_modules/@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "license": "MIT", + "dependencies": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==", + "license": "MIT" + }, + "node_modules/@stablelib/hkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hkdf/-/hkdf-1.0.1.tgz", + "integrity": "sha512-SBEHYE16ZXlHuaW5RcGk533YlBj4grMeg5TooN80W3NpcHRtLZLLXvKyX0qcRFxf+BGDobJLnwkvgEwHIDBR6g==", + "license": "MIT", + "dependencies": { + "@stablelib/hash": "^1.0.1", + "@stablelib/hmac": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hmac": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hmac/-/hmac-1.0.1.tgz", + "integrity": "sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA==", + "license": "MIT", + "dependencies": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", + "license": "MIT" + }, + "node_modules/@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "license": "MIT", + "dependencies": { + "@stablelib/bytes": "^1.0.1" + } + }, + "node_modules/@stablelib/poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", + "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "license": "MIT", + "dependencies": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha256": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz", + "integrity": "sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", + "license": "MIT" + }, + "node_modules/@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "license": "MIT", + "dependencies": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz", + "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.101.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.65.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@wagmi/connectors": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@wagmi/connectors/-/connectors-6.2.0.tgz", + "integrity": "sha512-2NfkbqhNWdjfibb4abRMrn7u6rPjEGolMfApXss6HCDVt9AW2oVC6k8Q5FouzpJezElxLJSagWz9FW1zaRlanA==", + "license": "MIT", + "dependencies": { + "@base-org/account": "2.4.0", + "@coinbase/wallet-sdk": "4.3.6", + "@gemini-wallet/core": "0.3.2", + "@metamask/sdk": "0.33.1", + "@safe-global/safe-apps-provider": "0.18.6", + "@safe-global/safe-apps-sdk": "9.1.0", + "@walletconnect/ethereum-provider": "2.21.1", + "cbw-sdk": "npm:@coinbase/wallet-sdk@3.9.3", + "porto": "0.2.35" + }, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "@wagmi/core": "2.22.1", + "typescript": ">=5.0.4", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wagmi/connectors/node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@wagmi/connectors/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@wagmi/connectors/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@wagmi/connectors/node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@wagmi/connectors/node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@wagmi/connectors/node_modules/abitype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.3.0.tgz", + "integrity": "sha512-fk6Te+bojIFrMvMZrnOO+SxCB+RUksTGOzq/60ZRvs1L+BVzvi2bqt9L3W/17ZLdZsyM1FuYf65P5nlmoiH1Bg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@wagmi/connectors/node_modules/ox": { + "version": "0.9.17", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.17.tgz", + "integrity": "sha512-rKAnhzhRU3Xh3hiko+i1ZxywZ55eWQzeS/Q4HRKLx2PqfHOolisZHErSsJVipGlmQKHW5qwOED/GighEw9dbLg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.9", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@wagmi/connectors/node_modules/porto": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/porto/-/porto-0.2.35.tgz", + "integrity": "sha512-gu9FfjjvvYBgQXUHWTp6n3wkTxVtEcqFotM7i3GEZeoQbvLGbssAicCz6hFZ8+xggrJWwi/RLmbwNra50SMmUQ==", + "license": "MIT", + "dependencies": { + "hono": "^4.10.3", + "idb-keyval": "^6.2.1", + "mipd": "^0.0.7", + "ox": "^0.9.6", + "zod": "^4.1.5", + "zustand": "^5.0.1" + }, + "bin": { + "porto": "dist/cli/bin/index.js" + }, + "peerDependencies": { + "@tanstack/react-query": ">=5.59.0", + "@wagmi/core": ">=2.16.3", + "expo-auth-session": ">=7.0.8", + "expo-crypto": ">=15.0.7", + "expo-web-browser": ">=15.0.8", + "react": ">=18", + "react-native": ">=0.81.4", + "typescript": ">=5.4.0", + "viem": ">=2.37.0", + "wagmi": ">=2.0.0" + }, + "peerDependenciesMeta": { + "@tanstack/react-query": { + "optional": true + }, + "expo-auth-session": { + "optional": true + }, + "expo-crypto": { + "optional": true + }, + "expo-web-browser": { + "optional": true + }, + "react": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + }, + "wagmi": { + "optional": true + } + } + }, + "node_modules/@wagmi/connectors/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@wagmi/core": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.22.1.tgz", + "integrity": "sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==", + "license": "MIT", + "dependencies": { + "eventemitter3": "5.0.1", + "mipd": "0.0.7", + "zustand": "5.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "@tanstack/query-core": ">=5.0.0", + "typescript": ">=5.0.4", + "viem": "2.x" + }, + "peerDependenciesMeta": { + "@tanstack/query-core": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@wagmi/core/node_modules/zustand": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.0.tgz", + "integrity": "sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, + "node_modules/@walletconnect/core": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.21.1.tgz", + "integrity": "sha512-Tp4MHJYcdWD846PH//2r+Mu4wz1/ZU/fr9av1UWFiaYQ2t2TPLDiZxjLw54AAEpMqlEHemwCgiRiAmjR1NDdTQ==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.16", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.1", + "@walletconnect/utils": "2.21.1", + "@walletconnect/window-getters": "1.0.1", + "es-toolkit": "1.33.0", + "events": "3.3.0", + "uint8arrays": "3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@walletconnect/environment": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.1.tgz", + "integrity": "sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/environment/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/ethereum-provider": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.21.1.tgz", + "integrity": "sha512-SSlIG6QEVxClgl1s0LMk4xr2wg4eT3Zn/Hb81IocyqNSGfXpjtawWxKxiC5/9Z95f1INyBD6MctJbL/R1oBwIw==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@reown/appkit": "1.7.8", + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/sign-client": "2.21.1", + "@walletconnect/types": "2.21.1", + "@walletconnect/universal-provider": "2.21.1", + "@walletconnect/utils": "2.21.1", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/events": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/events/-/events-1.0.1.tgz", + "integrity": "sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==", + "license": "MIT", + "dependencies": { + "keyvaluestorage-interface": "^1.0.0", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/events/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/heartbeat": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.2.tgz", + "integrity": "sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==", + "license": "MIT", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "events": "^3.3.0" + } + }, + "node_modules/@walletconnect/jsonrpc-http-connection": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-http-connection/-/jsonrpc-http-connection-1.0.8.tgz", + "integrity": "sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.6", + "@walletconnect/safe-json": "^1.0.1", + "cross-fetch": "^3.1.4", + "events": "^3.3.0" + } + }, + "node_modules/@walletconnect/jsonrpc-http-connection/node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/@walletconnect/jsonrpc-provider": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.14.tgz", + "integrity": "sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0" + } + }, + "node_modules/@walletconnect/jsonrpc-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.4.tgz", + "integrity": "sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0", + "keyvaluestorage-interface": "^1.0.0" + } + }, + "node_modules/@walletconnect/jsonrpc-utils": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz", + "integrity": "sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==", + "license": "MIT", + "dependencies": { + "@walletconnect/environment": "^1.0.1", + "@walletconnect/jsonrpc-types": "^1.0.3", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/jsonrpc-utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/jsonrpc-ws-connection": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.16.tgz", + "integrity": "sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.6", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0", + "ws": "^7.5.1" + } + }, + "node_modules/@walletconnect/jsonrpc-ws-connection/node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "license": "MIT", + "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/@walletconnect/keyvaluestorage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", + "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" + }, + "peerDependencies": { + "@react-native-async-storage/async-storage": "1.x" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@walletconnect/logger": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.1.2.tgz", + "integrity": "sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.2", + "pino": "7.11.0" + } + }, + "node_modules/@walletconnect/modal": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@walletconnect/modal/-/modal-2.6.2.tgz", + "integrity": "sha512-eFopgKi8AjKf/0U4SemvcYw9zlLpx9njVN8sf6DAkowC2Md0gPU/UNEbH1Wwj407pEKnEds98pKWib1NN1ACoA==", + "deprecated": "Please follow the migration guide on https://docs.reown.com/appkit/upgrade/wcm", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/modal-core": "2.6.2", + "@walletconnect/modal-ui": "2.6.2" + } + }, + "node_modules/@walletconnect/modal-core": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@walletconnect/modal-core/-/modal-core-2.6.2.tgz", + "integrity": "sha512-cv8ibvdOJQv2B+nyxP9IIFdxvQznMz8OOr/oR/AaUZym4hjXNL/l1a2UlSQBXrVjo3xxbouMxLb3kBsHoYP2CA==", + "license": "Apache-2.0", + "dependencies": { + "valtio": "1.11.2" + } + }, + "node_modules/@walletconnect/modal-core/node_modules/proxy-compare": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz", + "integrity": "sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==", + "license": "MIT" + }, + "node_modules/@walletconnect/modal-core/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@walletconnect/modal-core/node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@walletconnect/modal-core/node_modules/valtio": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.11.2.tgz", + "integrity": "sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==", + "license": "MIT", + "dependencies": { + "proxy-compare": "2.5.1", + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@walletconnect/modal-ui": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@walletconnect/modal-ui/-/modal-ui-2.6.2.tgz", + "integrity": "sha512-rbdstM1HPGvr7jprQkyPggX7rP4XiCG85ZA+zWBEX0dVQg8PpAgRUqpeub4xQKDgY7pY/xLRXSiCVdWGqvG2HA==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/modal-core": "2.6.2", + "lit": "2.8.0", + "motion": "10.16.2", + "qrcode": "1.5.3" + } + }, + "node_modules/@walletconnect/modal-ui/node_modules/@lit/reactive-element": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz", + "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.0.0" + } + }, + "node_modules/@walletconnect/modal-ui/node_modules/lit": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz", + "integrity": "sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^1.6.0", + "lit-element": "^3.3.0", + "lit-html": "^2.8.0" + } + }, + "node_modules/@walletconnect/modal-ui/node_modules/lit-element": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz", + "integrity": "sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.1.0", + "@lit/reactive-element": "^1.3.0", + "lit-html": "^2.8.0" + } + }, + "node_modules/@walletconnect/modal-ui/node_modules/lit-html": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz", + "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/@walletconnect/relay-api": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.11.tgz", + "integrity": "sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-types": "^1.0.2" + } + }, + "node_modules/@walletconnect/relay-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.1.0.tgz", + "integrity": "sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==", + "license": "MIT", + "dependencies": { + "@noble/curves": "1.8.0", + "@noble/hashes": "1.7.0", + "@walletconnect/safe-json": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "uint8arrays": "^3.0.0" + } + }, + "node_modules/@walletconnect/relay-auth/node_modules/@noble/hashes": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.0.tgz", + "integrity": "sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@walletconnect/safe-json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.2.tgz", + "integrity": "sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/safe-json/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/sign-client": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.21.1.tgz", + "integrity": "sha512-QaXzmPsMnKGV6tc4UcdnQVNOz4zyXgarvdIQibJ4L3EmLat73r5ZVl4c0cCOcoaV7rgM9Wbphgu5E/7jNcd3Zg==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "2.21.1", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.1", + "@walletconnect/utils": "2.21.1", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/time/-/time-1.0.2.tgz", + "integrity": "sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" } }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@walletconnect/time/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/types": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.21.1.tgz", + "integrity": "sha512-UeefNadqP6IyfwWC1Yi7ux+ljbP2R66PLfDrDm8izmvlPmYlqRerJWJvYO4t0Vvr9wrG4Ko7E0c4M7FaPKT/sQ==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/universal-provider": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.21.1.tgz", + "integrity": "sha512-Wjx9G8gUHVMnYfxtasC9poGm8QMiPCpXpbbLFT+iPoQskDDly8BwueWnqKs4Mx2SdIAWAwuXeZ5ojk5qQOxJJg==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/sign-client": "2.21.1", + "@walletconnect/types": "2.21.1", + "@walletconnect/utils": "2.21.1", + "es-toolkit": "1.33.0", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/utils": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.21.1.tgz", + "integrity": "sha512-VPZvTcrNQCkbGOjFRbC24mm/pzbRMUq2DSQoiHlhh0X1U7ZhuIrzVtAoKsrzu6rqjz0EEtGxCr3K1TGRqDG4NA==", + "license": "Apache-2.0", + "dependencies": { + "@noble/ciphers": "1.2.1", + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.1.0", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.21.1", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", + "bs58": "6.0.0", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "3.1.0", + "viem": "2.23.2" + } + }, + "node_modules/@walletconnect/utils/node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.1" + }, "engines": { - "node": ">= 10" + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@walletconnect/utils/node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", "engines": { - "node": ">= 10" + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "node_modules/@walletconnect/utils/node_modules/@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "optional": true, + "node_modules/@walletconnect/utils/node_modules/@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "license": "MIT", "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" }, - "engines": { - "node": ">=14.0.0" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" + "node_modules/@walletconnect/utils/node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } } }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" + "node_modules/@walletconnect/utils/node_modules/isows": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", + "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } ], - "engines": { - "node": ">= 10" + "license": "MIT", + "peerDependencies": { + "ws": "*" } }, - "node_modules/@tailwindcss/vite": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", - "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", + "node_modules/@walletconnect/utils/node_modules/ox": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", + "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", - "tailwindcss": "4.1.11" + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" }, "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7" + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, + "node_modules/@walletconnect/utils/node_modules/viem": { + "version": "2.23.2", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.2.tgz", + "integrity": "sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.7", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" + "node_modules/@walletconnect/utils/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "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 + } } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, + "node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "tslib": "1.14.1" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, + "node_modules/@walletconnect/window-getters/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "node_modules/@walletconnect/window-metadata/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" }, - "node_modules/@types/react": { - "version": "19.1.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", - "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, + "node_modules/@web3modal/common": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/common/-/common-4.2.3.tgz", + "integrity": "sha512-n0lvhoRjViqxmkgpy+iEM6E3HBylUgdxUDJU4hUxGmmrbGZGEP7USBRnQOEgXLqLCtWvxKjUAO33JBV/De+Osw==", + "license": "Apache-2.0", "dependencies": { - "csstype": "^3.0.2" + "bignumber.js": "9.1.2", + "dayjs": "1.11.10" + } + }, + "node_modules/@web3modal/common/node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", + "license": "MIT" + }, + "node_modules/@web3modal/core": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/core/-/core-4.2.3.tgz", + "integrity": "sha512-UykKZTELBpb6ey+IV6fkHWsLkjrIdILmRYzhlznyTPbm9qX5pOR9tH0Z3QGUo7YPFmUqMRH1tC9Irsr3SgIbbw==", + "deprecated": "Web3Modal is now Reown AppKit. Please follow the upgrade guide at https://docs.reown.com/appkit/upgrade/from-w3m-to-reown", + "license": "Apache-2.0", + "dependencies": { + "@web3modal/common": "4.2.3", + "@web3modal/wallet": "4.2.3", + "valtio": "1.11.2" + } + }, + "node_modules/@web3modal/core/node_modules/proxy-compare": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz", + "integrity": "sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==", + "license": "MIT" + }, + "node_modules/@web3modal/core/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@types/react-dom": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", - "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", - "dev": true, + "node_modules/@web3modal/core/node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "node_modules/@web3modal/core/node_modules/valtio": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.11.2.tgz", + "integrity": "sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==", + "license": "MIT", + "dependencies": { + "proxy-compare": "2.5.1", + "use-sync-external-store": "1.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=12.20.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "@types/react": ">=16.8", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "engines": { - "node": ">= 4" + "node_modules/@web3modal/polyfills": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/polyfills/-/polyfills-4.2.3.tgz", + "integrity": "sha512-RiGxh2hMLSD1s2aTjoejNK/UL377CJhGf5tzmdF1m5xsYHpil+Dnulpio8Yojnm27cOqQD+QiaYUKnHOxErLjQ==", + "license": "Apache-2.0", + "dependencies": { + "buffer": "6.0.3" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", - "dev": true, + "node_modules/@web3modal/scaffold": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/scaffold/-/scaffold-4.2.3.tgz", + "integrity": "sha512-8K+IV+luDUvppKgmlgdA+RbQGT2STdRrgHVHFRsAqsORFoLiIYvlrpQlxvV7J5Xc1bgKEn3KvEXC+BH2NMqF4w==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@web3modal/common": "4.2.3", + "@web3modal/core": "4.2.3", + "@web3modal/siwe": "4.2.3", + "@web3modal/ui": "4.2.3", + "@web3modal/wallet": "4.2.3", + "lit": "3.1.0" + } + }, + "node_modules/@web3modal/scaffold-react": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/scaffold-react/-/scaffold-react-4.2.3.tgz", + "integrity": "sha512-WRA244mO3qa9wnJtRa+mfXHkfW92VEkEt+HagLQuUcSRTQJH0Q95UF+EXZZ/r1mKbqdqIbpguewuF0dRtL/YrQ==", + "license": "Apache-2.0", + "dependencies": { + "@web3modal/scaffold": "4.2.3" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "react": ">=17", + "react-dom": ">=17" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", - "dev": true, + "node_modules/@web3modal/scaffold-utils": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/scaffold-utils/-/scaffold-utils-4.2.3.tgz", + "integrity": "sha512-z6t0ggYg1/8hpaKHUm77z2VyacjIZEZTI8IHSQYmHuRFGu5oDPJeAr1thr475JXdoGLYr08hwquZyed/ZINAvw==", + "license": "Apache-2.0", + "dependencies": { + "@web3modal/core": "4.2.3", + "@web3modal/polyfills": "4.2.3", + "valtio": "1.11.2" + } + }, + "node_modules/@web3modal/scaffold-utils/node_modules/proxy-compare": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz", + "integrity": "sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==", + "license": "MIT" + }, + "node_modules/@web3modal/scaffold-utils/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" + "loose-envify": "^1.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, + "node": ">=0.10.0" + } + }, + "node_modules/@web3modal/scaffold-utils/node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", - "dev": true, + "node_modules/@web3modal/scaffold-utils/node_modules/valtio": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.11.2.tgz", + "integrity": "sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "proxy-compare": "2.5.1", + "use-sync-external-store": "1.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=12.20.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@types/react": ">=16.8", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node_modules/@web3modal/scaffold-vue": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/scaffold-vue/-/scaffold-vue-4.2.3.tgz", + "integrity": "sha512-0mlx/t0A7srcuFcxP3xuUt2ACFUUcAhyRIsNImtQHPq7QHx7i5zvabQ38iplDsWS0TA7j83hW5gxHycppa5PXg==", + "license": "Apache-2.0", + "dependencies": { + "@web3modal/scaffold": "4.2.3" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "vue": ">=3" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@web3modal/scaffold/node_modules/lit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.0.tgz", + "integrity": "sha512-rzo/hmUqX8zmOdamDAeydfjsGXbbdtAFqMhmocnh2j9aDYqbu0fjXygjCa0T99Od9VQ/2itwaGrjZz/ZELVl7w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.0.0", + "lit-element": "^4.0.0", + "lit-html": "^3.1.0" + } + }, + "node_modules/@web3modal/siwe": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/siwe/-/siwe-4.2.3.tgz", + "integrity": "sha512-uPma0U/OxAy3LwnF7pCYYX8tn+ONBYNcssuVZxEGsusJD1kF4ueS8lK7eyQogyK5nXqOGdNESOjY1NImNNjMVw==", + "deprecated": "Web3Modal is now Reown AppKit. Please follow the upgrade guide at https://docs.reown.com/appkit/upgrade/from-w3m-to-reown", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/utils": "2.12.0", + "@web3modal/core": "4.2.3", + "@web3modal/scaffold-utils": "4.2.3", + "lit": "3.1.0", + "valtio": "1.11.2" + } + }, + "node_modules/@web3modal/siwe/node_modules/@walletconnect/heartbeat": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.1.tgz", + "integrity": "sha512-yVzws616xsDLJxuG/28FqtZ5rzrTA4gUjdEMTbWB5Y8V1XHRmqq4efAxCw5ie7WjbXFSUyBHaWlMR+2/CpQC5Q==", + "license": "MIT", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "tslib": "1.14.1" + } + }, + "node_modules/@web3modal/siwe/node_modules/@walletconnect/jsonrpc-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.3.tgz", + "integrity": "sha512-iIQ8hboBl3o5ufmJ8cuduGad0CQm3ZlsHtujv9Eu16xq89q+BG7Nh5VLxxUgmtpnrePgFkTwXirCTkwJH1v+Yw==", + "license": "MIT", + "dependencies": { + "keyvaluestorage-interface": "^1.0.0", + "tslib": "1.14.1" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", - "dev": true, + "node_modules/@web3modal/siwe/node_modules/@walletconnect/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.12.0.tgz", + "integrity": "sha512-uhB3waGmujQVJcPgJvGOpB8RalgYSBT+HpmVbfl4Qe0xJyqpRUo4bPjQa0UYkrHaW20xIw94OuP4+FMLYdeemg==", + "license": "Apache-2.0", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@walletconnect/events": "^1.0.1", + "@walletconnect/heartbeat": "1.2.1", + "@walletconnect/jsonrpc-types": "1.0.3", + "@walletconnect/keyvaluestorage": "^1.1.1", + "@walletconnect/logger": "^2.0.1", + "events": "^3.3.0" + } + }, + "node_modules/@web3modal/siwe/node_modules/@walletconnect/utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.12.0.tgz", + "integrity": "sha512-GIpfHUe1Bjp1Tjda0SkJEizKOT2biuv7VPFnKsOLT1T+8QxEP9NruC+K2UUEvijS1Qr/LKH9P5004RYNgrch+w==", + "license": "Apache-2.0", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "^1.0.3", + "@walletconnect/relay-api": "^1.0.9", + "@walletconnect/safe-json": "^1.0.2", + "@walletconnect/time": "^1.0.2", + "@walletconnect/types": "2.12.0", + "@walletconnect/window-getters": "^1.0.1", + "@walletconnect/window-metadata": "^1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "^3.1.0" + } + }, + "node_modules/@web3modal/siwe/node_modules/lit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.0.tgz", + "integrity": "sha512-rzo/hmUqX8zmOdamDAeydfjsGXbbdtAFqMhmocnh2j9aDYqbu0fjXygjCa0T99Od9VQ/2itwaGrjZz/ZELVl7w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.0.0", + "lit-element": "^4.0.0", + "lit-html": "^3.1.0" + } + }, + "node_modules/@web3modal/siwe/node_modules/proxy-compare": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz", + "integrity": "sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==", + "license": "MIT" + }, + "node_modules/@web3modal/siwe/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "node": ">=0.10.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node_modules/@web3modal/siwe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@web3modal/siwe/node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", - "dev": true, + "node_modules/@web3modal/siwe/node_modules/valtio": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.11.2.tgz", + "integrity": "sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==", + "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "proxy-compare": "2.5.1", + "use-sync-external-store": "1.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=12.20.0" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "@types/react": ">=16.8", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, + "node_modules/@web3modal/ui": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/ui/-/ui-4.2.3.tgz", + "integrity": "sha512-QPPgE0hii1gpAldTdnrP63D/ryI78Ohz99zRBp8vi81lawot7rbdUbryMoX13hMPCW9vW7JYyvX+jJN7uO3QwA==", + "deprecated": "Web3Modal is now Reown AppKit. Please follow the upgrade guide at https://docs.reown.com/appkit/upgrade/from-w3m-to-reown", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0" + "lit": "3.1.0", + "qrcode": "1.5.3" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, + "node_modules/@web3modal/ui/node_modules/lit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.0.tgz", + "integrity": "sha512-rzo/hmUqX8zmOdamDAeydfjsGXbbdtAFqMhmocnh2j9aDYqbu0fjXygjCa0T99Od9VQ/2itwaGrjZz/ZELVl7w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.0.0", + "lit-element": "^4.0.0", + "lit-html": "^3.1.0" + } + }, + "node_modules/@web3modal/wagmi": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/wagmi/-/wagmi-4.2.3.tgz", + "integrity": "sha512-oisBCMrOYn8TBgNaSPrumvMmTGox6+3Ii92zxQJalW5U/K9iBTxoejHT033Ss7mFEFybilcfXBAvGNFXfQmtkA==", + "deprecated": "Web3Modal is now Reown AppKit. Please follow the upgrade guide at https://docs.reown.com/appkit/upgrade/from-w3m-to-reown", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@walletconnect/ethereum-provider": "2.13.0", + "@web3modal/polyfills": "4.2.3", + "@web3modal/scaffold": "4.2.3", + "@web3modal/scaffold-react": "4.2.3", + "@web3modal/scaffold-utils": "4.2.3", + "@web3modal/scaffold-vue": "4.2.3", + "@web3modal/siwe": "4.2.3" }, - "engines": { - "node": ">=16 || 14 >=14.17" + "peerDependencies": { + "@wagmi/connectors": ">=4", + "@wagmi/core": ">=2.0.0", + "react": ">=17", + "react-dom": ">=17", + "viem": ">=2.0.0", + "vue": ">=3" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "vue": { + "optional": true + } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/core": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.13.0.tgz", + "integrity": "sha512-blDuZxQenjeXcVJvHxPznTNl6c/2DO4VNrFnus+qHmO6OtT5lZRowdMtlCaCNb1q0OxzgrmBDcTOCbFcCpio/g==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.14", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/relay-api": "1.0.10", + "@walletconnect/relay-auth": "1.0.4", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.13.0", + "@walletconnect/utils": "2.13.0", + "events": "3.3.0", + "isomorphic-unfetch": "3.1.0", + "lodash.isequal": "4.5.0", + "uint8arrays": "3.1.0" + } + }, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/ethereum-provider": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@walletconnect/ethereum-provider/-/ethereum-provider-2.13.0.tgz", + "integrity": "sha512-dnpW8mmLpWl1AZUYGYZpaAfGw1HFkL0WSlhk5xekx3IJJKn4pLacX2QeIOo0iNkzNQxZfux1AK4Grl1DvtzZEA==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/modal": "2.6.2", + "@walletconnect/sign-client": "2.13.0", + "@walletconnect/types": "2.13.0", + "@walletconnect/universal-provider": "2.13.0", + "@walletconnect/utils": "2.13.0", + "events": "3.3.0" + } + }, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/jsonrpc-ws-connection": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.14.tgz", + "integrity": "sha512-Jsl6fC55AYcbkNVkwNM6Jo+ufsuCQRqViOQ8ZBPH9pRREHH9welbBiszuTLqEJiQcO/6XfFDl6bzCJIkrEi8XA==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.6", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0", + "ws": "^7.5.1" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", - "dev": true, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/relay-api": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.10.tgz", + "integrity": "sha512-tqrdd4zU9VBNqUaXXQASaexklv6A54yEyQQEXYOCr+Jz8Ket0dmPBDyg19LVSNUN2cipAghQc45/KVmfFJ0cYw==", + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "@walletconnect/jsonrpc-types": "^1.0.2" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", - "dev": true, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/relay-auth": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.0.4.tgz", + "integrity": "sha512-kKJcS6+WxYq5kshpPaxGHdwf5y98ZwbfuS4EE/NkQzqrDFm5Cj+dP8LofzWvjrrLkZq7Afy7WrQMXdLy8Sx7HQ==", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" + "@stablelib/ed25519": "^1.0.2", + "@stablelib/random": "^1.0.1", + "@walletconnect/safe-json": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "tslib": "1.14.1", + "uint8arrays": "^3.0.0" + } + }, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/sign-client": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.13.0.tgz", + "integrity": "sha512-En7KSvNUlQFx20IsYGsFgkNJ2lpvDvRsSFOT5PTdGskwCkUfOpB33SQJ6nCrN19gyoKPNvWg80Cy6MJI0TjNYA==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "2.13.0", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.13.0", + "@walletconnect/utils": "2.13.0", + "events": "3.3.0" + } + }, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/types": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.13.0.tgz", + "integrity": "sha512-MWaVT0FkZwzYbD3tvk8F+2qpPlz1LUSWHuqbINUtMXnSzJtXN49Y99fR7FuBhNFtDalfuWsEK17GrNA+KnAsPQ==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "events": "3.3.0" + } + }, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/universal-provider": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@walletconnect/universal-provider/-/universal-provider-2.13.0.tgz", + "integrity": "sha512-B5QvO8pnk5Bqn4aIt0OukGEQn2Auk9VbHfhQb9cGwgmSCd1GlprX/Qblu4gyT5+TjHMb1Gz5UssUaZWTWbDhBg==", + "deprecated": "Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/jsonrpc-http-connection": "1.0.8", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/sign-client": "2.13.0", + "@walletconnect/types": "2.13.0", + "@walletconnect/utils": "2.13.0", + "events": "3.3.0" + } + }, + "node_modules/@web3modal/wagmi/node_modules/@walletconnect/utils": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.13.0.tgz", + "integrity": "sha512-q1eDCsRHj5iLe7fF8RroGoPZpdo2CYMZzQSrw1iqL+2+GOeqapxxuJ1vaJkmDUkwgklfB22ufqG6KQnz78sD4w==", + "license": "Apache-2.0", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "1.0.3", + "@walletconnect/relay-api": "1.0.10", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.13.0", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", + "detect-browser": "5.3.0", + "query-string": "7.1.3", + "uint8arrays": "3.1.0" + } + }, + "node_modules/@web3modal/wagmi/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@web3modal/wagmi/node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/@vitejs/plugin-react": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz", - "integrity": "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==", - "dev": true, + "node_modules/@web3modal/wallet": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@web3modal/wallet/-/wallet-4.2.3.tgz", + "integrity": "sha512-V+VpwmhQl9qeJMpzNkjpAaxercAsrr1O9oGRjrjD+c0q72NfdcbTalWSbjSQmqabI1M6N06Hw94FkAQuEfVGsg==", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.27.4", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.19", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" + "@web3modal/polyfills": "4.2.3", + "zod": "3.22.4" + } + }, + "node_modules/@web3modal/wallet/node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/abitype": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.6.tgz", + "integrity": "sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1830,15 +6769,17 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1850,11 +6791,20 @@ "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==", + "license": "MIT", + "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==", - "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -1865,44 +6815,195 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" + }, + "node_modules/async-mutex": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.2.6.tgz", + "integrity": "sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios-retry": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz", + "integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==", + "license": "Apache-2.0", + "dependencies": { + "is-retry-allowed": "^2.2.0" + }, + "peerDependencies": { + "axios": "0.x || 1.x" + } }, "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==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/base-x": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", + "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==", + "license": "MIT" + }, + "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" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.5.tgz", + "integrity": "sha512-xJo6a6YZnwZfnyGmQKWMbVOcii7XRibjOskRh+WJ9UHQoX16xrQrcIgAMQOzfvs8XiLMx6ih/fsLPF73iY2D1A==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "license": "MIT", + "engines": { + "node": "*" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", "engines": { - "node": ">=8" + "node": "*" + } + }, + "node_modules/bn.js": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.5.tgz", + "integrity": "sha512-Vq886eXykuP5E6HcKSSStP3bJgrE6In5WKxVUvJ8XGpWWYs2xZHWqUwzCtGgEtBcxyd57KBFDPFoUfNzdaHCNg==", + "license": "MIT" + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, "funding": [ { @@ -1918,17 +7019,112 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "license": "MIT", + "dependencies": { + "base-x": "^5.0.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bufferutil": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", + "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { @@ -1936,14 +7132,24 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "dev": true, "funding": [ { @@ -1958,37 +7164,88 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" + }, + "node_modules/cbw-sdk": { + "name": "@coinbase/wallet-sdk", + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/@coinbase/wallet-sdk/-/wallet-sdk-3.9.3.tgz", + "integrity": "sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==", + "license": "Apache-2.0", + "dependencies": { + "bn.js": "^5.2.1", + "buffer": "^6.0.3", + "clsx": "^1.2.1", + "eth-block-tracker": "^7.1.0", + "eth-json-rpc-filters": "^6.0.0", + "eventemitter3": "^5.0.1", + "keccak": "^3.0.3", + "preact": "^10.16.0", + "sha.js": "^2.4.11" + } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "readdirp": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">= 20.19.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=6" } }, "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==", - "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -2000,25 +7257,82 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "license": "MIT", + "engines": { + "node": ">=20" + } }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2028,17 +7342,58 @@ "node": ">= 8" } }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -2051,43 +7406,305 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/derive-valtio": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/derive-valtio/-/derive-valtio-0.1.0.tgz", + "integrity": "sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==", + "license": "MIT", + "peerDependencies": { + "valtio": "*" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-browser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", + "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==", + "license": "MIT" }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "engines": { "node": ">=8" } }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eciesjs": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz", + "integrity": "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==", + "license": "MIT", + "dependencies": { + "@ecies/ciphers": "^0.2.5", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0" + }, + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + } + }, + "node_modules/eciesjs/node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/eciesjs/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/eciesjs/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/electron-to-chromium": { - "version": "1.5.183", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.183.tgz", - "integrity": "sha512-vCrDBYjQCAEefWGjlK3EpoSKfKbT10pR4XXPdn65q7snuNOZnthoVpBfZPykmDapOKfoD+MMIPG8ZjKyyc9oHA==", - "dev": true + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", + "dev": true, + "license": "ISC" + }, + "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==", + "license": "MIT" + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz", + "integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.21.0", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } }, "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-toolkit": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.33.0.tgz", + "integrity": "sha512-X13Q/ZSc+vsO1q600bvNK4bxgXMkHcf//RxCmYDaRY5DAcT+eoXjY5hoAPGMdRnWQjvyLEcyauG3b6hz76LNqg==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2095,32 +7712,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { @@ -2128,6 +7745,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -2137,6 +7755,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2145,25 +7764,25 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -2182,7 +7801,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2209,6 +7828,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2217,10 +7837,11 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.20", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", - "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", "dev": true, + "license": "MIT", "peerDependencies": { "eslint": ">=8.40" } @@ -2230,6 +7851,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -2246,6 +7868,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2253,11 +7876,29 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -2271,10 +7912,11 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -2287,6 +7929,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -2299,6 +7942,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -2308,63 +7952,210 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.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==", - "dev": true + "node_modules/eth-block-tracker": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-7.1.0.tgz", + "integrity": "sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==", + "license": "MIT", + "dependencies": { + "@metamask/eth-json-rpc-provider": "^1.0.0", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^5.0.1", + "json-rpc-random-id": "^1.0.1", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, + "node_modules/eth-block-tracker/node_modules/@metamask/utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-5.0.2.tgz", + "integrity": "sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==", + "license": "ISC", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" + "@ethereumjs/tx": "^4.1.2", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "semver": "^7.3.8", + "superstruct": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/eth-block-tracker/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8.6.0" + "node": ">=10" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "node_modules/eth-json-rpc-filters": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-6.0.1.tgz", + "integrity": "sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig==", + "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "@metamask/safe-event-emitter": "^3.0.0", + "async-mutex": "^0.2.6", + "eth-query": "^2.1.2", + "json-rpc-engine": "^6.1.0", + "pify": "^5.0.0" }, "engines": { - "node": ">= 6" + "node": ">=14.0.0" + } + }, + "node_modules/eth-json-rpc-filters/node_modules/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eth-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz", + "integrity": "sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==", + "license": "ISC", + "dependencies": { + "json-rpc-random-id": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "node_modules/eth-rpc-errors": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz", + "integrity": "sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==", + "license": "MIT", + "dependencies": { + "fast-safe-stringify": "^2.0.6" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" } }, + "node_modules/extension-port-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/extension-port-stream/-/extension-port-stream-3.0.0.tgz", + "integrity": "sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==", + "license": "ISC", + "dependencies": { + "readable-stream": "^3.6.2 || ^4.4.2", + "webextension-polyfill": ">=0.10.0 <1.0" + }, + "engines": { + "node": ">=12.0.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==", + "license": "MIT" + }, "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "license": "MIT" + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/file-entry-cache": { @@ -2372,6 +8163,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -2379,16 +8171,13 @@ "node": ">=16.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, "node_modules/find-up": { @@ -2396,6 +8185,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -2412,6 +8202,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -2421,16 +8212,69 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz", + "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2439,20 +8283,86 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, + "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==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -2461,10 +8371,11 @@ } }, "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -2472,31 +8383,149 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" + }, + "node_modules/hono": { + "version": "4.12.32", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.32.tgz", + "integrity": "sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", + "license": "Apache-2.0" + }, + "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" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -2506,6 +8535,7 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2522,24 +8552,104 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -2547,40 +8657,141 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/isomorphic-unfetch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", + "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "unfetch": "^4.2.0" + } + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } }, "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.4.tgz", + "integrity": "sha512-N8acGzVsQy6M/fjFcxtysNc4Q379TcM5dM/qKkNtsHFji88yANnXTr7BLeP75iPnFwBfQzM/jg2BZ9+HZrHCZA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2593,6 +8804,7 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -2604,25 +8816,54 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/json-rpc-engine": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-6.1.0.tgz", + "integrity": "sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==", + "license": "ISC", + "dependencies": { + "@metamask/safe-event-emitter": "^2.0.0", + "eth-rpc-errors": "^4.0.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/json-rpc-engine/node_modules/@metamask/safe-event-emitter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", + "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==", + "license": "ISC" + }, + "node_modules/json-rpc-random-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz", + "integrity": "sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==", + "license": "ISC" }, "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -2630,20 +8871,43 @@ "node": ">=6" } }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "hasInstallScript": true, + "license": "MIT", + "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.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, + "node_modules/keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", + "license": "MIT" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -2653,9 +8917,10 @@ } }, "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, @@ -2665,27 +8930,49 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], + "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -2699,12 +8986,13 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], + "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -2718,12 +9006,13 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], + "license": "MPL-2.0", "optional": true, "os": [ "freebsd" @@ -2737,12 +9026,13 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -2756,12 +9046,16 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -2775,12 +9069,16 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -2794,12 +9092,16 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -2813,12 +9115,16 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -2832,12 +9138,13 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], + "license": "MPL-2.0", "optional": true, "os": [ "win32" @@ -2851,12 +9158,13 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], + "license": "MPL-2.0", "optional": true, "os": [ "win32" @@ -2869,11 +9177,43 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lit": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.0.tgz", + "integrity": "sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", + "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -2884,56 +9224,111 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "mime-db": "1.52.0" }, "engines": { - "node": ">=8.6" + "node": ">= 0.6" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2941,55 +9336,63 @@ "node": "*" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dependencies": { - "minipass": "^7.1.2" + "node_modules/mipd": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mipd/-/mipd-0.0.7.tgz", + "integrity": "sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": ">=5.0.4" }, - "engines": { - "node": ">= 18" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node_modules/motion": { + "version": "10.16.2", + "resolved": "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz", + "integrity": "sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ==", + "license": "MIT", + "dependencies": { + "@motionone/animation": "^10.15.1", + "@motionone/dom": "^10.16.2", + "@motionone/svelte": "^10.16.2", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "@motionone/vue": "^10.16.2" } }, "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==", - "dev": true + "license": "MIT" + }, + "node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "license": "(Apache-2.0 AND MIT)" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3001,19 +9404,171 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obj-multiplex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/obj-multiplex/-/obj-multiplex-1.0.0.tgz", + "integrity": "sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==", + "license": "ISC", + "dependencies": { + "end-of-stream": "^1.4.0", + "once": "^1.4.0", + "readable-stream": "^2.3.3" + } + }, + "node_modules/obj-multiplex/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/obj-multiplex/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/obj-multiplex/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==", + "license": "MIT" + }, + "node_modules/obj-multiplex/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/on-exit-leak-free": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", + "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==", + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openapi-fetch": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.13.8.tgz", + "integrity": "sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==", + "license": "MIT", + "dependencies": { + "openapi-typescript-helpers": "^0.0.15" + } + }, + "node_modules/openapi-typescript-helpers": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.15.tgz", + "integrity": "sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==", + "license": "MIT" }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -3026,11 +9581,95 @@ "node": ">= 0.8.0" } }, + "node_modules/ox": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.9.tgz", + "integrity": "sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ox/node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "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==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -3046,6 +9685,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -3056,11 +9696,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -3072,7 +9722,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3082,6 +9732,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3089,24 +9740,99 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pino": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz", + "integrity": "sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.0.0", + "on-exit-leak-free": "^0.2.0", + "pino-abstract-transport": "v0.5.0", + "pino-std-serializers": "^4.0.0", + "process-warning": "^1.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.1.0", + "safe-stable-stringify": "^2.1.0", + "sonic-boom": "^2.2.1", + "thread-stream": "^0.15.1" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", + "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", + "license": "MIT", + "dependencies": { + "duplexify": "^4.1.2", + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", + "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==", + "license": "MIT" + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pony-cause": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -3121,8 +9847,9 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3130,61 +9857,140 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.24.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.2.tgz", + "integrity": "sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/process-warning": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", + "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==", + "license": "MIT" + }, + "node_modules/proxy-compare": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.6.0.tgz", + "integrity": "sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "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/qrcode": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz", + "integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.2.8" } }, "node_modules/react-refresh": { @@ -3192,35 +9998,79 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", + "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.62.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.3.tgz", + "integrity": "sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==", + "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -3230,34 +10080,38 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.62.3", + "@rollup/rollup-android-arm64": "4.62.3", + "@rollup/rollup-darwin-arm64": "4.62.3", + "@rollup/rollup-darwin-x64": "4.62.3", + "@rollup/rollup-freebsd-arm64": "4.62.3", + "@rollup/rollup-freebsd-x64": "4.62.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.3", + "@rollup/rollup-linux-arm-musleabihf": "4.62.3", + "@rollup/rollup-linux-arm64-gnu": "4.62.3", + "@rollup/rollup-linux-arm64-musl": "4.62.3", + "@rollup/rollup-linux-loong64-gnu": "4.62.3", + "@rollup/rollup-linux-loong64-musl": "4.62.3", + "@rollup/rollup-linux-ppc64-gnu": "4.62.3", + "@rollup/rollup-linux-ppc64-musl": "4.62.3", + "@rollup/rollup-linux-riscv64-gnu": "4.62.3", + "@rollup/rollup-linux-riscv64-musl": "4.62.3", + "@rollup/rollup-linux-s390x-gnu": "4.62.3", + "@rollup/rollup-linux-x64-gnu": "4.62.3", + "@rollup/rollup-linux-x64-musl": "4.62.3", + "@rollup/rollup-openbsd-x64": "4.62.3", + "@rollup/rollup-openharmony-arm64": "4.62.3", + "@rollup/rollup-win32-arm64-msvc": "4.62.3", + "@rollup/rollup-win32-ia32-msvc": "4.62.3", + "@rollup/rollup-win32-x64-gnu": "4.62.3", + "@rollup/rollup-win32-x64-msvc": "4.62.3", "fsevents": "~2.3.2" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, + "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", @@ -3272,29 +10126,99 @@ "url": "https://feross.org/support" } ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", "dependencies": { - "queue-microtask": "^1.2.2" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3307,23 +10231,131 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/socket.io-client": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", + "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sonic-boom": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", + "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -3331,216 +10363,621 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superstruct": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.4.tgz", + "integrity": "sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/thread-stream": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", + "integrity": "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==", + "license": "MIT", + "dependencies": { + "real-require": "^0.1.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/uint8arrays": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.0.tgz", + "integrity": "sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==", + "license": "MIT", + "dependencies": { + "multiformats": "^9.4.2" + } + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.29.0.tgz", + "integrity": "sha512-vamA8dGlzMwhpyYpQp9d8vka3o4D/yn5I7ez7Or+msDA4bZ8Uh+Zy91WvWf3I73gDAkFha9JcYRqm2li0Npfgg==", + "license": "MIT" + }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "license": "MIT" + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "punycode": "^2.1.0" } }, - "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==" - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "engines": { - "node": ">=6" + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" + "node-gyp-build": "^4.3.0" }, "engines": { - "node": ">=18" + "node": ">=6.14.2" } }, - "node_modules/tar/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "engines": { - "node": ">=18" + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" } }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/valtio": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.13.2.tgz", + "integrity": "sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==", + "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "derive-valtio": "0.1.0", + "proxy-compare": "2.6.0", + "use-sync-external-store": "1.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=12.20.0" }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "peerDependencies": { + "@types/react": ">=16.8", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "node_modules/valtio/node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "license": "MIT", "peerDependencies": { - "picomatch": "^3 || ^4" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/viem": { + "version": "2.55.10", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.55.10.tgz", + "integrity": "sha512-Q9Ba+/ma81U2M5o5P2AQ7Ux8rTIwmCZvUcr8rKdQ22bV0IBFHllM2m5gWDP8hFaUN2nH2oW3QG44amRazflYNQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.14.33", + "ws": "8.21.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" }, "peerDependenciesMeta": { - "picomatch": { + "typescript": { "optional": true } } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "node_modules/viem/node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", "engines": { - "node": ">=12" + "node": "^14.21.3 || >=16" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "@noble/hashes": "1.8.0" }, "engines": { - "node": ">=8.0" + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, + "node_modules/viem/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", "engines": { - "node": ">=18.12" + "node": "^14.21.3 || >=16" }, - "peerDependencies": { - "typescript": ">=4.8.4" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, + "node_modules/viem/node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" }, - "engines": { - "node": ">= 0.8.0" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "node_modules/viem/node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" }, - "engines": { - "node": ">=14.17" + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/typescript-eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz", - "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==", - "dev": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, + "node_modules/viem/node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/wevm" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } } }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, + "node_modules/viem/node_modules/ox": { + "version": "0.14.33", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.14.33.tgz", + "integrity": "sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ==", "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, { "type": "github", - "url": "https://github.com/sponsors/ai" + "url": "https://github.com/sponsors/wevm" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" }, "peerDependencies": { - "browserslist": ">= 4.21.0" + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" + "node_modules/viem/node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "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 + } } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", - "tinyglobby": "^0.2.14" + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -3603,28 +11040,51 @@ } } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "node_modules/wagmi": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/wagmi/-/wagmi-2.19.5.tgz", + "integrity": "sha512-RQUfKMv6U+EcSNNGiPbdkDtJwtuFxZWLmvDiQmjjBgkuPulUwDJsKhi7gjynzJdsx2yDqhHCXkKsbbfbIsHfcQ==", + "license": "MIT", + "dependencies": { + "@wagmi/connectors": "6.2.0", + "@wagmi/core": "2.22.1", + "use-sync-external-store": "1.4.0" + }, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, "peerDependencies": { - "picomatch": "^3 || ^4" + "@tanstack/react-query": ">=5.0.0", + "react": ">=18", + "typescript": ">=5.0.4", + "viem": "2.x" }, "peerDependenciesMeta": { - "picomatch": { + "typescript": { "optional": true } } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node_modules/webextension-polyfill": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", + "integrity": "sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==", + "license": "MPL-2.0" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/which": { @@ -3632,6 +11092,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -3642,32 +11103,251 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "license": "MIT", + "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 + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } }, "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==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz", + "integrity": "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/frontend/package.json b/frontend/package.json index 46d0756..5c16b7b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,9 +11,17 @@ }, "dependencies": { "@tailwindcss/vite": "^4.1.11", + "@tanstack/react-query": "^5.101.2", + "@web3modal/wagmi": "4.2.3", "react": "^19.1.0", "react-dom": "^19.1.0", - "tailwindcss": "^4.1.11" + "tailwindcss": "^4.1.11", + "viem": "^2.54.3", + "wagmi": "2.19.5" + }, + "overrides": { + "@wagmi/core": "2.22.1", + "@wagmi/connectors": "6.2.0" }, "devDependencies": { "@eslint/js": "^9.30.1", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4756a17..7e293a5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,6 @@ import { useApi } from './lib/api.hook'; import { SystemOverview } from './components/SystemOverview'; +import { GuardDelegation } from './components/GuardDelegation'; import { PositionsTable } from './components/PositionsTable'; import { CollateralTable } from './components/CollateralTable'; import { ChallengesTable } from './components/ChallengesTable'; @@ -9,13 +10,14 @@ import { HealthStatus } from './components/HealthStatus'; import { Footer } from './components/Footer'; function App() { - const { health, jusd, positions, collateral, challenges, minters } = useApi(); + const { health, jusd, positions, collateral, challenges, minters, guard } = useApi(); return (
+ diff --git a/frontend/src/components/GuardDelegation.tsx b/frontend/src/components/GuardDelegation.tsx new file mode 100644 index 0000000..4d3326c --- /dev/null +++ b/frontend/src/components/GuardDelegation.tsx @@ -0,0 +1,154 @@ +import { useState } from 'react'; +import { useAccount, useConfig } from 'wagmi'; +import { writeContract, waitForTransactionReceipt } from 'wagmi/actions'; +import { useWeb3Modal } from '@web3modal/wagmi/react'; +import { zeroAddress } from 'viem'; +import type { GuardResponse } from '../../../shared/types'; +import type { DataState } from '../lib/api.hook'; +import { colors, spacing } from '../lib/theme'; +import { formatPercent } from '../lib/formatters'; +import { AddressLink } from './AddressLink'; +import { WalletProvider } from './WalletProvider'; +import { DELEGATE_VOTE_TO_ABI } from '../lib/wagmi'; + +type TxState = 'idle' | 'pending' | 'success' | 'error'; + +// Read-only guard status + (only when a live signer exists) the wallet-backed Delegate action. The +// status half needs no wallet — it renders from the backend /guard endpoint. The Delegate half is wrapped +// in a lazy WalletProvider so a missing wallet config never white-screens the dashboard (see F2). +export function GuardDelegation({ guard }: { guard?: DataState }) { + const data = guard?.data; + + // Fail-loud on a real backend error (the endpoint 5xxs rather than faking 0%/false); render nothing + // while the first poll is in flight, matching the other read-only sections. + if (guard?.error) return
{guard.error}
; + if (!data) return null; + + const pct = parseFloat(data.votingPowerPct); + const pctColor = data.qualified ? colors.success : colors.critical; + + // F1: When the guard is disabled the backend returns signerAddress = ZeroAddress. delegateVoteTo(0x0) + // would just point the caller's delegation at nobody (a wasted tx that supports no signer), so there + // is NO active delegate path in that state — only the "Guard disabled" hint. Compare + // case-insensitively (backend emits the all-zero address lowercased). + const signerActive = data.enabled && data.signerAddress.toLowerCase() !== zeroAddress; + + return ( +
+

GUARD DELEGATION

+ +
+
+ } /> + {formatPercent(pct, 2)}} /> + = ${data.quorumPct}%)`} + value={ + {data.qualified ? 'Yes' : 'No'} + } + /> + {data.helperCount}} /> + + {data.gasEnough ? 'Ready' : 'Low gas'} ({data.gasBalance} cBTC) + + } + /> + {!data.enabled && Guard disabled} />} +
+ +
+

+ The guard auto-denies unwhitelisted minter proposals during their application period, before they can mint. + To act it needs at least {data.quorumPct}% of Equity voting power. Delegating your JUICE votes to the guard + signer is non-custodial and additive: your JUICE stays in your + wallet and your own voting power is unchanged. Unlike the usual (Governor-style) delegation that moves your + power to the delegate, here the guard is only allowed to also count your votes toward the quorum — the signer + holds no JUICE itself, so its veto power comes entirely from delegators. You can re-delegate at any time. +

+ + {signerActive ? ( + // Lazy wallet boundary around ONLY the Delegate UI. On a missing VITE_RPC_URL / VITE_WAGMI_ID the + // wagmi config build throws fail-loud; the fallback surfaces it right here instead of killing the + // dashboard (F2). + ( + wallet delegation unavailable: {message} + )} + > + + + ) : ( + + Delegation is unavailable while the guard is disabled. + + )} +
+
+
+ ); +} + +// Wallet-backed Delegate button. Rendered only inside WalletProvider, so the wagmi/Web3Modal hooks and +// the imperative writeContract action all have their config from context. +function GuardDelegateAction({ data }: { data: GuardResponse }) { + const config = useConfig(); + const account = useAccount(); + const { open } = useWeb3Modal(); + const [txState, setTxState] = useState('idle'); + const [txError, setTxError] = useState(); + + const handleDelegate = async () => { + if (!account.address) { + open(); + return; + } + try { + setTxState('pending'); + setTxError(undefined); + const hash = await writeContract(config, { + address: data.equityAddress as `0x${string}`, + abi: DELEGATE_VOTE_TO_ABI, + functionName: 'delegateVoteTo', + args: [data.signerAddress as `0x${string}`], + // F4: pin the target chain (the backend's chainId, on which equityAddress lives). The wagmi + // config is built for that chain id, so a wallet on the wrong network is forced to switch + // instead of silently writing to the wrong chain. + chainId: data.chainId, + }); + await waitForTransactionReceipt(config, { hash, confirmations: 1 }); + setTxState('success'); + } catch (error: unknown) { + const err = error as { shortMessage?: string; message?: string }; + setTxError(err.shortMessage || err.message || 'Delegation failed'); + setTxState('error'); + } + }; + + return ( +
+ + {txState === 'success' && Votes delegated to the guard signer.} + {txState === 'error' && txError && {txError}} +
+ ); +} + +function Metric({ label, value }: { label: string; value: React.ReactNode }) { + return ( +
+ {label} + {value} +
+ ); +} diff --git a/frontend/src/components/WalletProvider.tsx b/frontend/src/components/WalletProvider.tsx new file mode 100644 index 0000000..68b17ba --- /dev/null +++ b/frontend/src/components/WalletProvider.tsx @@ -0,0 +1,56 @@ +import { useState, type ReactNode } from 'react'; +import { WagmiProvider, type Config } from 'wagmi'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { createWeb3Modal } from '@web3modal/wagmi/react'; +import { buildWagmiConfig, getWagmiProjectId } from '../lib/wagmi'; + +// One QueryClient + one Web3Modal for the whole app lifetime, created lazily on first mount of the +// Delegate UI (never at the app entry point). wagmi v2 uses @tanstack/react-query internally; only the +// wallet stack needs it, so it lives here rather than around the read-only dashboard. +const queryClient = new QueryClient(); +let web3ModalInitialized = false; + +/** + * Lazy, self-contained wallet boundary around ONLY the Delegate interaction (F2). + * + * The wagmi config reads VITE_RPC_URL / VITE_WAGMI_ID fail-loud (no silent fallback). Building it HERE — + * inside a boundary, on mount — instead of at the app entry point keeps that hard error contained: on a + * missing var the Delegate UI renders the `fallback` hint while the rest of the read-only dashboard keeps + * running, rather than the whole page white-screening. On success the children run inside WagmiProvider + + * QueryClientProvider so the wagmi/Web3Modal hooks have their context. + * + * The config build + Web3Modal init run in the useState initializer so WagmiProvider gets its config + * synchronously on the first render. Both are idempotent (buildWagmiConfig memoizes; web3ModalInitialized + * guards createWeb3Modal), so React StrictMode's double-invoke is a safe no-op the second time. + */ +export function WalletProvider({ + chainId, + children, + fallback, +}: { + chainId: number; + children: ReactNode; + fallback: (message: string) => ReactNode; +}) { + const [state] = useState<{ config?: Config; error?: string }>(() => { + try { + const config = buildWagmiConfig(chainId); + if (!web3ModalInitialized) { + createWeb3Modal({ wagmiConfig: config, projectId: getWagmiProjectId(), enableAnalytics: false }); + web3ModalInitialized = true; + } + return { config }; + } catch (error: unknown) { + const message = error instanceof Error ? error.message : 'wallet configuration error'; + return { error: message }; + } + }); + + if (state.error || !state.config) return <>{fallback(state.error ?? 'wallet configuration error')}; + + return ( + + {children} + + ); +} diff --git a/frontend/src/lib/api.hook.ts b/frontend/src/lib/api.hook.ts index 29493fb..1c93496 100644 --- a/frontend/src/lib/api.hook.ts +++ b/frontend/src/lib/api.hook.ts @@ -1,5 +1,13 @@ import { useEffect, useMemo, useState } from 'react'; -import type { ChallengeResponse, CollateralResponse, JusdState, HealthResponse, MinterResponse, PositionResponse } from '../../../shared/types'; +import type { + ChallengeResponse, + CollateralResponse, + GuardResponse, + JusdState, + HealthResponse, + MinterResponse, + PositionResponse, +} from '../../../shared/types'; export interface DataState { data?: T; @@ -13,6 +21,7 @@ export interface UseApiResult { collateral?: DataState; challenges?: DataState; minters?: DataState; + guard?: DataState; } const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001'; @@ -25,6 +34,7 @@ export function useApi(): UseApiResult { const [collateral, setCollateral] = useState>(); const [challenges, setChallenges] = useState>(); const [minters, setMinters] = useState>(); + const [guard, setGuard] = useState>(); useEffect(() => { fetchAllData(); @@ -42,6 +52,7 @@ export function useApi(): UseApiResult { fetchData('collateral', setCollateral), fetchData('challenges', setChallenges), fetchData('minters', setMinters), + fetchData('guard', setGuard), ]); } @@ -70,7 +81,8 @@ export function useApi(): UseApiResult { collateral, challenges, minters, + guard, }), - [health, jusd, positions, collateral, challenges, minters] + [health, jusd, positions, collateral, challenges, minters, guard] ); } diff --git a/frontend/src/lib/wagmi.ts b/frontend/src/lib/wagmi.ts new file mode 100644 index 0000000..3ec60c4 --- /dev/null +++ b/frontend/src/lib/wagmi.ts @@ -0,0 +1,83 @@ +import { createConfig, http, type Config } from 'wagmi'; +import { injected, walletConnect, coinbaseWallet } from 'wagmi/connectors'; +import { defineChain } from 'viem'; + +// LAZY, memoized wagmi config for the Delegate button. Built on FIRST call (when the Delegate UI mounts +// inside WalletProvider), never at module load. Rationale: the config reads VITE_RPC_URL / VITE_WAGMI_ID +// fail-loud (a missing value is a hard error, never a silent fallback). If that throw fired at the entry +// point it would white-screen the whole read-only dashboard; deferring it here keeps the hard error +// contained to the Guard delegation section (see components/WalletProvider.tsx). The read path (signer +// address / voting-power % / gas) comes from the backend /guard endpoint and needs no wallet. + +let cachedConfig: Config | undefined; +let cachedChainId: number | undefined; +let cachedProjectId: string | undefined; + +export function buildWagmiConfig(chainId: number): Config { + if (cachedConfig) { + // A changing backend chain id is a real inconsistency (wrong network for equityAddress), not + // something to silently re-create a config for — fail loud so the Delegate UI surfaces it. + if (cachedChainId !== chainId) { + throw new Error(`wagmi config already built for chain ${cachedChainId}, cannot rebuild for ${chainId}`); + } + return cachedConfig; + } + + const rpcUrl = import.meta.env.VITE_RPC_URL; + if (!rpcUrl) throw new Error('VITE_RPC_URL is required (wallet/delegation http transport)'); + + const projectId = import.meta.env.VITE_WAGMI_ID; + if (!projectId) throw new Error('VITE_WAGMI_ID (WalletConnect project id) is required for the Delegate button'); + + cachedProjectId = projectId; + cachedChainId = chainId; + + // Citrea mainnet is not shipped by viem/wagmi (only citreaTestnet id 5115 exists, and this service + // dropped testnet). Define the chain locally from the backend-reported chain id so the frontend + // cannot drift from the network Equity actually lives on. Explorer URL matches formatExplorerUrl + // in frontend/src/lib/formatters.ts (https://citreascan.com). + const citrea = defineChain({ + id: chainId, + name: 'Citrea', + nativeCurrency: { name: 'cBTC', symbol: 'cBTC', decimals: 18 }, + rpcUrls: { + default: { http: [rpcUrl] }, + }, + blockExplorers: { + default: { name: 'Citreascan', url: 'https://citreascan.com' }, + }, + }); + + // Connector set mirrors the sibling dashboard: injected + WalletConnect + Coinbase. + // showQrModal:false because Web3Modal renders the connect UI (see WalletProvider createWeb3Modal). + cachedConfig = createConfig({ + chains: [citrea], + transports: { + [chainId]: http(rpcUrl), + }, + connectors: [ + injected({ shimDisconnect: true }), + walletConnect({ projectId, showQrModal: false }), + coinbaseWallet({ appName: 'JUSD Monitor' }), + ], + }); + return cachedConfig; +} + +// WalletConnect project id, valid only AFTER buildWagmiConfig() has run (same fail-loud source). +export function getWagmiProjectId(): string { + if (!cachedProjectId) throw new Error('buildWagmiConfig() must be called before getWagmiProjectId()'); + return cachedProjectId; +} + +// Single-function ABI fragment for the Delegate button — avoids pulling a backend-only package into the +// browser bundle. delegateVoteTo does NOT reduce the caller's own votes (non-custodial and additive). +export const DELEGATE_VOTE_TO_ABI = [ + { + type: 'function', + name: 'delegateVoteTo', + stateMutability: 'nonpayable', + inputs: [{ name: 'delegate', type: 'address' }], + outputs: [], + }, +] as const; diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts index 5565f7b..4cc3573 100644 --- a/frontend/src/vite-env.d.ts +++ b/frontend/src/vite-env.d.ts @@ -3,6 +3,8 @@ interface ImportMetaEnv { readonly VITE_API_BASE_URL?: string; readonly VITE_DEPLOYMENT_ENV?: string; + readonly VITE_RPC_URL?: string; + readonly VITE_WAGMI_ID?: string; } interface ImportMeta { From 193d280b870bffff128b5276d7bc697bfe5be3df Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 02:02:35 +0200 Subject: [PATCH 03/11] test(minter-guard): cover helper-set and error classification Unit-tests the two security-critical pure functions: computeHelpers (ordering contract, latest-wins re-delegation, multi-hop, cycle safety, seed union, signer exclusion) and classifyDenyError (permanent vs transient, every revert data nesting shape, empty data, no data at all, extraction precedence), plus a regression test pinning the quorum constant that Equity keeps private. Also registers a jest moduleNameMapper for the src/... path alias. Without it the runner cannot resolve provider.service's import, so the whole suite failed to load and the new tests would not have been runnable. README and .env.example now describe the guard as it behaves: the 2% quorum, the pre-send verification, the optional GUARD_HELPER_ADDRESS seed, bounded retries with a single escalation, empty-whitelist deny-by-default, and the GET /guard endpoint. --- .env.example | 21 +- README.md | 3 +- package.json | 3 + src/monitoringV2/minter-guard.logic.spec.ts | 258 ++++++++++++++++++++ 4 files changed, 279 insertions(+), 6 deletions(-) create mode 100644 src/monitoringV2/minter-guard.logic.spec.ts diff --git a/.env.example b/.env.example index 2902f60..fe2b319 100644 --- a/.env.example +++ b/.env.example @@ -67,15 +67,26 @@ GECKOTERMINAL_BASE_URL=http://pricing-proxy:8080/geckoterminal # committed whitelist (src/monitoringV2/config/whitelist.mainnet.json). # Bridge proposals are not exempted: the bridge type is inferred from a trivial # usd() view call and is therefore unsafe to exclude. +# Live status (signer, voting power, qualified, helpers, gas) is at GET /guard. # # GUARD_ENABLED true/false. Disables the watcher entirely if false. -# GUARD_PRIVATE_KEY Hex private key (0x...) of the signer. Must hold or be -# delegated enough voting power to pass checkQualified() -# on the JUSD reserve. -# GUARD_HELPER_ADDRESS Address passed as the single helper to denyMinter(). -# Use the equity holder that delegated to the signer. +# GUARD_PRIVATE_KEY Hex private key (0x...) of the signer. Missing or invalid +# while the guard is enabled is a hard config error +# (bootstrap aborts). The signer needs >= 2% of Equity +# voting power (own + delegated) to pass checkQualified(); +# without it the guard skips and pages instead of denying. +# GUARD_HELPER_ADDRESS OPTIONAL. Single static seed helper unioned with the +# helper set derived from indexed Delegation events. +# Only contributes if it holds JUICE AND delegates to +# the signer; a stale value makes the on-chain helper +# check revert with EMPTY revert data (bare requires in +# Equity.votesDelegated), so the guard drops the seed and +# pages. Leave unset to use the delegation graph alone. # GUARD_WHITELIST_FILE Absolute path to the whitelist JSON inside the # container (e.g. /app/src/monitoringV2/config/whitelist.mainnet.json). +# An empty whitelist is deny-by-default and is logged as +# a warning at startup (so an unmounted/truncated file is +# distinguishable). # GUARD_ENABLED=false # GUARD_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 # GUARD_HELPER_ADDRESS=0x0000000000000000000000000000000000000000 diff --git a/README.md b/README.md index 9787e5f..75e3c63 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The monitoring service continuously syncs blockchain data to provide real-time i - Collateral aggregation by token type 4. **Token Prices**: Fetches real-time prices from GeckoTerminal API with caching 5. **API Endpoints**: Serves data via REST API for frontend consumption -6. **Minter Guard**: Optional auto-deny watcher (opt-in via `GUARD_ENABLED=true`). At the end of every monitoring cycle it submits `denyMinter()` for any `PROPOSED` minter not on a committed whitelist (`src/monitoringV2/config/whitelist.mainnet.json`). Requires `GUARD_PRIVATE_KEY` and `GUARD_HELPER_ADDRESS`. See `.env.example`. +6. **Minter Guard**: Optional auto-deny watcher (opt-in via `GUARD_ENABLED=true`). At the end of every monitoring cycle it submits `denyMinter()` for any `PROPOSED` minter not on the committed whitelist (`src/monitoringV2/config/whitelist.mainnet.json`), bridges included. `denyMinter` is a shareholder veto (not an admin call): the signer needs ≥2% of Equity voting power (`Equity.checkQualified`), alone or via delegators, within the finite application window from `suggestMinter`. The guard verifies qualification and gas before sending (skips loudly and rate-limits those pages instead of submitting a doomed tx). Helpers are derived from indexed `Delegation` events; `GUARD_HELPER_ADDRESS` is an optional static seed for a named helper independent of the indexer. Permanently rejected denies (expired application period) stop retrying; other failures stop after a bounded attempt count with a single escalation. Live status is at `GET /guard` and rendered as the dashboard "Guard Delegation" section (where shareholders can delegate). An empty whitelist is deny-by-default and logged as a warning. See `.env.example`. ## Tech Stack @@ -88,6 +88,7 @@ Swagger documentation available at: `http://localhost:3001/swagger` | `/collateral` | Supported collateral tokens | | `/jusd` | JUSD supply and protocol stats | | `/minters` | Registered minters | +| `/guard` | Minter-guard live status | ## CoinGecko diff --git a/package.json b/package.json index 7fe3875..30fce45 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,9 @@ ], "rootDir": "src", "testRegex": ".*\\.spec\\.ts$", + "moduleNameMapper": { + "^src/(.*)$": "/$1" + }, "transform": { "^.+\\.(t|j)s$": "ts-jest" }, diff --git a/src/monitoringV2/minter-guard.logic.spec.ts b/src/monitoringV2/minter-guard.logic.spec.ts new file mode 100644 index 0000000..4faa4e4 --- /dev/null +++ b/src/monitoringV2/minter-guard.logic.spec.ts @@ -0,0 +1,258 @@ +import { ethers } from 'ethers'; +import { JuiceDollarABI, EquityABI } from '@juicedollar/jusd'; +import { computeHelpers, classifyDenyError, QUORUM_BPS } from './minter-guard.logic'; + +// Fixed 20-byte addresses (never random). Chosen so BigInt order is obvious when asserted. +const SIGNER = '0x00000000000000000000000000000000000000aa'; +const HELPER_A = '0x0000000000000000000000000000000000000001'; +const HELPER_B = '0x0000000000000000000000000000000000000002'; +const OTHER = '0x00000000000000000000000000000000000000bb'; + +// Sort-order fixtures: equal-length hex whose leading digits mix 0x0f / 0x10 / 0x90. +// BigInt ascending: ADDR_0F < ADDR_10 < ADDR_90 (asserted exactly — string sort is not the contract rule). +const ADDR_0F = '0x0f00000000000000000000000000000000000001'; +const ADDR_10 = '0x1000000000000000000000000000000000000001'; +const ADDR_90 = '0x9000000000000000000000000000000000000001'; + +// Seed that slots between ADDR_0F and ADDR_10 in BigInt order. +const SEED_BETWEEN = '0x0f80000000000000000000000000000000000001'; + +describe('computeHelpers', () => { + it('returns [] when nobody delegates to the signer', () => { + // Contract: helpers only include addresses that reach the signer via _canVoteFor. + expect(computeHelpers([{ from: HELPER_A, to: OTHER }], SIGNER)).toEqual([]); + }); + + it('collects direct delegators and excludes the signer itself', () => { + // Contract: require(current != sender) — signer must never appear in helpers. + const result = computeHelpers([{ from: HELPER_A, to: SIGNER }], SIGNER); + expect(result).toEqual([HELPER_A]); + expect(result).not.toContain(SIGNER); + }); + + it('includes multi-hop intermediates (a -> b -> signer yields both a and b)', () => { + // Equity._canVoteFor walks the chain recursively; intermediates count as helpers. + const result = computeHelpers( + [ + { from: HELPER_A, to: HELPER_B }, + { from: HELPER_B, to: SIGNER }, + ], + SIGNER + ); + expect(result).toEqual([HELPER_A, HELPER_B]); + }); + + it('latest-wins per from: re-delegation away from the signer drops that helper', () => { + // Input ordered ascending by block/logIndex — later entry overwrites for the same from. + const result = computeHelpers( + [ + { from: HELPER_A, to: SIGNER }, + { from: HELPER_A, to: OTHER }, + ], + SIGNER + ); + expect(result).toEqual([]); + }); + + it('latest-wins per from: re-delegation toward the signer adds that helper', () => { + const result = computeHelpers( + [ + { from: HELPER_A, to: OTHER }, + { from: HELPER_A, to: SIGNER }, + ], + SIGNER + ); + expect(result).toEqual([HELPER_A]); + }); + + it('is cycle-safe when a cycle includes the signer and still yields the peer helper', () => { + // Equity allows legal delegation cycles; visited set terminates the walk. + const result = computeHelpers( + [ + { from: SIGNER, to: HELPER_A }, + { from: HELPER_A, to: SIGNER }, + ], + SIGNER + ); + expect(result).toEqual([HELPER_A]); + }); + + it('sorts strictly ascending by BigInt(address), not by hex-string lexicography', () => { + // Equity._checkDuplicatesAndSorted rejects helpers[i] <= helpers[i-1] (uint160 order). + const result = computeHelpers( + [ + { from: ADDR_90, to: SIGNER }, + { from: ADDR_0F, to: SIGNER }, + { from: ADDR_10, to: SIGNER }, + ], + SIGNER + ); + expect(result).toEqual([ADDR_0F, ADDR_10, ADDR_90]); + }); + + it('lowercases and dedupes checksummed / mixed-case input', () => { + // All comparisons are on lowercased addresses; checksummed from/signer must not duplicate. + const checksummedFrom = '0xAbC0000000000000000000000000000000000001'; + const mixedSigner = '0xDeF00000000000000000000000000000000000Aa'; + const result = computeHelpers([{ from: checksummedFrom, to: mixedSigner }], mixedSigner); + expect(result).toEqual(['0xabc0000000000000000000000000000000000001']); + }); + + it('includes a seedHelpers entry not present in the delegation graph, in ascending position', () => { + // Optional GUARD_HELPER_ADDRESS seed is unioned, then sorted with graph helpers. + const result = computeHelpers( + [ + { from: ADDR_0F, to: SIGNER }, + { from: ADDR_10, to: SIGNER }, + ], + SIGNER, + [SEED_BETWEEN] + ); + expect(result).toEqual([ADDR_0F, SEED_BETWEEN, ADDR_10]); + }); + + it('does not duplicate a seedHelpers entry already present in the graph', () => { + // visited is a Set — seed union must not produce duplicates for _checkDuplicatesAndSorted. + const result = computeHelpers([{ from: HELPER_A, to: SIGNER }], SIGNER, [HELPER_A]); + expect(result).toEqual([HELPER_A]); + }); + + it('drops a seedHelpers entry equal to the signer', () => { + // Contract: require(current != sender) — signer seed is filtered, not an error. + const result = computeHelpers([{ from: HELPER_A, to: SIGNER }], SIGNER, [SIGNER]); + expect(result).toEqual([HELPER_A]); + expect(result).not.toContain(SIGNER); + }); + + it('treats seedHelpers undefined like graph-only', () => { + const graphOnly = computeHelpers([{ from: HELPER_A, to: SIGNER }], SIGNER); + expect(computeHelpers([{ from: HELPER_A, to: SIGNER }], SIGNER, undefined)).toEqual(graphOnly); + }); + + it('treats seedHelpers [] like graph-only', () => { + const graphOnly = computeHelpers([{ from: HELPER_A, to: SIGNER }], SIGNER); + expect(computeHelpers([{ from: HELPER_A, to: SIGNER }], SIGNER, [])).toEqual(graphOnly); + }); +}); + +describe('classifyDenyError', () => { + // Real ABIs so TooLate (JuiceDollar) and NotQualified (Equity) encode/decode correctly. + const iface = new ethers.Interface([...JuiceDollarABI, ...EquityABI]); + const tooLateData = iface.encodeErrorResult('TooLate', []); + const notQualifiedData = iface.encodeErrorResult('NotQualified', []); + + it('classifies TooLate() as permanent (payload at error.info.error.data)', () => { + // Application window closed — permanent; nesting shape exercises info.error.data extraction. + const error = { message: 'execution reverted', info: { error: { data: tooLateData } } }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('permanent'); + expect(result.label).toBe('TooLate'); + }); + + it('classifies NotQualified() as transient (payload at error.data)', () => { + // Under-quorum may recover via delegation; top-level data extraction path. + const error = { message: 'execution reverted', data: notQualifiedData }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('NotQualified'); + }); + + it('classifies top-level data 0x as EmptyRevert', () => { + // Bare requires in Equity.votesDelegated revert with no data — helper list rejected. + const error = { message: 'execution reverted', data: '0x' }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('EmptyRevert'); + expect(result.detail).toMatch(/helper/i); + }); + + it('classifies missing revert data message with no candidate data as EmptyRevert', () => { + const error = { message: 'call exception: missing revert data' }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('EmptyRevert'); + expect(result.detail).toMatch(/helper/i); + }); + + it('classifies error.info.error.data = 0x as EmptyRevert (info nesting path)', () => { + // Top-level data absent; extraction must walk error.info.error.data. + const error = { message: 'execution reverted', info: { error: { data: '0x' } } }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('EmptyRevert'); + expect(result.detail).toMatch(/helper/i); + }); + + it('classifies error.error.data = 0x as EmptyRevert (error.error nesting path)', () => { + // Top-level data absent; extraction must walk error.error.data. + const error = { message: 'execution reverted', error: { data: '0x' } }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('EmptyRevert'); + expect(result.detail).toMatch(/helper/i); + }); + + it('classifies undecodable non-empty data as Unknown with detail equal to the error message', () => { + // Wrong selector at error.error.data — third nesting shape carrying real (non-0x) payload. + const message = 'execution reverted: custom failure'; + const error = { message, error: { data: '0xdeadbeef' } }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('Unknown'); + expect(result.detail).toBe(message); + }); + + it('classifies a plain Error with no extractable data as NoRevertData (not EmptyRevert)', () => { + // Transport/account error is not a helper-list rejection — must not match /helper/i. + const error = new Error('nonce too low'); + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('NoRevertData'); + expect(result.detail).toContain('nonce too low'); + expect(result.detail).not.toMatch(/helper/i); + }); + + it('classifies CALL_EXCEPTION with no data candidate as EmptyRevert', () => { + // ethers v6 data-less on-chain revert: code CALL_EXCEPTION, no 0x… payload. + const error = { message: 'execution reverted', code: 'CALL_EXCEPTION' }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('EmptyRevert'); + expect(result.detail).toMatch(/helper/i); + }); + + it('classifies NETWORK_ERROR with no data as NoRevertData and names the code', () => { + const error = { message: 'could not detect network', code: 'NETWORK_ERROR' }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('NoRevertData'); + expect(result.detail).toContain('NETWORK_ERROR'); + expect(result.detail).not.toMatch(/helper/i); + }); + + it('classifies TooLate() at error.error.data as permanent (error.error nesting path)', () => { + // Coverage gap: error.error.data must carry real decodable custom-error data, not only 0x. + const error = { message: 'execution reverted', error: { data: tooLateData } }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('permanent'); + expect(result.label).toBe('TooLate'); + }); + + it('prefers top-level error.data over error.error.data when both are present', () => { + // Documented extraction order: error.data wins over error.info.error.data and error.error.data. + const error = { + message: 'execution reverted', + data: notQualifiedData, + error: { data: tooLateData }, + }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('NotQualified'); + }); +}); + +describe('QUORUM_BPS', () => { + it('is exactly 200n (Equity.sol private constant, absent from ABI)', () => { + expect(QUORUM_BPS).toBe(200n); + }); +}); From 2bce242d941e938e5c8550e368b2f04c392a747c Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 02:46:13 +0200 Subject: [PATCH 04/11] fix(minter-guard): close alerting and diagnosis gaps found in review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ten defects, each of which could either lose a page that a human must act on or point that human at the wrong cause. Alerting integrity: - A terminal escalation marked itself delivered without checking the boolean sendCriticalAlert returns, which the service documents explicitly. With Telegram down the page was lost for good, because `done` also removes the minter from the candidate set. The message is now retained and retried on the next cycle; when alerting is disabled outright there is nothing to retry and that is recorded instead. - The reassuring "helper seed rejected, cycle continues" page shared its cooldown with the critical "under quorum, nothing denied" page, so the former could silence the latter for an hour while a finite veto window ran out. Each page class now has its own timer. - A pre-check that cannot evaluate qualification at all (RPC failure, or a helper list rejected with no seed configured) only wrote a log line while every candidate went unchallenged. It now pages, rate-limited. - On a first boot or a reset database the probe ran before the first backfill and reported a quorum shortfall that was really an empty delegation graph. It now says it cannot assess yet. Diagnosis truthfulness: - ethers reports a mined, reverted transaction as CALL_EXCEPTION with data:null regardless of the real reason, so the empty-revert branch blamed the helper list for every failed confirmation. A mined revert is now its own class that names the realistic causes and the transaction hash. - The deny deadline came from the indexed row, which lags a confirmed deny. After a restart in that gap the guard re-sent, the contract reverted because denyMinter deletes the mapping entry, and the guard paged that an unwhitelisted minter was passing unchallenged — about a minter it had successfully denied. The deadline is now read from the chain, and a no-longer-pending minter is recorded without an alert. - GET /guard summed raw votes() over the helper set including the unvalidated seed. Since votes() is balance times time and says nothing about delegation, a funded seed pointing elsewhere made the dashboard report qualified while a real deny would revert. `qualified` now comes from votesDelegated, the value the contract itself checks, with a seed-less retry; the percentage stays a display estimate and says so. Robustness: - Confirmation waits ran sequentially with a per-transaction timeout only, so two slow denies could overrun the monitoring cadence. A per-cycle budget now bounds the total, and candidates that no longer fit are deferred rather than failed. - An invalid optional GUARD_HELPER_ADDRESS threw the config error class and therefore aborted the whole monitoring process, contradicting the stated contract that only the private key does that. It now disables the guard. - A seed equal to the signer was dropped silently, although the contract rejects it outright; it is reported at startup now. Also corrects three statements that were simply untrue: the dashboard claimed the signer holds no JUICE (nothing enforces that, and its own votes count), the event config claimed the guard depends on its alert flag for indexing, and the environment documentation overstated where the stale-seed protection applies. --- .env.example | 7 +- frontend/src/components/GuardDelegation.tsx | 4 +- src/monitoringV2/events.config.ts | 8 +- src/monitoringV2/minter-guard.logic.spec.ts | 48 +++ src/monitoringV2/minter-guard.logic.ts | 153 +++++---- src/monitoringV2/minter-guard.service.ts | 335 ++++++++++++++++---- src/monitoringV2/telegram.service.ts | 9 + 7 files changed, 434 insertions(+), 130 deletions(-) diff --git a/.env.example b/.env.example index fe2b319..5eb1c88 100644 --- a/.env.example +++ b/.env.example @@ -80,8 +80,11 @@ GECKOTERMINAL_BASE_URL=http://pricing-proxy:8080/geckoterminal # Only contributes if it holds JUICE AND delegates to # the signer; a stale value makes the on-chain helper # check revert with EMPTY revert data (bare requires in -# Equity.votesDelegated), so the guard drops the seed and -# pages. Leave unset to use the delegation graph alone. +# Equity.votesDelegated), so the per-cycle deny pre-check +# drops the seed for that cycle and pages. If set to the +# same address as the guard signer, it is filtered out +# client-side before any contract call. Leave unset to +# use the delegation graph alone. # GUARD_WHITELIST_FILE Absolute path to the whitelist JSON inside the # container (e.g. /app/src/monitoringV2/config/whitelist.mainnet.json). # An empty whitelist is deny-by-default and is logged as diff --git a/frontend/src/components/GuardDelegation.tsx b/frontend/src/components/GuardDelegation.tsx index 4d3326c..45933b4 100644 --- a/frontend/src/components/GuardDelegation.tsx +++ b/frontend/src/components/GuardDelegation.tsx @@ -65,8 +65,8 @@ export function GuardDelegation({ guard }: { guard?: DataState }) To act it needs at least {data.quorumPct}% of Equity voting power. Delegating your JUICE votes to the guard signer is non-custodial and additive: your JUICE stays in your wallet and your own voting power is unchanged. Unlike the usual (Governor-style) delegation that moves your - power to the delegate, here the guard is only allowed to also count your votes toward the quorum — the signer - holds no JUICE itself, so its veto power comes entirely from delegators. You can re-delegate at any time. + power to the delegate, here the guard is only allowed to also count your votes toward the quorum — the + signer's qualification is its own votes plus the votes delegated to it. You can re-delegate at any time.

{signerActive ? ( diff --git a/src/monitoringV2/events.config.ts b/src/monitoringV2/events.config.ts index f632afd..106876a 100644 --- a/src/monitoringV2/events.config.ts +++ b/src/monitoringV2/events.config.ts @@ -26,10 +26,10 @@ export const EVENT_CONFIG: Record = { // Trading events Trade: { severity: EventSeverity.LOW, enabled: false }, - // Delegation rows feed the minter-guard helper set (computeHelpers). They are persisted regardless of - // this flag — ingestion is driven by EVENT_SIGNATURES in constants.ts, while `enabled` only gates - // Telegram alerting (telegram.service.notifyEvent). Keep it false: a delegation is routine and must - // not page. Do not remove the entry — the guard depends on these rows being indexed. + // Delegation rows feed the minter-guard helper set (computeHelpers). This entry does NOT control that: + // persistence is driven by the Delegation entry in EVENT_SIGNATURES (constants.ts) and happens + // regardless of the flag below, which only gates Telegram alerting (telegram.service.notifyEvent). + // Keep it false — a delegation is routine and must not page. Delegation: { severity: EventSeverity.LOW, enabled: false }, // Roll event diff --git a/src/monitoringV2/minter-guard.logic.spec.ts b/src/monitoringV2/minter-guard.logic.spec.ts index 4faa4e4..47c50d2 100644 --- a/src/monitoringV2/minter-guard.logic.spec.ts +++ b/src/monitoringV2/minter-guard.logic.spec.ts @@ -221,6 +221,54 @@ describe('classifyDenyError', () => { expect(result.detail).toMatch(/helper/i); }); + it('classifies a mined receipt revert (data null + receipt) as RevertedOnChain, not EmptyRevert', () => { + // ethers v6 checkReceipt: status===0 always throws CALL_EXCEPTION with data:null and a receipt, + // regardless of the real revert reason — must not be diagnosed as a helper-list rejection. + const hash = '0x' + 'ab'.repeat(32); + const error = { + message: 'transaction execution reverted', + code: 'CALL_EXCEPTION', + data: null, + receipt: { status: 0, hash }, + }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('RevertedOnChain'); + // The detail may mention the helper list — it says the revert is NOT evidence of one. What it must + // never carry is the pre-send diagnosis wording, which would send the operator after the wrong cause. + expect(result.detail).not.toMatch(/unsorted|duplicated|does NOT delegate/i); + expect(result.detail).toMatch(/mined/i); + expect(result.detail).toContain(hash); + }); + + it('classifies the same CALL_EXCEPTION shape without a receipt as EmptyRevert (eth_call path)', () => { + // Pre-send eth_call / estimateGas bare require: no receipt, data-less CALL_EXCEPTION — + // that is the helper-list rejection surface, distinct from a mined status===0 receipt. + const error = { + message: 'transaction execution reverted', + code: 'CALL_EXCEPTION', + data: null, + }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('EmptyRevert'); + expect(result.detail).toMatch(/helper/i); + }); + + it('prefers decoded TooLate over RevertedOnChain when a mined receipt also carries error data', () => { + // Some providers attach both receipt and decodable data on a mined revert; permanent TooLate + // is strictly more useful than the generic mined-revert class, so decode wins. + const error = { + message: 'transaction execution reverted', + code: 'CALL_EXCEPTION', + data: tooLateData, + receipt: { status: 0, hash: '0x' + 'cd'.repeat(32) }, + }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('permanent'); + expect(result.label).toBe('TooLate'); + }); + it('classifies NETWORK_ERROR with no data as NoRevertData and names the code', () => { const error = { message: 'could not detect network', code: 'NETWORK_ERROR' }; const result = classifyDenyError(error, iface); diff --git a/src/monitoringV2/minter-guard.logic.ts b/src/monitoringV2/minter-guard.logic.ts index 0ebb1a8..c567ef5 100644 --- a/src/monitoringV2/minter-guard.logic.ts +++ b/src/monitoringV2/minter-guard.logic.ts @@ -5,7 +5,7 @@ export const QUORUM_BPS = 200n; export interface DenyErrorClass { kind: 'permanent' | 'transient'; - label: string; // 'TooLate' | 'NotQualified' | 'EmptyRevert' | 'NoRevertData' | a decoded error name | 'Unknown' + label: string; // 'TooLate' | 'NotQualified' | 'EmptyRevert' | 'RevertedOnChain' | 'NoRevertData' | a decoded error name | 'Unknown' detail: string; // human-readable diagnosis for logs + Telegram } @@ -78,9 +78,14 @@ export function computeHelpers(delegations: Array<{ from: string; to: string }>, /** * Classifies a failed denyMinter/votesDelegated error into permanent vs transient with a diagnosis. * + * Separates three failure surfaces: + * - eth_call / estimateGas bare require (empty revert data) — helper-list rejection before send, + * - mined receipt with status === 0 — ethers reports data:null even when a custom error fired, + * - client/transport/account failure — no on-chain revert marker at all. + * * Permanent (TooLate): the application window has closed — retrying forever is useless and would only - * burn gas + page. Transient: under-quorum, helper-list rejection, RPC blips, unknown — may recover - * next cycle (or after operator action) within the attempt cap. + * burn gas + page. Transient: under-quorum, helper-list rejection, mined-but-reverted, RPC blips, + * unknown — may recover next cycle (or after operator action) within the attempt cap. */ export function classifyDenyError(error: unknown, iface: ethers.Interface): DenyErrorClass { const err = error as any; @@ -96,7 +101,7 @@ export function classifyDenyError(error: unknown, iface: ethers.Interface): Deny } } - // Empty-data on-chain revert diagnosis (shared by steps 1–2 below). + // Empty-data on-chain revert diagnosis (shared by the eth_call empty-data steps below). // Equity.votesDelegated uses BARE requires that revert with NO data: // require(_checkDuplicatesAndSorted(helpers)) // require(current != sender) @@ -110,74 +115,102 @@ export function classifyDenyError(error: unknown, iface: ethers.Interface): Deny 'equals the signer, or does NOT delegate to the signer (this is NOT an RPC fault).', }; - // 1. A data candidate exists and is exactly empty hex ('0x' / '0X') -> real empty-data revert. - if (data === '0x' || data === '0X') { - return emptyRevert; + // 1. Non-empty data candidate (not bare '0x'): decode first. + // Precedence: a mined revert can carry BOTH a receipt and a decodable data payload (some providers + // attach data even on status===0). A permanent TooLate is strictly more useful than the generic + // RevertedOnChain class, so decode wins when both are present. + if (data !== undefined && data !== '0x' && data !== '0X') { + try { + const parsed = iface.parseError(data); + if (parsed) { + const name = parsed.name; + if (name === 'TooLate') { + return { + kind: 'permanent', + label: 'TooLate', + detail: + 'The application period has expired; denyMinter is impossible for anyone now — ' + + 'the minter will pass unless it is a bridge that can be handled otherwise.', + }; + } + if (name === 'NotQualified') { + return { + kind: 'transient', + label: 'NotQualified', + detail: + 'The signer is under the 2% Equity quorum; delegation can fix this at runtime ' + + '(delegateVoteTo the guard signer, or fund the signer with JUICE).', + }; + } + const args = parsed.args?.length ? ` args=${parsed.args.map((a) => String(a)).join(',')}` : ''; + return { + kind: 'transient', + label: name, + detail: `Decoded on-chain error ${name}${args}`, + }; + } + } catch { + // Not a decodable custom error — fall through to Unknown. + } + + return { + kind: 'transient', + label: 'Unknown', + detail: message, + }; } - // 2. No data candidate, but still recognisably an on-chain revert -> EmptyRevert. - // ethers v6 surfaces a data-less CALL_EXCEPTION / "missing revert data" this way, which is what - // distinguishes a real empty-data revert from a client/transport failure (no data, no revert marker). - if (data === undefined) { - const code = err?.code; - const isOnChainEmptyRevert = message.toLowerCase().includes('missing revert data') || code === 'CALL_EXCEPTION'; - if (isOnChainEmptyRevert) { - return emptyRevert; + // 2. Mined receipt revert (ethers checkReceipt: status===0 throws CALL_EXCEPTION with data:null + // and a receipt). Distinct from eth_call empty-data EmptyRevert — NOT a helper-list signal. + // kind stays transient: the authoritative on-chain window check at the start of the next cycle + // decides whether anything is still deniable. + const receipt = err?.receipt; + if (receipt !== null && typeof receipt === 'object') { + // ethers receipt uses `.hash`; some shapes expose `.transactionHash` instead. + let hashSuffix = ''; + if (typeof receipt.hash === 'string') { + hashSuffix = ` tx=${receipt.hash}`; + } else if (typeof receipt.transactionHash === 'string') { + hashSuffix = ` tx=${receipt.transactionHash}`; } - - // 3. No data candidate and no revert marker -> client/transport/account failure, not a helper-list - // rejection. This class exists so we do NOT attribute network/nonce/timeout faults to the helper - // list (which would send the operator after GUARD_HELPER_ADDRESS and trip the pre-check seed-drop). - const codeSuffix = typeof code === 'string' && code.length > 0 ? ` code=${code}` : ''; return { kind: 'transient', - label: 'NoRevertData', + label: 'RevertedOnChain', detail: - 'Not a contract rejection: client/transport/account-level failure ' + - '(nonce, funds at send time, timeout, network, or user rejection). ' + - `Raw error: ${message}${codeSuffix}`, + 'Transaction was mined and reverted; ethers reports data:null for mined reverts so the ' + + 'revert reason is not recoverable from the receipt. Realistic causes (most likely first): ' + + 'application window closed before inclusion (TooLate), qualification lost between pre-check ' + + 'and inclusion (NotQualified), or the application was already resolved by someone else. ' + + 'This is NOT evidence of a malformed helper list.' + + hashSuffix, }; } - // 4. Decodable custom error (TooLate on JuiceDollar, NotQualified on Equity, …). - // Reached only when a non-empty data candidate exists — TooLate/NotQualified can never be - // swallowed by steps 1–3 above. - try { - const parsed = iface.parseError(data); - if (parsed) { - const name = parsed.name; - if (name === 'TooLate') { - return { - kind: 'permanent', - label: 'TooLate', - detail: - 'The application period has expired; denyMinter is impossible for anyone now — ' + - 'the minter will pass unless it is a bridge that can be handled otherwise.', - }; - } - if (name === 'NotQualified') { - return { - kind: 'transient', - label: 'NotQualified', - detail: - 'The signer is under the 2% Equity quorum; delegation can fix this at runtime ' + - '(delegateVoteTo the guard signer, or fund the signer with JUICE).', - }; - } - const args = parsed.args?.length ? ` args=${parsed.args.map((a) => String(a)).join(',')}` : ''; - return { - kind: 'transient', - label: name, - detail: `Decoded on-chain error ${name}${args}`, - }; - } - } catch { - // Not a decodable custom error — fall through to Unknown. + // 3. A data candidate exists and is exactly empty hex ('0x' / '0X') -> real empty-data eth_call revert. + if (data === '0x' || data === '0X') { + return emptyRevert; } + // 4. No data candidate, but still recognisably an on-chain revert -> EmptyRevert. + // ethers v6 surfaces a data-less CALL_EXCEPTION / "missing revert data" this way, which is what + // distinguishes a real empty-data eth_call revert from a client/transport failure (no data, no + // revert marker). Reached only when there is no mined receipt (step 2 above). + const code = err?.code; + const isOnChainEmptyRevert = message.toLowerCase().includes('missing revert data') || code === 'CALL_EXCEPTION'; + if (isOnChainEmptyRevert) { + return emptyRevert; + } + + // 5. No data candidate and no revert marker -> client/transport/account failure, not a helper-list + // rejection. This class exists so we do NOT attribute network/nonce/timeout faults to the helper + // list (which would send the operator after GUARD_HELPER_ADDRESS and trip the pre-check seed-drop). + const codeSuffix = typeof code === 'string' && code.length > 0 ? ` code=${code}` : ''; return { kind: 'transient', - label: 'Unknown', - detail: message, + label: 'NoRevertData', + detail: + 'Not a contract rejection: client/transport/account-level failure ' + + '(nonce, funds at send time, timeout, network, or user rejection). ' + + `Raw error: ${message}${codeSuffix}`, }; } diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index df66521..30bf6a0 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -18,8 +18,19 @@ import { GuardResponse } from '../../shared/types'; // and it retries next cycle within the attempt cap. const DENY_CONFIRM_TIMEOUT_MS = 180_000; -// Cooldown between repeated skip pages (in-memory only, reset on restart). Two independent timers so a -// votes-skip page and a gas-skip page never suppress each other. +// Per-cycle budget for sequential tx.wait confirmations across all candidates. Must stay below the +// EVERY_5_MINUTES (300s) cadence so later watchers in the same cycle are not delayed and the next +// tick does not skip on isRunning. Invariant: total confirmation wait per cycle stays under this budget. +const DENY_CYCLE_CONFIRM_BUDGET_MS = 240_000; + +// Floor: if remaining confirmation budget is below this, defer remaining candidates to the next cycle +// rather than starting a deny whose confirmation cannot complete usefully within the cadence. +// A deferral is not a failure — do not mark done and do not page. +const DENY_CONFIRM_MIN_USEFUL_MS = 30_000; + +// Cooldown between repeated skip pages (in-memory only, reset on restart). Independent timers per kind +// so no page class may suppress another (e.g. a reassuring seed-drop page must never swallow the critical +// under-quorum page, and a precheck failure must not share a timer with votes/gas/seed). const SKIP_ALERT_COOLDOWN_MS = 60 * 60 * 1000; // Rough denyMinter() gas ceiling used for the balance floor (pre-check) and the read-only /guard status @@ -37,6 +48,16 @@ const DENY_TOOLATE_BUFFER_SECONDS = 60n; // 5-minute cycle. Permanent failures (TooLate) and the cap both set done=true. const MAX_DENY_ATTEMPTS = 3; +type SkipAlertKind = 'votes' | 'gas' | 'seed' | 'precheck'; + +interface DenyStateEntry { + attempts: number; + done?: boolean; + alerted?: boolean; + /** Terminal page text awaiting confirmed Telegram delivery; cleared when alerted becomes true. */ + pendingAlert?: string; +} + interface Whitelist { minters: string[]; } @@ -75,11 +96,16 @@ export class MinterGuardService { private helperSeed: string[] = []; // Per-minter attempt/terminal state for this process lifetime. done=true means stop attempting // (confirmed deny, permanent rejection, or attempt cap reached). alerted=true means the terminal - // FAILED page was already sent. - private readonly denyState = new Map(); - // In-memory skip-alert rate limiting (see SKIP_ALERT_COOLDOWN_MS). - private lastVotesSkipAlertAt = 0; - private lastGasSkipAlertAt = 0; + // page was delivered (or telegram is disabled so there is nothing to retry). pendingAlert holds the + // message when delivery failed and must be retried next cycle. + private readonly denyState = new Map(); + // In-memory skip-alert rate limiting (see SKIP_ALERT_COOLDOWN_MS): one independent timer per kind. + private readonly lastSkipAlertAt: Record = { + votes: 0, + gas: 0, + seed: 0, + precheck: 0, + }; // Built once from JuiceDollar + Equity ABIs so both TooLate and NotQualified decode. private denyErrorInterface?: ethers.Interface; @@ -116,14 +142,28 @@ export class MinterGuardService { this.signerAddress = signerAddress; // GUARD_HELPER_ADDRESS is OPTIONAL: when set it seeds computeHelpers with an explicitly named helper, - // independent of the indexer. A typo is a config error (checksum/format), not a silent ignore. + // independent of the indexer. A typo is a recoverable init error (plain Error, not GuardConfigError) + // so the caller disables the guard and pages once while the rest of monitoring keeps running — + // an optional convenience value must never be able to take monitoring down. const helper = this.config.guardHelperAddress; if (helper) { try { - this.helperSeed = [ethers.getAddress(helper)]; + const checksummed = ethers.getAddress(helper); + // Equity.votesDelegated requires current != sender; a seed equal to the signer is dropped + // inside computeHelpers with no diagnosis. Detect and log here so misconfig is never silent. + if (checksummed.toLowerCase() === signerAddress.toLowerCase()) { + this.logger.error( + `MinterGuard: GUARD_HELPER_ADDRESS equals the guard signer (${checksummed}) — ` + + `Equity.votesDelegated rejects that outright. Ignoring seed; helper set comes from the ` + + `Delegation graph alone. Fix GUARD_HELPER_ADDRESS.` + ); + this.helperSeed = []; + } else { + this.helperSeed = [checksummed]; + } } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); - throw new GuardConfigError(`GUARD_HELPER_ADDRESS is invalid: ${errorMsg}`); + throw new Error(`GUARD_HELPER_ADDRESS is invalid: ${errorMsg}`); } } else { this.logger.log('MinterGuard: GUARD_HELPER_ADDRESS unset — helper set comes from the Delegation graph alone'); @@ -180,6 +220,16 @@ export class MinterGuardService { try { const equity = new ethers.Contract(equityAddress, EquityABI, this.providerService.multicallProvider); const delegations = await this.eventsRepo.getDelegations(); + // Before the first processBlocks backfill (or on a reset DB) getDelegations() is empty. With no + // configured seed the additive power is only the signer's own — paging "under 2% quorum" at boot + // would be a false alarm. The per-cycle pre-check will page if it matters once candidates exist. + if (delegations.length === 0 && this.helperSeed.length === 0) { + this.logger.warn( + 'MinterGuard startup: Delegation graph has not been backfilled yet; qualification cannot be ' + + 'assessed. The per-cycle pre-check will page if under-quorum matters once candidates exist.' + ); + return; + } const helpers = computeHelpers(delegations, signerAddress, this.helperSeed); const voteResults = await this.providerService.callBatch([ () => equity.totalVotes(), @@ -232,6 +282,44 @@ export class MinterGuardService { } } + /** + * Deliver a terminal (per-minter) critical page, honouring sendCriticalAlert's return value. + * On confirmed delivery sets alerted=true and clears pendingAlert. On failure keeps pendingAlert + * so the next cycle can retry — prevents "done + alerted with zero notification" when Telegram is down. + * When alerts are disabled entirely there is nothing to retry: alerted=true without pendingAlert; + * logger.error is the durable record (not swallowing). + */ + private async deliverTerminalAlert(addrLc: string, state: DenyStateEntry, message: string): Promise { + if (state.alerted) return; + + // Telegram disabled: nothing to deliver to and nothing to retry — error log is the record. + if (!this.telegramService.alertsEnabled) { + this.denyState.set(addrLc, { ...state, alerted: true, pendingAlert: undefined }); + this.logger.error(`MinterGuard terminal page not deliverable (telegram disabled; not retained for retry): ${message}`); + return; + } + + const delivered = await this.telegramService.sendCriticalAlert(message); + if (delivered) { + this.denyState.set(addrLc, { ...state, alerted: true, pendingAlert: undefined }); + } else { + this.denyState.set(addrLc, { ...state, alerted: false, pendingAlert: message }); + this.logger.error(`MinterGuard terminal page could not be delivered; will retry next cycle for ${addrLc}: ${message}`); + } + } + + /** + * Re-send any terminal pages that failed delivery on a previous cycle. Notification-only — + * never re-sends a deny transaction. Runs even when there are no current candidates. + */ + private async retryPendingAlerts(): Promise { + for (const [addrLc, state] of this.denyState) { + if (state.pendingAlert && state.alerted !== true) { + await this.deliverTerminalAlert(addrLc, state, state.pendingAlert); + } + } + } + /** * Called by MonitoringService after syncMinters(). Iterates PROPOSED minters and denies any not on * the whitelist that are not already terminal in denyState. Runs a signer-global votes/gas pre-check @@ -241,6 +329,10 @@ export class MinterGuardService { const { signerKey, signerAddress, jusdAddress } = this; if (!this.enabled || !signerKey || !signerAddress || !jusdAddress || !this.denyErrorInterface) return; + // Retry undelivered terminal pages first (even when there are no candidates this cycle). + // Notification-only — does not re-send deny transactions. + await this.retryPendingAlerts(); + const minters = await this.minterRepo.findAll(); // Denies BRIDGE-typed proposals too: bridge type is inferred from a single // `usd()` view call, which is trivial to mimic in a malicious contract. @@ -267,42 +359,76 @@ export class MinterGuardService { const juiceDollar = new ethers.Contract(jusdAddress, JuiceDollarABI, wallet); + // Track confirmation wait spent this cycle so sequential timeouts cannot overrun the cron cadence. + let confirmBudgetSpentMs = 0; + let deferDeniesLogged = false; + for (const minter of candidates) { const address = ethers.getAddress(minter.address); const addrLc = address.toLowerCase(); // Just-in-time TooLate guard. denyMinter reverts TooLate once block.timestamp > - // minters[_minter] (applicationTimestamp + applicationPeriod). Sending into that margin only - // burns gas — mark done so we never retry a window that can never succeed again. + // minters[_minter] (the on-chain validityStart set by suggestMinter). The authoritative + // deadline is the chain mapping, not the indexed row (which stays PROPOSED until the next + // event sync). Reading DB-derived deadline caused false "passing unchallenged" pages after a + // confirmed deny + process restart, and double-send of already-mined denies. let latestBlock: ethers.Block | null = null; + let onChainDeadline: bigint; try { latestBlock = await this.providerService.provider.getBlock('latest'); + onChainDeadline = BigInt(await juiceDollar.minters(address)); } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); - this.logger.error(`MinterGuard skip ${address}: failed to read latest block for TooLate pre-check: ${errorMsg}`); + this.logger.error(`MinterGuard skip ${address}: failed to read latest block/minters for TooLate pre-check: ${errorMsg}`); continue; } if (!latestBlock) { this.logger.error(`MinterGuard skip ${address}: provider.getBlock('latest') returned null for TooLate pre-check`); continue; } - const deadline = BigInt(minter.applicationTimestamp) + BigInt(minter.applicationPeriod); - if (BigInt(latestBlock.timestamp) + DENY_TOOLATE_BUFFER_SECONDS >= deadline) { + // on-chain 0 means the minter is no longer pending (already denied, or application resolved + // otherwise) while the indexed row is simply behind — the false-alarm class we remove: no page. + if (onChainDeadline === 0n) { + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + this.denyState.set(addrLc, { ...prev, done: true }); + this.logger.warn( + `MinterGuard skip ${address}: on-chain minters(address)=0 (already denied or resolved; ` + + `indexed row still PROPOSED) — marking done without alert to avoid false "passing unchallenged" page` + ); + continue; + } + if (BigInt(latestBlock.timestamp) + DENY_TOOLATE_BUFFER_SECONDS >= onChainDeadline) { this.logger.warn( `MinterGuard skip ${address}: deny window closed or about to close ` + - `(block ts ${latestBlock.timestamp} + ${DENY_TOOLATE_BUFFER_SECONDS}s buffer >= deadline ${deadline})` + `(block ts ${latestBlock.timestamp} + ${DENY_TOOLATE_BUFFER_SECONDS}s buffer >= on-chain deadline ${onChainDeadline})` ); const prev = this.denyState.get(addrLc) || { attempts: 0 }; - this.denyState.set(addrLc, { ...prev, done: true }); - // Alert ONCE that an unwhitelisted minter is passing unchallenged. + const next: DenyStateEntry = { ...prev, done: true }; + this.denyState.set(addrLc, next); + // Alert ONCE that an unwhitelisted minter is passing unchallenged (honour delivery return). if (!prev.alerted) { - this.denyState.set(addrLc, { ...prev, done: true, alerted: true }); - await this.telegramService.sendCriticalAlert( + const alertMsg = `⚠️ *Unwhitelisted minter passing unchallenged*\n\n` + - `Address: \`${address}\`\n` + - `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + - `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.` + `Address: \`${address}\`\n` + + `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + + `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.`; + await this.deliverTerminalAlert(addrLc, next, alertMsg); + } + continue; + } + + // Remaining confirmation budget too small to be useful: defer new denies (not fail) so later + // watchers still run and the next cycle can finish the rest. Do not mark done; do not page. + // JIT closed-window / already-resolved paths above still run for every candidate. + const remainingBudgetMs = DENY_CYCLE_CONFIRM_BUDGET_MS - confirmBudgetSpentMs; + if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { + if (!deferDeniesLogged) { + this.logger.warn( + `MinterGuard: confirmation budget exhausted (${confirmBudgetSpentMs}ms spent of ` + + `${DENY_CYCLE_CONFIRM_BUDGET_MS}ms); deferring remaining deny candidate(s) including ${address} ` + + `to the next cycle (not marked done, no page — deferral is not a failure).` ); + deferDeniesLogged = true; } continue; } @@ -310,27 +436,44 @@ export class MinterGuardService { const message = `Auto-deny by minter-guard: not in whitelist (${this.config.environment ?? 'unknown'}/${this.config.chain ?? 'unknown'})`; let confirmed = false; let txHash: string | undefined; + const waitTimeoutMs = Math.min(DENY_CONFIRM_TIMEOUT_MS, remainingBudgetMs); try { const tx = await juiceDollar.denyMinter(address, helpers, message); txHash = tx.hash; this.logger.warn(`Submitted denyMinter for ${address}: tx=${tx.hash}`); - // Bounded wait: on timeout this throws and the minter is left unmarked to retry next cycle. - // A retry sends a fresh-nonce tx (it does not replace a stuck one); under sustained mempool/gas - // pathology the deny may not land, but the terminal FAILED alert then pages a human — an accepted - // limitation of the opt-in guard, deliberately not carrying nonce/replacement state. - const receipt = await tx.wait(1, DENY_CONFIRM_TIMEOUT_MS); + // Bounded wait within the per-cycle confirmation budget: on timeout this throws and the + // minter is left unmarked to retry next cycle. A retry sends a fresh-nonce tx (it does not + // replace a stuck one); under sustained mempool/gas pathology the deny may not land, but the + // terminal FAILED alert then pages a human — an accepted limitation of the opt-in guard, + // deliberately not carrying nonce/replacement state. + const waitStartedAt = Date.now(); + let receipt: ethers.ContractTransactionReceipt | null; + try { + receipt = await tx.wait(1, waitTimeoutMs); + } finally { + confirmBudgetSpentMs += Date.now() - waitStartedAt; + } + if (!receipt) { + // wait resolved without a receipt (should be rare with confirms=1); treat as unconfirmed for retry. + throw new Error(`denyMinter tx.wait returned null for ${address} (tx=${txHash})`); + } confirmed = true; // Confirmed on-chain from here — mark before alerting so a Telegram hiccup cannot cause a double deny. const prev = this.denyState.get(addrLc) || { attempts: 0 }; this.denyState.set(addrLc, { attempts: prev.attempts, done: true }); this.logger.warn(`denyMinter confirmed for ${address}: block=${receipt.blockNumber}`); - await this.telegramService.sendCriticalAlert( + const delivered = await this.telegramService.sendCriticalAlert( `🛡️ *Minter auto-denied*\n\n` + `Address: \`${address}\`\n` + `Tx: \`${txHash}\`\n` + `Block: ${receipt.blockNumber}\n` + `Message: ${message}` ); + // Success page does not need pendingAlert retry (deny is already on-chain and marked done), + // but log loud when delivery fails so the gap is visible. + if (!delivered) { + this.logger.error(`MinterGuard success page could not be delivered for ${address} (tx=${txHash}); deny is on-chain`); + } } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); if (confirmed) { @@ -344,7 +487,8 @@ export class MinterGuardService { const prev = this.denyState.get(addrLc) || { attempts: 0 }; const attempts = prev.attempts + 1; const done = classification.kind === 'permanent' || attempts >= MAX_DENY_ATTEMPTS; - this.denyState.set(addrLc, { attempts, done, alerted: prev.alerted }); + const next: DenyStateEntry = { attempts, done, alerted: prev.alerted, pendingAlert: prev.pendingAlert }; + this.denyState.set(addrLc, next); this.logger.error( `Failed to deny minter ${address} (attempt ${attempts}/${MAX_DENY_ATTEMPTS}, ` + @@ -355,20 +499,20 @@ export class MinterGuardService { // FAILED critical alert ONLY on the terminal state for this minter, and only once. // NotQualified must not produce a per-attempt page — the precheck owns that page. // Non-terminal transient failures log at error level and stay silent on Telegram. + // windowClosed uses the on-chain deadline so remedy text stays truthful. if (done && !prev.alerted) { - this.denyState.set(addrLc, { attempts, done: true, alerted: true }); - const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= deadline; + const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= onChainDeadline; const remedy = windowClosed ? 'The application period has ended — denyMinter is impossible; challenge/handle the minter otherwise if needed.' : 'Manual denyMinter() required before the application period ends.'; - await this.telegramService.sendCriticalAlert( + const alertMsg = `⚠️ *Minter auto-deny FAILED*\n\n` + - `Address: \`${address}\`\n` + - `Class: ${classification.label} (${classification.kind})\n` + - `Detail: ${classification.detail}\n` + - `Attempts: ${attempts}/${MAX_DENY_ATTEMPTS}\n\n` + - remedy - ); + `Address: \`${address}\`\n` + + `Class: ${classification.label} (${classification.kind})\n` + + `Detail: ${classification.detail}\n` + + `Attempts: ${attempts}/${MAX_DENY_ATTEMPTS}\n\n` + + remedy; + await this.deliverTerminalAlert(addrLc, { ...next, done: true }, alertMsg); } } } @@ -377,9 +521,16 @@ export class MinterGuardService { /** * Signer-global deny pre-check, run once per cycle before any denyMinter(). Returns { ok, helpers }: - * - ok=false => SKIP all denies this cycle (under quorum, out of gas, or a transient read error). - * - Any thrown chain error is caught here and turned into a skip — nothing escapes to the cycle and - * no doomed reverting tx is ever sent. Votes/gas skips page a human via rate-limited critical alerts. + * - ok=true => helpers are ready; proceed to per-candidate deny loop. + * - ok=false => SKIP all denies this cycle. Paths that produce ok=false: + * * under quorum (votes) — rate-limited 'votes' page when candidates exist, + * * low gas — rate-limited 'gas' page when candidates exist, + * * seed rejected by votesDelegated — rate-limited 'seed' page, then continues seed-less if retry works + * (if seed-less also fails, falls into the generic catch), + * * unusable pre-check (RPC / votesDelegated still failing) — rate-limited 'precheck' page when + * candidates.length > 0 so unchallenged PROPOSED minters are never silent that cycle. + * - Any thrown chain error is caught and turned into a skip — nothing escapes to the cycle and + * no doomed reverting tx is ever sent. */ private async runDenyPrecheck( signerAddress: string, @@ -400,10 +551,11 @@ export class MinterGuardService { // both validates the helper list and measures qualification. estimateGas below would itself revert // on NotQualified, so checking votes first keeps the two skip causes distinct. // - // SEED-DROP RETRY: a stale GUARD_HELPER_ADDRESS (does not delegate to the signer / equals the - // signer) poisons every votesDelegated read with an empty-data revert that reads like an RPC - // fault. If EmptyRevert fires and we have a seed, retry once seed-less so a config typo cannot - // silently disable the guard forever. + // SEED-DROP RETRY: a stale GUARD_HELPER_ADDRESS that does not delegate to the signer (or an + // otherwise rejected helper list) poisons every votesDelegated read with an empty-data revert that + // reads like an RPC fault. If EmptyRevert fires and we have a seed, retry once seed-less so a + // config typo cannot silently disable the guard forever. (A seed equal to the signer is detected + // and cleared in initialize — it never reaches this path.) let totalVotes: bigint; let delegatedVotes: bigint; try { @@ -419,17 +571,17 @@ export class MinterGuardService { helpers = seedLess; this.logger.error( `MinterGuard: GUARD_HELPER_ADDRESS ${this.helperSeed[0]} rejected by votesDelegated ` + - `(EmptyRevert — does not delegate to the signer, or equals the signer). ` + + `(EmptyRevert — does not delegate to the signer, or otherwise rejected helper list). ` + `Continuing this cycle with the seed-less helper set; fix the env value.` ); await this.maybeAlertSkip( - 'votes', + 'seed', `⚠️ *Minter guard GUARD_HELPER_ADDRESS rejected*\n\n` + `Seed: \`${this.helperSeed[0]}\`\n` + `Signer: \`${signerAddress}\`\n\n` + `votesDelegated reverted with empty data on the seed helper (it does not delegate to ` + - `the signer, or equals the signer). Cycle continues with the Delegation-graph helpers only. ` + - `Fix GUARD_HELPER_ADDRESS.` + `the signer, or the helper list is otherwise rejected). Cycle continues with the ` + + `Delegation-graph helpers only. Fix GUARD_HELPER_ADDRESS.` ); } catch { // Retry also failed — rethrow the original so the outer catch skips the cycle. @@ -525,22 +677,37 @@ export class MinterGuardService { return { ok: true, helpers }; } catch (error) { - // Transient RPC error / votesDelegated revert on a momentarily stale helper graph: skip this - // cycle (logged, not silently swallowed). It retries next cycle. runWatcher also isolates this, - // but the explicit catch guarantees no doomed tx is sent this cycle. + // Unusable pre-check (RPC failure, or votesDelegated still failing with no usable seed-less set): + // skip this cycle (logged, not silently swallowed). When candidates exist, also page under the + // independent 'precheck' kind so unchallenged PROPOSED minters are never only an app log line. const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); this.logger.error(`MinterGuard pre-check failed, skipping deny this cycle: ${errorMsg}`, error?.stack || error); + if (candidates.length > 0) { + const classification = this.denyErrorInterface + ? classifyDenyError(error, this.denyErrorInterface) + : { label: 'Unknown', detail: errorMsg }; + await this.maybeAlertSkip( + 'precheck', + `⚠️ *Minter guard pre-check failed — qualification unknown*\n\n` + + `${candidates.length} unwhitelisted PROPOSED minter(s) left undenied this cycle.\n` + + `Qualification could not be determined (class: ${classification.label}).\n` + + `Detail: ${classification.detail}\n\n` + + `The guard will retry next cycle; investigate RPC / helper set if this persists.` + ); + } return { ok: false, helpers: [] }; } } - /** Rate-limited skip page: at most one per kind per SKIP_ALERT_COOLDOWN_MS (in-memory, reset on restart). */ - private async maybeAlertSkip(kind: 'votes' | 'gas', message: string): Promise { + /** + * Rate-limited skip page: at most one per kind per SKIP_ALERT_COOLDOWN_MS (in-memory, reset on restart). + * Kinds have independent timers so one class of page cannot suppress another. + */ + private async maybeAlertSkip(kind: SkipAlertKind, message: string): Promise { const nowMs = Date.now(); - const lastAt = kind === 'votes' ? this.lastVotesSkipAlertAt : this.lastGasSkipAlertAt; + const lastAt = this.lastSkipAlertAt[kind]; if (nowMs - lastAt < SKIP_ALERT_COOLDOWN_MS) return; - if (kind === 'votes') this.lastVotesSkipAlertAt = nowMs; - else this.lastGasSkipAlertAt = nowMs; + this.lastSkipAlertAt[kind] = nowMs; await this.telegramService.sendCriticalAlert(message); } @@ -573,8 +740,11 @@ export class MinterGuardService { const provider = this.providerService.provider; const equity = new ethers.Contract(equityAddress, EquityABI, this.providerService.multicallProvider); - // Additive, revert-proof voting power for DISPLAY: votes(signer) + Σ votes(helper) equals - // votesDelegated for a valid helper set, but plain votes() never reverts on a momentarily stale graph. + // Additive, revert-proof voting power for DISPLAY (votingPowerPct): votes(signer) + Σ votes(helper). + // This percentage is a display estimate over the derived helper set; `qualified` below is the + // contract's own verdict via votesDelegated — the two can disagree when a helper is invalid + // (e.g. a seed that does not delegate to the signer makes the additive sum look healthy while a + // real deny would revert). const delegations = await this.eventsRepo.getDelegations(); const helpers = computeHelpers(delegations, signerAddress, this.helperSeed); const voteResults = await this.providerService.callBatch([ @@ -583,7 +753,48 @@ export class MinterGuardService { ]); const totalVotes: bigint = BigInt(voteResults[0]); const votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); - const qualified = votingPower * 10000n >= QUORUM_BPS * totalVotes; + + // Contract's own verdict for `qualified` — exact value checkQualified uses. + // On-chain rejection of the helper list (EmptyRevert / decoded contract error): retry seed-less once. + // NoRevertData is a transport/client failure — rethrow so the endpoint 5xxes (fail-loud). + const denyIface = this.denyErrorInterface; + if (!denyIface) throw new Error('MinterGuard getStatus: denyErrorInterface missing while guard is enabled'); + + let activeHelpers = helpers; + let qualified: boolean; + try { + const delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, helpers)); + qualified = delegatedVotes * 10000n >= QUORUM_BPS * totalVotes; + } catch (error) { + const classification = classifyDenyError(error, denyIface); + if (classification.label === 'NoRevertData') { + // Transport/client failure, not an on-chain rejection — fail loud for the endpoint. + throw error; + } + // EmptyRevert or decoded contract error: helper list rejected on-chain. + if (this.helperSeed.length > 0) { + const seedLess = computeHelpers(delegations, signerAddress); + try { + const delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, seedLess)); + activeHelpers = seedLess; + qualified = delegatedVotes * 10000n >= QUORUM_BPS * totalVotes; + } catch (retryError) { + const retryClass = classifyDenyError(retryError, denyIface); + if (retryClass.label === 'NoRevertData') throw retryError; + // Still rejected: a real deny would also revert — report qualified:false truthfully. + this.logger.warn( + `MinterGuard getStatus: votesDelegated rejected helper set (${retryClass.label}): ${retryClass.detail}` + ); + qualified = false; + activeHelpers = seedLess; + } + } else { + this.logger.warn( + `MinterGuard getStatus: votesDelegated rejected helper set (${classification.label}): ${classification.detail}` + ); + qualified = false; + } + } // Gas status: model denyMinter() cost with a fixed gas ceiling * live fee (see DENY_GAS_ESTIMATE). const balance: bigint = await provider.getBalance(signerAddress); @@ -598,7 +809,7 @@ export class MinterGuardService { votingPowerPct: formatVotingPowerPct(votingPower, totalVotes), quorumPct: Number(QUORUM_BPS) / 100, qualified, - helperCount: helpers.length, + helperCount: activeHelpers.length, gasBalance: ethers.formatEther(balance), estimatedDenyCost: ethers.formatEther(estimatedDenyCost), gasEnough: balance >= estimatedDenyCost, diff --git a/src/monitoringV2/telegram.service.ts b/src/monitoringV2/telegram.service.ts index b1cea5a..cecd5a7 100644 --- a/src/monitoringV2/telegram.service.ts +++ b/src/monitoringV2/telegram.service.ts @@ -176,6 +176,15 @@ export class TelegramService implements OnModuleInit, OnModuleDestroy { return [env, chain].filter((part) => part.length > 0).join(' '); } + /** + * Whether critical-alert delivery is configured and enabled (token + groups path present). + * Callers use this to distinguish "nothing to deliver to" (disabled) from a transient + * delivery failure that should be retried. + */ + get alertsEnabled(): boolean { + return this.enabled; + } + /** * Send a critical alert to every subscriber. Returns true only on confirmed delivery to * at least one chat. Returns false when telegram is disabled, no subscribers exist, or From 47ac3fbed13c15dfb44c404f03f5667c3de99c51 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 03:31:39 +0200 Subject: [PATCH 05/11] fix(minter-guard): make the slipped-through warning reachable, and harden alerting further MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second review round. The heaviest finding was that the terminal page warning that an unwhitelisted minter slipped through could never fire in normal operation: syncMinters() runs before the guard each cycle and relabels the row from PROPOSED to APPROVED on local wall-clock time, while the guard only looks at PROPOSED rows and only pages inside a 60-second buffer. With a 5-minute cadence no cycle ever sees both conditions at once, so the minter dropped out of the candidate set in silence. A bounded sweep now covers exactly the minters this process already tracked: for each one the on-chain mapping decides — cleared means it was denied and is marked done without an alert, a future deadline means it stays a candidate, and a past deadline means the application period ended without a deny and pages once. The candidate query is deliberately NOT widened to APPROVED: the whitelist ships empty, so that would page for every legitimately approved minter on the first run. Further fixes from the same round: - The startup probe still summed raw votes over an unvalidated seed, so a funded seed delegating elsewhere reported "qualified" at boot. It now takes its verdict from the same contract-truthful path the /guard endpoint uses, so the boot log and the dashboard cannot disagree. - Candidates are resolved against the chain and sorted by deadline before the pre-check runs. Previously a stale row for an already-denied minter inflated the candidate count of a critical page, and the per-cycle confirmation budget could be spent on a distant deadline while a closing window was deferred. - A skip page armed its hour-long cooldown before delivery was attempted, so a failed page silenced its whole class for an hour. The cooldown is now armed only on confirmed delivery. - Dynamic provider text (error messages, codes such as NONCE_EXPIRED) is escaped before it enters a Markdown alert. Unescaped underscores made those messages undeliverable, and the retry machinery then resent the same unsendable text every cycle. - The pending-alert retry pass moved behind the deny work and is bounded per cycle, with the remaining backlog named in the log. Notifications are not time-critical; a veto window is. - The reported voting-power percentage is recomputed over the helper set actually used, so it can no longer contradict the qualification verdict beside it. Two limitations are deliberate and now documented in place: the boot probe cannot distinguish an unbackfilled delegation graph from a genuinely empty one (at worst a missing convenience page, since the per-cycle pre-check pages when it matters), and the confirmed-deny success page is not retained for retry because the deny is already on-chain and needs no human action. --- src/monitoringV2/minter-guard.service.ts | 593 +++++++++++++++-------- 1 file changed, 392 insertions(+), 201 deletions(-) diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index 30bf6a0..963b704 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -48,7 +48,11 @@ const DENY_TOOLATE_BUFFER_SECONDS = 60n; // 5-minute cycle. Permanent failures (TooLate) and the cap both set done=true. const MAX_DENY_ATTEMPTS = 3; -type SkipAlertKind = 'votes' | 'gas' | 'seed' | 'precheck'; +// Cap pending terminal-page retries per cycle so a backlog built during a Telegram outage cannot delay +// the time-critical deny work (each send sleeps 50ms per subscriber). Remainder waits for the next cycle. +const MAX_ALERT_RETRIES_PER_CYCLE = 5; + +type SkipAlertKind = 'votes' | 'gas' | 'seed' | 'precheck' | 'deadline'; interface DenyStateEntry { attempts: number; @@ -62,6 +66,12 @@ interface Whitelist { minters: string[]; } +/** Working-set item after the resolve pass: on-chain deadline already read, ready for urgency sort + deny. */ +interface ResolvedCandidate { + address: string; + onChainDeadline: bigint; +} + /** * Raised for a guard CONFIG error that must fail loud and abort bootstrap (missing/invalid * GUARD_PRIVATE_KEY) — as opposed to a recoverable init error (e.g. a bad whitelist file), which @@ -105,6 +115,7 @@ export class MinterGuardService { gas: 0, seed: 0, precheck: 0, + deadline: 0, }; // Built once from JuiceDollar + Equity ABIs so both TooLate and NotQualified decode. private denyErrorInterface?: ethers.Interface; @@ -206,53 +217,57 @@ export class MinterGuardService { } /** - * Startup preflight: read additive voting power (votes(signer)+Σvotes(helper)) and page once if the - * signer is under the 2% quorum. Deliberately does NOT abort bootstrap on not-qualified: taking the - * whole monitoring process down over a governance state that delegation can fix at runtime would be - * strictly worse than running loud-but-degraded; the state is also exposed continuously via GET /guard. - * A read/RPC failure is warn-only (no page, no throw) so a transient blip at boot cannot page or kill. + * Startup preflight: obtain the same contract-truthful verdict as GET /guard via getStatus() + * (votesDelegated + seed-less retry) so the dashboard and the boot log can never disagree. Pages once + * if under the 2% quorum when helpers/seed are assessable. Deliberately does NOT abort bootstrap on + * not-qualified: taking the whole monitoring process down over a governance state that delegation can + * fix at runtime would be strictly worse than running loud-but-degraded. A getStatus transport failure + * is warn-only (no page, no throw) so a transient blip at boot cannot page or kill. + * + * Empty delegation graph with no seed: cannot distinguish "not backfilled yet" from "genuinely nobody + * delegates". Consequence is at most a missing convenience page at boot — the per-cycle pre-check pages + * as soon as a real candidate exists. The signer's own votes are still assessed via getStatus(). */ private async probeQualification(): Promise { const signerAddress = this.signerAddress; - const equityAddress = this.equityAddress; - if (!signerAddress || !equityAddress) return; + if (!signerAddress) return; try { - const equity = new ethers.Contract(equityAddress, EquityABI, this.providerService.multicallProvider); + const status = await this.getStatus(); + + if (status.qualified) { + this.logger.log( + `MinterGuard startup: signer ${signerAddress} qualified at ${status.votingPowerPct}% ` + + `(quorum ${status.quorumPct}%) with ${status.helperCount} helper(s)` + ); + return; + } + + // Under quorum: empty graph + no seed → warn only (helpers not assessable until backfill). + // Otherwise page once so operators know the guard is armed but below threshold. const delegations = await this.eventsRepo.getDelegations(); - // Before the first processBlocks backfill (or on a reset DB) getDelegations() is empty. With no - // configured seed the additive power is only the signer's own — paging "under 2% quorum" at boot - // would be a false alarm. The per-cycle pre-check will page if it matters once candidates exist. if (delegations.length === 0 && this.helperSeed.length === 0) { + // Empty-graph early path: at most a missing convenience page at boot; per-cycle pre-check + // pages as soon as a real candidate exists (see method docstring). this.logger.warn( - 'MinterGuard startup: Delegation graph has not been backfilled yet; qualification cannot be ' + - 'assessed. The per-cycle pre-check will page if under-quorum matters once candidates exist.' + `MinterGuard startup: signer ${signerAddress} alone is below the 2% quorum ` + + `(${status.votingPowerPct}% < ${status.quorumPct}%); helpers cannot be assessed until the ` + + `Delegation graph is backfilled. The per-cycle pre-check will page once a real candidate exists.` ); return; } - const helpers = computeHelpers(delegations, signerAddress, this.helperSeed); - const voteResults = await this.providerService.callBatch([ - () => equity.totalVotes(), - ...[signerAddress, ...helpers].map((a) => () => equity.votes(a)), - ]); - const totalVotes: bigint = BigInt(voteResults[0]); - const votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); - const bps = totalVotes > 0n ? (votingPower * 10000n) / totalVotes : 0n; - - if (votingPower * 10000n < QUORUM_BPS * totalVotes) { - this.logger.error( - `MinterGuard startup: signer ${signerAddress} under quorum ` + - `(${bps} bps < ${QUORUM_BPS} bps / 2%). denyMinter will be skipped until qualified.` - ); - await this.telegramService.sendCriticalAlert( - `⚠️ *Minter guard under 2% quorum at startup*\n\n` + - `Signer: \`${signerAddress}\`\n` + - `Voting power: ${bps} bps (needs >= ${QUORUM_BPS} bps / 2%)\n\n` + - `Remedy: delegateVoteTo(${signerAddress}) on Equity, or fund the signer with JUICE.` - ); - } else { - this.logger.log(`MinterGuard startup: signer ${signerAddress} qualified at ${bps} bps (>= ${QUORUM_BPS} bps)`); - } + + this.logger.error( + `MinterGuard startup: signer ${signerAddress} under quorum ` + + `(${status.votingPowerPct}% < ${status.quorumPct}%). denyMinter will be skipped until qualified.` + ); + await this.telegramService.sendCriticalAlert( + `⚠️ *Minter guard under 2% quorum at startup*\n\n` + + `Signer: \`${signerAddress}\`\n` + + `Voting power: ${this.escapeMarkdown(status.votingPowerPct)}% (needs >= ${status.quorumPct}%)\n` + + `Helpers: ${status.helperCount}\n\n` + + `Remedy: delegateVoteTo(${signerAddress}) on Equity, or fund the signer with JUICE.` + ); } catch (error) { // Transient RPC blip at boot must not page and must not throw (see method docstring). const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); @@ -282,6 +297,17 @@ export class MinterGuardService { } } + /** + * Escape Telegram legacy-Markdown specials so dynamic provider/config text cannot poison + * parse_mode: 'Markdown' delivery (Telegram rejects malformed entities, which would make those + * error classes permanently undeliverable and retry the same unsendable text forever). Escapes + * `_`, `*`, backtick and `[` by prefixing each with a backslash. Mirrors the established pattern + * used for `[` in telegram.service.envTag(). + */ + private escapeMarkdown(value: string): string { + return value.replace(/([_*`\[])/g, '\\$1'); + } + /** * Deliver a terminal (per-minter) critical page, honouring sendCriticalAlert's return value. * On confirmed delivery sets alerted=true and clears pendingAlert. On failure keeps pendingAlert @@ -310,29 +336,47 @@ export class MinterGuardService { /** * Re-send any terminal pages that failed delivery on a previous cycle. Notification-only — - * never re-sends a deny transaction. Runs even when there are no current candidates. + * never re-sends a deny transaction. Bounded per cycle (MAX_ALERT_RETRIES_PER_CYCLE) so a + * backlog from an outage cannot delay the time-critical deny work; remainder waits for the next + * cycle (logged at warn so a truncated backlog is never mistaken for an empty one). Runs at the + * END of checkAndDeny even when there are no candidates — notifications are not time-critical. */ private async retryPendingAlerts(): Promise { + const pending: Array<[string, DenyStateEntry]> = []; for (const [addrLc, state] of this.denyState) { if (state.pendingAlert && state.alerted !== true) { - await this.deliverTerminalAlert(addrLc, state, state.pendingAlert); + pending.push([addrLc, state]); + } + } + + let attempted = 0; + for (const [addrLc, state] of pending) { + if (attempted >= MAX_ALERT_RETRIES_PER_CYCLE) { + const stillPending = pending.length - attempted; + this.logger.warn( + `MinterGuard: alert retry cap reached (${MAX_ALERT_RETRIES_PER_CYCLE} per cycle); ` + + `${stillPending} pending page(s) still waiting for the next cycle` + ); + break; } + // pendingAlert is defined by the filter above. + await this.deliverTerminalAlert(addrLc, state, state.pendingAlert as string); + attempted++; } } /** * Called by MonitoringService after syncMinters(). Iterates PROPOSED minters and denies any not on - * the whitelist that are not already terminal in denyState. Runs a signer-global votes/gas pre-check - * once per cycle before any send; permanent rejections and the attempt cap stop retry amplification. + * the whitelist that are not already terminal in denyState. Resolves on-chain deadlines first so the + * pre-check counts only genuinely actionable minters and the send loop serves the soonest veto window + * first. Runs a signer-global votes/gas pre-check once per cycle before any send; permanent rejections + * and the attempt cap stop retry amplification. Ends with a tracked-minter sweep (passing-unchallenged + * pages) and a bounded pending-alert retry — notifications run after deny work, never before. */ async checkAndDeny(): Promise { const { signerKey, signerAddress, jusdAddress } = this; if (!this.enabled || !signerKey || !signerAddress || !jusdAddress || !this.denyErrorInterface) return; - // Retry undelivered terminal pages first (even when there are no candidates this cycle). - // Notification-only — does not re-send deny transactions. - await this.retryPendingAlerts(); - const minters = await this.minterRepo.findAll(); // Denies BRIDGE-typed proposals too: bridge type is inferred from a single // `usd()` view call, which is trivial to mimic in a malicious contract. @@ -344,177 +388,299 @@ export class MinterGuardService { !this.denyState.get(m.address.toLowerCase())?.done ); - if (candidates.length === 0) return; - - this.logger.warn(`Found ${candidates.length} unwhitelisted PROPOSED minter(s) to deny`); - // Build the signer + contract fresh from the live provider for this run, so a recycled // provider is picked up rather than a stale connection captured at initialize(). const wallet = new ethers.Wallet(signerKey, this.providerService.provider); + const juiceDollar = new ethers.Contract(jusdAddress, JuiceDollarABI, wallet); - // Signer-global pre-check (once per cycle, before any deny): verify quorum + gas and build helpers. - const precheck = await this.runDenyPrecheck(signerAddress, wallet, candidates); - if (!precheck.ok) return; - const helpers = precheck.helpers; + // Addresses still in this cycle's candidate set (handled by the deny path when actionable). + const candidateAddressSet = new Set(candidates.map((m) => m.address.toLowerCase())); - const juiceDollar = new ethers.Contract(jusdAddress, JuiceDollarABI, wallet); + // Resolve pass BEFORE pre-check: read each on-chain deadline so (a) already-resolved minters do + // not inflate the under-quorum candidate count and (b) urgency order can replace database order. + const workingSet: ResolvedCandidate[] = []; + if (candidates.length > 0) { + this.logger.warn(`Found ${candidates.length} unwhitelisted PROPOSED minter(s) to deny`); + + for (const minter of candidates) { + const address = ethers.getAddress(minter.address); + const addrLc = address.toLowerCase(); + let onChainDeadline: bigint; + try { + onChainDeadline = BigInt(await juiceDollar.minters(address)); + } catch (error) { + // Sustained RPC fault must not let a veto window expire in silence — rate-limited page. + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + this.logger.error(`MinterGuard skip ${address}: failed to read on-chain deny deadline (minters): ${errorMsg}`); + await this.maybeAlertSkip( + 'deadline', + `⚠️ *Minter guard could not read deny deadline*\n\n` + + `Address: \`${address}\`\n` + + `Detail: ${this.escapeMarkdown(errorMsg)}\n\n` + + `The on-chain application deadline could not be read this cycle; the candidate is deferred ` + + `(not marked done). Investigate RPC if this persists — a silent window expiry must not happen.` + ); + // Drop for this cycle only — do not mark done. + continue; + } + // on-chain 0 means the minter is no longer pending (already denied, or application resolved + // otherwise) while the indexed row is simply behind — the false-alarm class we remove: no page. + if (onChainDeadline === 0n) { + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + this.denyState.set(addrLc, { ...prev, done: true }); + this.logger.warn( + `MinterGuard skip ${address}: on-chain minters(address)=0 (already denied or resolved; ` + + `indexed row still PROPOSED) — marking done without alert to avoid false "passing unchallenged" page` + ); + continue; + } + workingSet.push({ address, onChainDeadline }); + } + } + + // Nothing actionable: skip pre-check (would page "N left undenied" about minters that need nothing). + if (workingSet.length > 0) { + // Urgency, not database order, must decide when the confirmation budget runs short: serve the + // soonest veto window first. + workingSet.sort((a, b) => (a.onChainDeadline < b.onChainDeadline ? -1 : a.onChainDeadline > b.onChainDeadline ? 1 : 0)); + + // Signer-global pre-check (once per cycle, before any deny): verify quorum + gas and build helpers. + // Count is the number of genuinely actionable minters after the resolve pass. + const precheck = await this.runDenyPrecheck(signerAddress, wallet, workingSet); + if (precheck.ok) { + const helpers = precheck.helpers; + + // Track confirmation wait spent this cycle so sequential timeouts cannot overrun the cron cadence. + let confirmBudgetSpentMs = 0; + let deferDeniesLogged = false; + + for (const { address, onChainDeadline } of workingSet) { + const addrLc = address.toLowerCase(); + + // Just-in-time TooLate guard. denyMinter reverts TooLate once block.timestamp > + // minters[_minter] (the on-chain validityStart set by suggestMinter). Deadline was already + // read in the resolve pass; re-read only the live block timestamp so the comparison advances + // during the cycle (which is what this check is for). + let latestBlock: ethers.Block | null = null; + try { + latestBlock = await this.providerService.provider.getBlock('latest'); + } catch (error) { + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + this.logger.error(`MinterGuard skip ${address}: failed to read latest block for TooLate pre-check: ${errorMsg}`); + continue; + } + if (!latestBlock) { + this.logger.error(`MinterGuard skip ${address}: provider.getBlock('latest') returned null for TooLate pre-check`); + continue; + } + if (BigInt(latestBlock.timestamp) + DENY_TOOLATE_BUFFER_SECONDS >= onChainDeadline) { + this.logger.warn( + `MinterGuard skip ${address}: deny window closed or about to close ` + + `(block ts ${latestBlock.timestamp} + ${DENY_TOOLATE_BUFFER_SECONDS}s buffer >= on-chain deadline ${onChainDeadline})` + ); + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + const next: DenyStateEntry = { ...prev, done: true }; + this.denyState.set(addrLc, next); + // Alert ONCE that an unwhitelisted minter is passing unchallenged (honour delivery return). + if (!prev.alerted) { + const alertMsg = + `⚠️ *Unwhitelisted minter passing unchallenged*\n\n` + + `Address: \`${address}\`\n` + + `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + + `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.`; + await this.deliverTerminalAlert(addrLc, next, alertMsg); + } + continue; + } + + // Remaining confirmation budget too small to be useful: defer new denies (not fail) so later + // watchers still run and the next cycle can finish the rest. Do not mark done; do not page. + // JIT closed-window / already-resolved paths above still run for every candidate. + const remainingBudgetMs = DENY_CYCLE_CONFIRM_BUDGET_MS - confirmBudgetSpentMs; + if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { + if (!deferDeniesLogged) { + this.logger.warn( + `MinterGuard: confirmation budget exhausted (${confirmBudgetSpentMs}ms spent of ` + + `${DENY_CYCLE_CONFIRM_BUDGET_MS}ms); deferring remaining deny candidate(s) including ${address} ` + + `to the next cycle (not marked done, no page — deferral is not a failure).` + ); + deferDeniesLogged = true; + } + continue; + } - // Track confirmation wait spent this cycle so sequential timeouts cannot overrun the cron cadence. - let confirmBudgetSpentMs = 0; - let deferDeniesLogged = false; + const envLabel = `${this.config.environment ?? 'unknown'}/${this.config.chain ?? 'unknown'}`; + const message = `Auto-deny by minter-guard: not in whitelist (${envLabel})`; + let confirmed = false; + let txHash: string | undefined; + const waitTimeoutMs = Math.min(DENY_CONFIRM_TIMEOUT_MS, remainingBudgetMs); + try { + const tx = await juiceDollar.denyMinter(address, helpers, message); + txHash = tx.hash; + this.logger.warn(`Submitted denyMinter for ${address}: tx=${tx.hash}`); + // Bounded wait within the per-cycle confirmation budget: on timeout this throws and the + // minter is left unmarked to retry next cycle. A retry sends a fresh-nonce tx (it does not + // replace a stuck one); under sustained mempool/gas pathology the deny may not land, but the + // terminal FAILED alert then pages a human — an accepted limitation of the opt-in guard, + // deliberately not carrying nonce/replacement state. + const waitStartedAt = Date.now(); + let receipt: ethers.ContractTransactionReceipt | null; + try { + receipt = await tx.wait(1, waitTimeoutMs); + } finally { + confirmBudgetSpentMs += Date.now() - waitStartedAt; + } + if (!receipt) { + // wait resolved without a receipt (should be rare with confirms=1); treat as unconfirmed for retry. + throw new Error(`denyMinter tx.wait returned null for ${address} (tx=${txHash})`); + } + confirmed = true; + // Confirmed on-chain from here — mark before alerting so a Telegram hiccup cannot cause a double deny. + // Success page is deliberately NOT retained for retry: the deny is already on-chain, no human + // action is required, marking must precede the alert to prevent a double deny, and a failed + // delivery is logged at error (durable record without pendingAlert). + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + this.denyState.set(addrLc, { attempts: prev.attempts, done: true }); + this.logger.warn(`denyMinter confirmed for ${address}: block=${receipt.blockNumber}`); + const delivered = await this.telegramService.sendCriticalAlert( + `🛡️ *Minter auto-denied*\n\n` + + `Address: \`${address}\`\n` + + `Tx: \`${txHash}\`\n` + + `Block: ${receipt.blockNumber}\n` + + `Message: ${this.escapeMarkdown(message)}` + ); + // Success page does not need pendingAlert retry (deny is already on-chain and marked done), + // but log loud when delivery fails so the gap is visible. + if (!delivered) { + this.logger.error( + `MinterGuard success page could not be delivered for ${address} (tx=${txHash}); deny is on-chain` + ); + } + } catch (error) { + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + if (confirmed) { + // deny already landed on-chain; only post-confirmation bookkeeping/alert failed — NOT a deny failure. + this.logger.error( + `denyMinter confirmed but post-processing failed for ${address} (tx=${txHash}): ${errorMsg}`, + error?.stack || error + ); + } else { + const classification = classifyDenyError(error, this.denyErrorInterface); + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + const attempts = prev.attempts + 1; + const done = classification.kind === 'permanent' || attempts >= MAX_DENY_ATTEMPTS; + const next: DenyStateEntry = { attempts, done, alerted: prev.alerted, pendingAlert: prev.pendingAlert }; + this.denyState.set(addrLc, next); + + this.logger.error( + `Failed to deny minter ${address} (attempt ${attempts}/${MAX_DENY_ATTEMPTS}, ` + + `${classification.kind}/${classification.label}): ${classification.detail}`, + error?.stack || error + ); + + // FAILED critical alert ONLY on the terminal state for this minter, and only once. + // NotQualified must not produce a per-attempt page — the precheck owns that page. + // Non-terminal transient failures log at error level and stay silent on Telegram. + // windowClosed uses the on-chain deadline so remedy text stays truthful. + if (done && !prev.alerted) { + const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= onChainDeadline; + const remedy = windowClosed + ? 'The application period has ended — denyMinter is impossible; challenge/handle the minter otherwise if needed.' + : 'Manual denyMinter() required before the application period ends.'; + const alertMsg = + `⚠️ *Minter auto-deny FAILED*\n\n` + + `Address: \`${address}\`\n` + + `Class: ${this.escapeMarkdown(classification.label)} (${classification.kind})\n` + + `Detail: ${this.escapeMarkdown(classification.detail)}\n` + + `Attempts: ${attempts}/${MAX_DENY_ATTEMPTS}\n\n` + + remedy; + await this.deliverTerminalAlert(addrLc, { ...next, done: true }, alertMsg); + } + } + } + } + } + } - for (const minter of candidates) { - const address = ethers.getAddress(minter.address); - const addrLc = address.toLowerCase(); + // Sweep tracked minters that left the PROPOSED candidate set without a deny (see method). + await this.sweepPassedUnchallenged(juiceDollar, candidateAddressSet); - // Just-in-time TooLate guard. denyMinter reverts TooLate once block.timestamp > - // minters[_minter] (the on-chain validityStart set by suggestMinter). The authoritative - // deadline is the chain mapping, not the indexed row (which stays PROPOSED until the next - // event sync). Reading DB-derived deadline caused false "passing unchallenged" pages after a - // confirmed deny + process restart, and double-send of already-mined denies. - let latestBlock: ethers.Block | null = null; + // Pending terminal pages after deny work — notifications are not time-critical; a veto window is. + await this.retryPendingAlerts(); + } + + /** + * Bounded sweep over minters this process already tracked (denyState entries that are not done). + * WHY: MinterService.syncMinters() runs BEFORE checkAndDeny() every cycle and derives status from + * local wall-clock time (PROPOSED while currentTimestamp < startTimestamp, else APPROVED). + * checkAndDeny only considers PROPOSED rows, and the window-closed page only fires inside a 60s + * buffer while cycles run every 5 minutes — so in normal operation no cycle ever observes PROPOSED + * AND inside that buffer: by the next tick syncMinters has relabelled the row APPROVED, it drops + * out of candidates, and the "passing unchallenged" page never fires. This sweep covers exactly + * those previously tracked addresses without widening the candidate query to APPROVED (which would + * page once for every legitimately approved unwhitelisted minter on first run). + */ + private async sweepPassedUnchallenged(juiceDollar: ethers.Contract, candidateAddresses: Set): Promise { + let latestBlock: ethers.Block | null = null; + try { + latestBlock = await this.providerService.provider.getBlock('latest'); + } catch (error) { + const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); + this.logger.error(`MinterGuard sweep: failed to read latest block: ${errorMsg}`); + return; + } + if (!latestBlock) { + this.logger.error(`MinterGuard sweep: provider.getBlock('latest') returned null`); + return; + } + const blockTs = BigInt(latestBlock.timestamp); + + for (const [addrLc, state] of this.denyState) { + if (state.done) continue; + // Still in this cycle's candidate set — handled by the normal deny path. + if (candidateAddresses.has(addrLc)) continue; + + const address = ethers.getAddress(addrLc); let onChainDeadline: bigint; try { - latestBlock = await this.providerService.provider.getBlock('latest'); onChainDeadline = BigInt(await juiceDollar.minters(address)); } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); - this.logger.error(`MinterGuard skip ${address}: failed to read latest block/minters for TooLate pre-check: ${errorMsg}`); + this.logger.error(`MinterGuard sweep: failed to read minters(${address}): ${errorMsg}`); + // Leave entry untouched — retries next cycle. continue; } - if (!latestBlock) { - this.logger.error(`MinterGuard skip ${address}: provider.getBlock('latest') returned null for TooLate pre-check`); - continue; - } - // on-chain 0 means the minter is no longer pending (already denied, or application resolved - // otherwise) while the indexed row is simply behind — the false-alarm class we remove: no page. + if (onChainDeadline === 0n) { - const prev = this.denyState.get(addrLc) || { attempts: 0 }; - this.denyState.set(addrLc, { ...prev, done: true }); - this.logger.warn( - `MinterGuard skip ${address}: on-chain minters(address)=0 (already denied or resolved; ` + - `indexed row still PROPOSED) — marking done without alert to avoid false "passing unchallenged" page` - ); - continue; - } - if (BigInt(latestBlock.timestamp) + DENY_TOOLATE_BUFFER_SECONDS >= onChainDeadline) { + // Denied by us or by someone else — mark done, no alert (false-alarm class). + this.denyState.set(addrLc, { ...state, done: true }); this.logger.warn( - `MinterGuard skip ${address}: deny window closed or about to close ` + - `(block ts ${latestBlock.timestamp} + ${DENY_TOOLATE_BUFFER_SECONDS}s buffer >= on-chain deadline ${onChainDeadline})` + `MinterGuard sweep ${address}: on-chain minters(address)=0 (already denied or resolved) — ` + + `marking done without alert` ); - const prev = this.denyState.get(addrLc) || { attempts: 0 }; - const next: DenyStateEntry = { ...prev, done: true }; - this.denyState.set(addrLc, next); - // Alert ONCE that an unwhitelisted minter is passing unchallenged (honour delivery return). - if (!prev.alerted) { - const alertMsg = - `⚠️ *Unwhitelisted minter passing unchallenged*\n\n` + - `Address: \`${address}\`\n` + - `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + - `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.`; - await this.deliverTerminalAlert(addrLc, next, alertMsg); - } continue; } - // Remaining confirmation budget too small to be useful: defer new denies (not fail) so later - // watchers still run and the next cycle can finish the rest. Do not mark done; do not page. - // JIT closed-window / already-resolved paths above still run for every candidate. - const remainingBudgetMs = DENY_CYCLE_CONFIRM_BUDGET_MS - confirmBudgetSpentMs; - if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { - if (!deferDeniesLogged) { - this.logger.warn( - `MinterGuard: confirmation budget exhausted (${confirmBudgetSpentMs}ms spent of ` + - `${DENY_CYCLE_CONFIRM_BUDGET_MS}ms); deferring remaining deny candidate(s) including ${address} ` + - `to the next cycle (not marked done, no page — deferral is not a failure).` - ); - deferDeniesLogged = true; - } + if (blockTs < onChainDeadline) { + // Deadline still in the future — stays a candidate next cycle if still PROPOSED; leave alone. continue; } - const message = `Auto-deny by minter-guard: not in whitelist (${this.config.environment ?? 'unknown'}/${this.config.chain ?? 'unknown'})`; - let confirmed = false; - let txHash: string | undefined; - const waitTimeoutMs = Math.min(DENY_CONFIRM_TIMEOUT_MS, remainingBudgetMs); - try { - const tx = await juiceDollar.denyMinter(address, helpers, message); - txHash = tx.hash; - this.logger.warn(`Submitted denyMinter for ${address}: tx=${tx.hash}`); - // Bounded wait within the per-cycle confirmation budget: on timeout this throws and the - // minter is left unmarked to retry next cycle. A retry sends a fresh-nonce tx (it does not - // replace a stuck one); under sustained mempool/gas pathology the deny may not land, but the - // terminal FAILED alert then pages a human — an accepted limitation of the opt-in guard, - // deliberately not carrying nonce/replacement state. - const waitStartedAt = Date.now(); - let receipt: ethers.ContractTransactionReceipt | null; - try { - receipt = await tx.wait(1, waitTimeoutMs); - } finally { - confirmBudgetSpentMs += Date.now() - waitStartedAt; - } - if (!receipt) { - // wait resolved without a receipt (should be rare with confirms=1); treat as unconfirmed for retry. - throw new Error(`denyMinter tx.wait returned null for ${address} (tx=${txHash})`); - } - confirmed = true; - // Confirmed on-chain from here — mark before alerting so a Telegram hiccup cannot cause a double deny. - const prev = this.denyState.get(addrLc) || { attempts: 0 }; - this.denyState.set(addrLc, { attempts: prev.attempts, done: true }); - this.logger.warn(`denyMinter confirmed for ${address}: block=${receipt.blockNumber}`); - const delivered = await this.telegramService.sendCriticalAlert( - `🛡️ *Minter auto-denied*\n\n` + - `Address: \`${address}\`\n` + - `Tx: \`${txHash}\`\n` + - `Block: ${receipt.blockNumber}\n` + - `Message: ${message}` - ); - // Success page does not need pendingAlert retry (deny is already on-chain and marked done), - // but log loud when delivery fails so the gap is visible. - if (!delivered) { - this.logger.error(`MinterGuard success page could not be delivered for ${address} (tx=${txHash}); deny is on-chain`); - } - } catch (error) { - const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); - if (confirmed) { - // deny already landed on-chain; only post-confirmation bookkeeping/alert failed — NOT a deny failure. - this.logger.error( - `denyMinter confirmed but post-processing failed for ${address} (tx=${txHash}): ${errorMsg}`, - error?.stack || error - ); - } else { - const classification = classifyDenyError(error, this.denyErrorInterface); - const prev = this.denyState.get(addrLc) || { attempts: 0 }; - const attempts = prev.attempts + 1; - const done = classification.kind === 'permanent' || attempts >= MAX_DENY_ATTEMPTS; - const next: DenyStateEntry = { attempts, done, alerted: prev.alerted, pendingAlert: prev.pendingAlert }; - this.denyState.set(addrLc, next); - - this.logger.error( - `Failed to deny minter ${address} (attempt ${attempts}/${MAX_DENY_ATTEMPTS}, ` + - `${classification.kind}/${classification.label}): ${classification.detail}`, - error?.stack || error - ); - - // FAILED critical alert ONLY on the terminal state for this minter, and only once. - // NotQualified must not produce a per-attempt page — the precheck owns that page. - // Non-terminal transient failures log at error level and stay silent on Telegram. - // windowClosed uses the on-chain deadline so remedy text stays truthful. - if (done && !prev.alerted) { - const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= onChainDeadline; - const remedy = windowClosed - ? 'The application period has ended — denyMinter is impossible; challenge/handle the minter otherwise if needed.' - : 'Manual denyMinter() required before the application period ends.'; - const alertMsg = - `⚠️ *Minter auto-deny FAILED*\n\n` + - `Address: \`${address}\`\n` + - `Class: ${classification.label} (${classification.kind})\n` + - `Detail: ${classification.detail}\n` + - `Attempts: ${attempts}/${MAX_DENY_ATTEMPTS}\n\n` + - remedy; - await this.deliverTerminalAlert(addrLc, { ...next, done: true }, alertMsg); - } - } + // Application period ended without a deny: mark done and page exactly once via deliverTerminalAlert + // so failed delivery inherits the retry-on-failed-delivery behaviour. + const next: DenyStateEntry = { ...state, done: true }; + this.denyState.set(addrLc, next); + this.logger.warn( + `MinterGuard sweep ${address}: application period ended without deny ` + + `(block ts ${latestBlock.timestamp} >= on-chain deadline ${onChainDeadline}) — marking done` + ); + if (!state.alerted) { + const alertMsg = + `⚠️ *Unwhitelisted minter passing unchallenged*\n\n` + + `Address: \`${address}\`\n` + + `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + + `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.`; + await this.deliverTerminalAlert(addrLc, next, alertMsg); } } } @@ -690,8 +856,8 @@ export class MinterGuardService { 'precheck', `⚠️ *Minter guard pre-check failed — qualification unknown*\n\n` + `${candidates.length} unwhitelisted PROPOSED minter(s) left undenied this cycle.\n` + - `Qualification could not be determined (class: ${classification.label}).\n` + - `Detail: ${classification.detail}\n\n` + + `Qualification could not be determined (class: ${this.escapeMarkdown(classification.label)}).\n` + + `Detail: ${this.escapeMarkdown(classification.detail)}\n\n` + `The guard will retry next cycle; investigate RPC / helper set if this persists.` ); } @@ -702,13 +868,29 @@ export class MinterGuardService { /** * Rate-limited skip page: at most one per kind per SKIP_ALERT_COOLDOWN_MS (in-memory, reset on restart). * Kinds have independent timers so one class of page cannot suppress another. + * Stamps the cooldown timer only when the page was delivered (or when telegram is disabled — nothing + * to deliver to and retrying every cycle would only spam the log). A failed delivery does NOT arm + * the cooldown so the next cycle can retry once Telegram recovers. */ private async maybeAlertSkip(kind: SkipAlertKind, message: string): Promise { const nowMs = Date.now(); const lastAt = this.lastSkipAlertAt[kind]; if (nowMs - lastAt < SKIP_ALERT_COOLDOWN_MS) return; - this.lastSkipAlertAt[kind] = nowMs; - await this.telegramService.sendCriticalAlert(message); + + // Telegram disabled: nothing to deliver to — stamp so we do not re-log every cycle; error log is the record. + if (!this.telegramService.alertsEnabled) { + this.lastSkipAlertAt[kind] = nowMs; + this.logger.error(`MinterGuard skip page not deliverable (telegram disabled): ${message}`); + return; + } + + const delivered = await this.telegramService.sendCriticalAlert(message); + if (delivered) { + this.lastSkipAlertAt[kind] = nowMs; + } else { + // Do not stamp: page failed and will be retried on the next cycle once Telegram recovers. + this.logger.error(`MinterGuard skip page failed delivery (kind=${kind}); will retry next cycle: ${message}`); + } } /** @@ -741,10 +923,10 @@ export class MinterGuardService { const equity = new ethers.Contract(equityAddress, EquityABI, this.providerService.multicallProvider); // Additive, revert-proof voting power for DISPLAY (votingPowerPct): votes(signer) + Σ votes(helper). - // This percentage is a display estimate over the derived helper set; `qualified` below is the - // contract's own verdict via votesDelegated — the two can disagree when a helper is invalid - // (e.g. a seed that does not delegate to the signer makes the additive sum look healthy while a - // real deny would revert). + // This percentage is a display estimate over the helper set actually used for the contract verdict; + // `qualified` is the contract's own verdict via votesDelegated. Both now refer to the same set + // (recomputed below when a seed-less retry switches the active helper set) so the percentage and + // the verdict cannot disagree with no explanation. const delegations = await this.eventsRepo.getDelegations(); const helpers = computeHelpers(delegations, signerAddress, this.helperSeed); const voteResults = await this.providerService.callBatch([ @@ -752,7 +934,7 @@ export class MinterGuardService { ...[signerAddress, ...helpers].map((a) => () => equity.votes(a)), ]); const totalVotes: bigint = BigInt(voteResults[0]); - const votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); + let votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); // Contract's own verdict for `qualified` — exact value checkQualified uses. // On-chain rejection of the helper list (EmptyRevert / decoded contract error): retry seed-less once. @@ -796,6 +978,15 @@ export class MinterGuardService { } } + // If the contract path switched to a different helper set, recompute the additive display sum + // over that set so votingPowerPct, qualified and helperCount all describe the same helpers. + if (activeHelpers !== helpers) { + const powerResults = await this.providerService.callBatch( + [signerAddress, ...activeHelpers].map((a) => () => equity.votes(a)) + ); + votingPower = powerResults.reduce((sum, v) => sum + BigInt(v), 0n); + } + // Gas status: model denyMinter() cost with a fixed gas ceiling * live fee (see DENY_GAS_ESTIMATE). const balance: bigint = await provider.getBalance(signerAddress); const feeData = await provider.getFeeData(); From b4c974186ce2a8ce2d9c6fa84eb72028a8d4fc8a Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 04:19:10 +0200 Subject: [PATCH 06/11] fix(minter-guard): assert the tracking invariant, share the alert escaper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third review round. The recurring defect of this branch had one root: a minter became tracked as a side effect of an outcome — a deny attempt, a closed window, an already-resolved mapping — rather than as a consequence of having been observed. Every early exit therefore left a candidate untracked and invisible to the sweep that is supposed to notice it slipping through: a read failure, the confirmation-budget deferral, and a pre-check that bailed for the whole cycle. That is why the same silence reappeared three times, one level deeper each round. The candidate is now registered the moment it is confirmed deniable, before any pre-check runs, and the end of each cycle asserts the invariant: every observed candidate has tracking state. A violation logs the addresses and pages, so a future early exit cannot quietly reopen the hole instead of being caught. Also from this round: - The guard-init failure alert interpolated raw error text into a Markdown message. Its real messages contain underscores — GUARD_HELPER_ADDRESS is invalid, GUARD_WHITELIST_FILE is missing — so Telegram could reject the whole page at the one moment it matters: the guard is off and nobody is told. The escaper moved to the telegram service as a shared function and is used there too, and that alert now records a failed delivery instead of discarding the result. It deliberately gets no retry machinery: a one-shot bootstrap path is not a cycle. - The on-chain deadline is re-read immediately before each send. Reading it once per cycle meant that a minter another actor denied while the guard waited for a previous confirmation was still sent to, reverted, marked permanently failed, and paged as needing a manual deny. - The two RPC passes are bounded per cycle and the sweep rotates its starting point, so a large tracking map can neither outgrow the cadence nor starve its own tail. - Alert bodies are truncated below Telegram's message limit, and a failed retry rotates to the back of the queue. An oversized provider error could otherwise make a page permanently unsendable and monopolise every retry pass. - The startup probe no longer depends on the gas reads it does not need, so a fee-data failure cannot suppress an under-quorum page. - The reported voting power and the qualification verdict now come from the same contract call, so they cannot contradict each other. - A rejected page retries after a bounded backoff instead of on every cycle. - Two overstatements corrected: a page claimed a deny was impossible while the contract would still have accepted one, and the backlog counter stayed silent when every delivery attempt failed. --- src/monitoringV2/minter-guard.service.ts | 489 +++++++++++++++++------ src/monitoringV2/monitoring.service.ts | 13 +- src/monitoringV2/telegram.service.ts | 11 + 3 files changed, 386 insertions(+), 127 deletions(-) diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index 963b704..44a3250 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -6,7 +6,7 @@ import { AppConfigService } from '../config/config.service'; import { ProviderService } from './provider.service'; import { MinterRepository } from './prisma/repositories/minter.repository'; import { EventsRepository } from './prisma/repositories/events.repository'; -import { TelegramService } from './telegram.service'; +import { TelegramService, escapeMarkdownText } from './telegram.service'; import { MinterStatus } from './types'; import { computeHelpers, classifyDenyError, QUORUM_BPS } from './minter-guard.logic'; import { GuardResponse } from '../../shared/types'; @@ -52,7 +52,24 @@ const MAX_DENY_ATTEMPTS = 3; // the time-critical deny work (each send sleeps 50ms per subscriber). Remainder waits for the next cycle. const MAX_ALERT_RETRIES_PER_CYCLE = 5; -type SkipAlertKind = 'votes' | 'gas' | 'seed' | 'precheck' | 'deadline'; +// Cap serial minters() reads in the resolve pass so a large PROPOSED set cannot stretch one cycle past the +// 5-minute cadence and delay every later watcher. Remainder is examined on a later cycle. +const MAX_RESOLVE_READS_PER_CYCLE = 25; + +// Cap serial minters() reads in the passing-unchallenged sweep for the same cadence reason. Paired with +// round-robin resume so a fixed start cannot starve the same tail forever under the cap. +const MAX_SWEEP_READS_PER_CYCLE = 25; + +// Safe Telegram body length (Telegram rejects sendMessage bodies over 4096). Comfortably under the hard +// limit so Markdown/entity overhead cannot push a page into permanent undeliverable rejection. +const MAX_ALERT_BODY_CHARS = 3500; + +// Short backoff after a FAILED skip-page delivery. Full SKIP_ALERT_COOLDOWN_MS still applies on success +// (and when alerts are disabled). Trade-off: a delivered page must not repeat for an hour; a failed one +// must be retried; neither may become a per-cycle loop (twelve attempts an hour). +const SKIP_ALERT_RETRY_BACKOFF_MS = 15 * 60 * 1000; + +type SkipAlertKind = 'votes' | 'gas' | 'seed' | 'precheck' | 'deadline' | 'invariant'; interface DenyStateEntry { attempts: number; @@ -116,7 +133,12 @@ export class MinterGuardService { seed: 0, precheck: 0, deadline: 0, + invariant: 0, }; + // Round-robin resume for the capped sweep: address last examined when the read cap stopped the pass. + // Next cycle starts AFTER this key so entries beyond MAX_SWEEP_READS_PER_CYCLE are not starved forever + // under a fixed Map iteration start. + private sweepResumeAfter?: string; // Built once from JuiceDollar + Equity ABIs so both TooLate and NotQualified decode. private denyErrorInterface?: ethers.Interface; @@ -217,28 +239,33 @@ export class MinterGuardService { } /** - * Startup preflight: obtain the same contract-truthful verdict as GET /guard via getStatus() - * (votesDelegated + seed-less retry) so the dashboard and the boot log can never disagree. Pages once - * if under the 2% quorum when helpers/seed are assessable. Deliberately does NOT abort bootstrap on - * not-qualified: taking the whole monitoring process down over a governance state that delegation can - * fix at runtime would be strictly worse than running loud-but-degraded. A getStatus transport failure - * is warn-only (no page, no throw) so a transient blip at boot cannot page or kill. + * Startup preflight: obtain the same contract-truthful qualification verdict as GET /guard via + * evaluateQualification() (votesDelegated + seed-less retry) — deliberately NOT getStatus(), so a + * gas-read failure (getBalance / getFeeData) cannot suppress the under-quorum page the probe exists + * for. Dashboard and boot log still share the qualification half. Pages once if under the 2% quorum + * when helpers/seed are assessable. Deliberately does NOT abort bootstrap on not-qualified: taking the + * whole monitoring process down over a governance state that delegation can fix at runtime would be + * strictly worse than running loud-but-degraded. A qualification transport failure is warn-only (no + * page, no throw) so a transient blip at boot cannot page or kill. * * Empty delegation graph with no seed: cannot distinguish "not backfilled yet" from "genuinely nobody * delegates". Consequence is at most a missing convenience page at boot — the per-cycle pre-check pages - * as soon as a real candidate exists. The signer's own votes are still assessed via getStatus(). + * as soon as a real candidate exists. The signer's own votes are still assessed via evaluateQualification(). */ private async probeQualification(): Promise { const signerAddress = this.signerAddress; if (!signerAddress) return; + const quorumPct = Number(QUORUM_BPS) / 100; + try { - const status = await this.getStatus(); + // Qualification only — gas reads live in getStatus and must not gate this page. + const status = await this.evaluateQualification(); if (status.qualified) { this.logger.log( `MinterGuard startup: signer ${signerAddress} qualified at ${status.votingPowerPct}% ` + - `(quorum ${status.quorumPct}%) with ${status.helperCount} helper(s)` + `(quorum ${quorumPct}%) with ${status.helperCount} helper(s)` ); return; } @@ -251,7 +278,7 @@ export class MinterGuardService { // pages as soon as a real candidate exists (see method docstring). this.logger.warn( `MinterGuard startup: signer ${signerAddress} alone is below the 2% quorum ` + - `(${status.votingPowerPct}% < ${status.quorumPct}%); helpers cannot be assessed until the ` + + `(${status.votingPowerPct}% < ${quorumPct}%); helpers cannot be assessed until the ` + `Delegation graph is backfilled. The per-cycle pre-check will page once a real candidate exists.` ); return; @@ -259,15 +286,15 @@ export class MinterGuardService { this.logger.error( `MinterGuard startup: signer ${signerAddress} under quorum ` + - `(${status.votingPowerPct}% < ${status.quorumPct}%). denyMinter will be skipped until qualified.` + `(${status.votingPowerPct}% < ${quorumPct}%). denyMinter will be skipped until qualified.` ); - await this.telegramService.sendCriticalAlert( + const startupMsg = `⚠️ *Minter guard under 2% quorum at startup*\n\n` + - `Signer: \`${signerAddress}\`\n` + - `Voting power: ${this.escapeMarkdown(status.votingPowerPct)}% (needs >= ${status.quorumPct}%)\n` + - `Helpers: ${status.helperCount}\n\n` + - `Remedy: delegateVoteTo(${signerAddress}) on Equity, or fund the signer with JUICE.` - ); + `Signer: \`${signerAddress}\`\n` + + `Voting power: ${escapeMarkdownText(status.votingPowerPct)}% (needs >= ${quorumPct}%)\n` + + `Helpers: ${status.helperCount}\n\n` + + `Remedy: delegateVoteTo(${signerAddress}) on Equity, or fund the signer with JUICE.`; + await this.telegramService.sendCriticalAlert(this.truncateAlertBody(startupMsg)); } catch (error) { // Transient RPC blip at boot must not page and must not throw (see method docstring). const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); @@ -298,14 +325,13 @@ export class MinterGuardService { } /** - * Escape Telegram legacy-Markdown specials so dynamic provider/config text cannot poison - * parse_mode: 'Markdown' delivery (Telegram rejects malformed entities, which would make those - * error classes permanently undeliverable and retry the same unsendable text forever). Escapes - * `_`, `*`, backtick and `[` by prefixing each with a backslash. Mirrors the established pattern - * used for `[` in telegram.service.envTag(). + * Truncate a terminal page body to MAX_ALERT_BODY_CHARS so Telegram cannot permanently reject it + * (hard limit 4096). A truncated body always ends with an explicit marker so operators know text was cut. + * Applied before every store and every send — never retain an untruncated body as pendingAlert. */ - private escapeMarkdown(value: string): string { - return value.replace(/([_*`\[])/g, '\\$1'); + private truncateAlertBody(message: string): string { + if (message.length <= MAX_ALERT_BODY_CHARS) return message; + return `${message.slice(0, MAX_ALERT_BODY_CHARS)}\n\n… (truncated)`; } /** @@ -313,24 +339,28 @@ export class MinterGuardService { * On confirmed delivery sets alerted=true and clears pendingAlert. On failure keeps pendingAlert * so the next cycle can retry — prevents "done + alerted with zero notification" when Telegram is down. * When alerts are disabled entirely there is nothing to retry: alerted=true without pendingAlert; - * logger.error is the durable record (not swallowing). + * logger.error is the durable record (not swallowing). Bodies are always truncated (see truncateAlertBody) + * so a long provider error cannot make a page permanently undeliverable. */ private async deliverTerminalAlert(addrLc: string, state: DenyStateEntry, message: string): Promise { if (state.alerted) return; + const body = this.truncateAlertBody(message); + // Telegram disabled: nothing to deliver to and nothing to retry — error log is the record. if (!this.telegramService.alertsEnabled) { this.denyState.set(addrLc, { ...state, alerted: true, pendingAlert: undefined }); - this.logger.error(`MinterGuard terminal page not deliverable (telegram disabled; not retained for retry): ${message}`); + this.logger.error(`MinterGuard terminal page not deliverable (telegram disabled; not retained for retry): ${body}`); return; } - const delivered = await this.telegramService.sendCriticalAlert(message); + const delivered = await this.telegramService.sendCriticalAlert(body); if (delivered) { this.denyState.set(addrLc, { ...state, alerted: true, pendingAlert: undefined }); } else { - this.denyState.set(addrLc, { ...state, alerted: false, pendingAlert: message }); - this.logger.error(`MinterGuard terminal page could not be delivered; will retry next cycle for ${addrLc}: ${message}`); + // Store the truncated body only — retry must never re-send an oversize original. + this.denyState.set(addrLc, { ...state, alerted: false, pendingAlert: body }); + this.logger.error(`MinterGuard terminal page could not be delivered; will retry next cycle for ${addrLc}: ${body}`); } } @@ -340,6 +370,10 @@ export class MinterGuardService { * backlog from an outage cannot delay the time-critical deny work; remainder waits for the next * cycle (logged at warn so a truncated backlog is never mistaken for an empty one). Runs at the * END of checkAndDeny even when there are no candidates — notifications are not time-critical. + * + * On a failed retry the entry is delete+re-set so Map insertion order moves it to the end: the next + * cycle starts with pages not tried recently. Without rotation, five permanently failing entries at + * the front of the map would starve every later pending page indefinitely under the per-cycle cap. */ private async retryPendingAlerts(): Promise { const pending: Array<[string, DenyStateEntry]> = []; @@ -359,9 +393,30 @@ export class MinterGuardService { ); break; } - // pendingAlert is defined by the filter above. + // pendingAlert is defined by the filter above; re-truncate in case an older entry predates the cap. await this.deliverTerminalAlert(addrLc, state, state.pendingAlert as string); attempted++; + + // Rotate failed retries to the end of Map iteration order (starvation prevention — see method). + const after = this.denyState.get(addrLc); + if (after && after.pendingAlert && after.alerted !== true) { + this.denyState.delete(addrLc); + this.denyState.set(addrLc, after); + } + } + + // Aggregate backlog line, independent of whether the cap truncated the pass: a page attempted this + // cycle that STILL failed (as opposed to never attempted) would otherwise leave no summary line at + // all when the pending count is at or below the cap — a backlog must never be invisible. + // Individual failures keep their own log line from deliverTerminalAlert. + let stillPendingAfter = 0; + for (const state of this.denyState.values()) { + if (state.pendingAlert && state.alerted !== true) stillPendingAfter++; + } + if (stillPendingAfter > 0) { + this.logger.warn( + `MinterGuard: ${stillPendingAfter} pending terminal page(s) still awaiting delivery after this cycle's retry pass` + ); } } @@ -396,17 +451,27 @@ export class MinterGuardService { // Addresses still in this cycle's candidate set (handled by the deny path when actionable). const candidateAddressSet = new Set(candidates.map((m) => m.address.toLowerCase())); - // Resolve pass BEFORE pre-check: read each on-chain deadline so (a) already-resolved minters do - // not inflate the under-quorum candidate count and (b) urgency order can replace database order. + // Resolve pass BEFORE pre-check: filter already-resolved minters, order by urgency, and give the + // pre-check an honest actionable candidate count. Deadlines read here are NOT authoritative for the + // send — the mapping can change while the cycle runs (another actor may deny mid-loop); the send + // loop re-reads minters(address) immediately before each deny (see send loop). const workingSet: ResolvedCandidate[] = []; if (candidates.length > 0) { this.logger.warn(`Found ${candidates.length} unwhitelisted PROPOSED minter(s) to deny`); + let resolveReads = 0; + let resolveTruncated = false; for (const minter of candidates) { + // Cap serial RPC so this pass cannot outgrow the 5-minute cadence (see MAX_RESOLVE_READS_PER_CYCLE). + if (resolveReads >= MAX_RESOLVE_READS_PER_CYCLE) { + resolveTruncated = true; + break; + } const address = ethers.getAddress(minter.address); const addrLc = address.toLowerCase(); let onChainDeadline: bigint; try { + resolveReads++; onChainDeadline = BigInt(await juiceDollar.minters(address)); } catch (error) { // Sustained RPC fault must not let a veto window expire in silence — rate-limited page. @@ -416,7 +481,7 @@ export class MinterGuardService { 'deadline', `⚠️ *Minter guard could not read deny deadline*\n\n` + `Address: \`${address}\`\n` + - `Detail: ${this.escapeMarkdown(errorMsg)}\n\n` + + `Detail: ${escapeMarkdownText(errorMsg)}\n\n` + `The on-chain application deadline could not be read this cycle; the candidate is deferred ` + `(not marked done). Investigate RPC if this persists — a silent window expiry must not happen.` ); @@ -434,8 +499,24 @@ export class MinterGuardService { ); continue; } + // INVARIANT: every minter the guard has ever considered deniable (non-zero on-chain deadline) + // is tracked in denyState. The sweep iterates denyState only; without this entry a candidate + // that hits a continue path (confirmation-budget deferral, getBlock failure) or an ok:false + // pre-check that returns before the send loop would be invisible to the sweep — and once + // syncMinters flips the row to APPROVED the veto window can pass with no page at all. + // Do not touch an existing entry (attempts/done/pendingAlert must survive). + if (this.denyState.get(addrLc) === undefined) { + this.denyState.set(addrLc, { attempts: 0 }); + } workingSet.push({ address, onChainDeadline }); } + if (resolveTruncated) { + const notExamined = candidates.length - resolveReads; + this.logger.warn( + `MinterGuard: resolve pass capped at ${MAX_RESOLVE_READS_PER_CYCLE} minters() reads; ` + + `${notExamined} candidate(s) not examined this cycle (truncated — not a completed pass)` + ); + } } // Nothing actionable: skip pre-check (would page "N left undenied" about minters that need nothing). @@ -454,29 +535,47 @@ export class MinterGuardService { let confirmBudgetSpentMs = 0; let deferDeniesLogged = false; - for (const { address, onChainDeadline } of workingSet) { + for (const { address, onChainDeadline: resolveDeadline } of workingSet) { const addrLc = address.toLowerCase(); - // Just-in-time TooLate guard. denyMinter reverts TooLate once block.timestamp > - // minters[_minter] (the on-chain validityStart set by suggestMinter). Deadline was already - // read in the resolve pass; re-read only the live block timestamp so the comparison advances - // during the cycle (which is what this check is for). + // Just-in-time TooLate / already-resolved guard. denyMinter reverts TooLate once + // block.timestamp > minters[_minter]. Re-read BOTH the live block timestamp and the + // on-chain deadline immediately before send: the resolve-pass deadline is stale once + // another actor denies mid-cycle (mapping deleted → 0) while we wait on a previous + // candidate's confirmation. Sending with a stale deadline reverts, marks the minter + // permanently failed, and pages "manual denyMinter() required" for a minter already denied. + // currentDeadline is the authoritative value for buffer comparison and windowClosed text. let latestBlock: ethers.Block | null = null; + let currentDeadline: bigint; try { latestBlock = await this.providerService.provider.getBlock('latest'); + currentDeadline = BigInt(await juiceDollar.minters(address)); } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); - this.logger.error(`MinterGuard skip ${address}: failed to read latest block for TooLate pre-check: ${errorMsg}`); + this.logger.error( + `MinterGuard skip ${address}: failed to re-read latest block / minters for TooLate pre-check: ${errorMsg}` + ); continue; } if (!latestBlock) { this.logger.error(`MinterGuard skip ${address}: provider.getBlock('latest') returned null for TooLate pre-check`); continue; } - if (BigInt(latestBlock.timestamp) + DENY_TOOLATE_BUFFER_SECONDS >= onChainDeadline) { + // Resolved by someone else mid-cycle: mapping entry gone — mark done, no alert (false-alarm class). + if (currentDeadline === 0n) { + const prev = this.denyState.get(addrLc) || { attempts: 0 }; + this.denyState.set(addrLc, { ...prev, done: true }); + this.logger.warn( + `MinterGuard skip ${address}: on-chain minters(address)=0 at send time ` + + `(resolved by another actor mid-cycle; resolve-pass deadline was ${resolveDeadline}) — ` + + `marking done without alert` + ); + continue; + } + if (BigInt(latestBlock.timestamp) + DENY_TOOLATE_BUFFER_SECONDS >= currentDeadline) { this.logger.warn( `MinterGuard skip ${address}: deny window closed or about to close ` + - `(block ts ${latestBlock.timestamp} + ${DENY_TOOLATE_BUFFER_SECONDS}s buffer >= on-chain deadline ${onChainDeadline})` + `(block ts ${latestBlock.timestamp} + ${DENY_TOOLATE_BUFFER_SECONDS}s buffer >= on-chain deadline ${currentDeadline})` ); const prev = this.denyState.get(addrLc) || { attempts: 0 }; const next: DenyStateEntry = { ...prev, done: true }; @@ -486,8 +585,9 @@ export class MinterGuardService { const alertMsg = `⚠️ *Unwhitelisted minter passing unchallenged*\n\n` + `Address: \`${address}\`\n` + - `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + - `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.`; + `The application period has closed or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s ` + + `safety buffer — a deny will no longer be attempted; the minter will pass unless it ` + + `is a bridge that can be handled otherwise.`; await this.deliverTerminalAlert(addrLc, next, alertMsg); } continue; @@ -496,6 +596,7 @@ export class MinterGuardService { // Remaining confirmation budget too small to be useful: defer new denies (not fail) so later // watchers still run and the next cycle can finish the rest. Do not mark done; do not page. // JIT closed-window / already-resolved paths above still run for every candidate. + // Entry was registered in the resolve pass so the sweep still sees this deferred minter. const remainingBudgetMs = DENY_CYCLE_CONFIRM_BUDGET_MS - confirmBudgetSpentMs; if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { if (!deferDeniesLogged) { @@ -542,13 +643,13 @@ export class MinterGuardService { const prev = this.denyState.get(addrLc) || { attempts: 0 }; this.denyState.set(addrLc, { attempts: prev.attempts, done: true }); this.logger.warn(`denyMinter confirmed for ${address}: block=${receipt.blockNumber}`); - const delivered = await this.telegramService.sendCriticalAlert( + const successMsg = `🛡️ *Minter auto-denied*\n\n` + - `Address: \`${address}\`\n` + - `Tx: \`${txHash}\`\n` + - `Block: ${receipt.blockNumber}\n` + - `Message: ${this.escapeMarkdown(message)}` - ); + `Address: \`${address}\`\n` + + `Tx: \`${txHash}\`\n` + + `Block: ${receipt.blockNumber}\n` + + `Message: ${escapeMarkdownText(message)}`; + const delivered = await this.telegramService.sendCriticalAlert(this.truncateAlertBody(successMsg)); // Success page does not need pendingAlert retry (deny is already on-chain and marked done), // but log loud when delivery fails so the gap is visible. if (!delivered) { @@ -581,17 +682,18 @@ export class MinterGuardService { // FAILED critical alert ONLY on the terminal state for this minter, and only once. // NotQualified must not produce a per-attempt page — the precheck owns that page. // Non-terminal transient failures log at error level and stay silent on Telegram. - // windowClosed uses the on-chain deadline so remedy text stays truthful. + // windowClosed uses the live currentDeadline (not the resolve-pass value) so remedy + // text stays truthful after a mid-cycle mapping change. if (done && !prev.alerted) { - const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= onChainDeadline; + const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= currentDeadline; const remedy = windowClosed ? 'The application period has ended — denyMinter is impossible; challenge/handle the minter otherwise if needed.' : 'Manual denyMinter() required before the application period ends.'; const alertMsg = `⚠️ *Minter auto-deny FAILED*\n\n` + `Address: \`${address}\`\n` + - `Class: ${this.escapeMarkdown(classification.label)} (${classification.kind})\n` + - `Detail: ${this.escapeMarkdown(classification.detail)}\n` + + `Class: ${escapeMarkdownText(classification.label)} (${classification.kind})\n` + + `Detail: ${escapeMarkdownText(classification.detail)}\n` + `Attempts: ${attempts}/${MAX_DENY_ATTEMPTS}\n\n` + remedy; await this.deliverTerminalAlert(addrLc, { ...next, done: true }, alertMsg); @@ -607,6 +709,28 @@ export class MinterGuardService { // Pending terminal pages after deny work — notifications are not time-critical; a veto window is. await this.retryPendingAlerts(); + + // INVARIANT: every address OBSERVED this cycle (candidateAddressSet, built from the PROPOSED / + // non-whitelisted / not-yet-done query above) must hold a denyState entry by now — "observation + // implies tracking". The resolve pass above is what establishes this for a successfully resolved + // candidate; this assertion exists because three separate review rounds found the same class of + // silent pass-through (a candidate observed but never tracked, and therefore invisible to + // sweepPassedUnchallenged once syncMinters relabels it APPROVED). A violation here means some path + // still drops a candidate without recording it — fail loud rather than patch a fourth time. + const untracked = [...candidateAddressSet].filter((addrLc) => this.denyState.get(addrLc) === undefined); + if (untracked.length > 0) { + this.logger.error( + `MinterGuard INVARIANT VIOLATED (observation implies tracking): ${untracked.length} candidate(s) ` + + `observed this cycle have no denyState entry: ${untracked.join(', ')}` + ); + await this.maybeAlertSkip( + 'invariant', + `⚠️ *Minter guard invariant violated*\n\n` + + `${untracked.length} candidate(s) observed this cycle were never tracked in denyState ` + + `(invariant: observation implies tracking): ${untracked.join(', ')}\n\n` + + `This is a monitoring code defect, not a chain event — investigate the resolve/send paths.` + ); + } } /** @@ -619,6 +743,10 @@ export class MinterGuardService { * out of candidates, and the "passing unchallenged" page never fires. This sweep covers exactly * those previously tracked addresses without widening the candidate query to APPROVED (which would * page once for every legitimately approved unwhitelisted minter on first run). + * + * Read count is capped (MAX_SWEEP_READS_PER_CYCLE) so serial minters() calls cannot stretch a cycle + * past the 5-minute cadence. Resume is round-robin via sweepResumeAfter: with a fixed start, entries + * beyond the cap would never be examined again. */ private async sweepPassedUnchallenged(juiceDollar: ethers.Contract, candidateAddresses: Set): Promise { let latestBlock: ethers.Block | null = null; @@ -635,19 +763,49 @@ export class MinterGuardService { } const blockTs = BigInt(latestBlock.timestamp); - for (const [addrLc, state] of this.denyState) { - if (state.done) continue; + // Snapshot keys once so round-robin index math is stable even if denyState mutates mid-pass. + const keys = [...this.denyState.keys()]; + if (keys.length === 0) return; + + // Start after the address the previous capped pass stopped at (wrap around). If the cursor is + // gone (entry removed) or unset, start at index 0. + let startIdx = 0; + if (this.sweepResumeAfter !== undefined) { + const cursorIdx = keys.indexOf(this.sweepResumeAfter); + if (cursorIdx >= 0) { + startIdx = (cursorIdx + 1) % keys.length; + } + } + + let sweepReads = 0; + let lastExamined: string | undefined; + let truncated = false; + // Eligible keys we walked past without a minters() read (done / still-candidate) do not count + // toward the read cap; only actual RPC reads do. + for (let i = 0; i < keys.length; i++) { + const addrLc = keys[(startIdx + i) % keys.length]; + const state = this.denyState.get(addrLc); + // Entry may have been removed (unlikely) or already marked done earlier in this pass. + if (!state || state.done) continue; // Still in this cycle's candidate set — handled by the normal deny path. if (candidateAddresses.has(addrLc)) continue; + if (sweepReads >= MAX_SWEEP_READS_PER_CYCLE) { + truncated = true; + break; + } + const address = ethers.getAddress(addrLc); let onChainDeadline: bigint; try { + sweepReads++; + lastExamined = addrLc; onChainDeadline = BigInt(await juiceDollar.minters(address)); } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); this.logger.error(`MinterGuard sweep: failed to read minters(${address}): ${errorMsg}`); - // Leave entry untouched — retries next cycle. + // Leave entry untouched — retries next cycle. Still advance resume so a bad RPC on one + // address cannot pin the cursor forever. continue; } @@ -678,11 +836,39 @@ export class MinterGuardService { const alertMsg = `⚠️ *Unwhitelisted minter passing unchallenged*\n\n` + `Address: \`${address}\`\n` + - `The application period has closed (or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s buffer) — ` + - `denyMinter is impossible; the minter will pass unless it is a bridge that can be handled otherwise.`; + `The application period has closed or is within the ${DENY_TOOLATE_BUFFER_SECONDS}s ` + + `safety buffer — a deny will no longer be attempted; the minter will pass unless it ` + + `is a bridge that can be handled otherwise.`; await this.deliverTerminalAlert(addrLc, next, alertMsg); } } + + // Remember where we stopped so the next cycle continues after this address (round-robin). + // A completed full walk still advances the cursor to the last examined key so the rotation + // keeps moving when the set stays larger than the cap across restarts of the pass. + if (lastExamined !== undefined) { + this.sweepResumeAfter = lastExamined; + } + + if (truncated) { + // Count still-eligible keys after lastExamined until we wrap back to this pass's startIdx — + // those were not examined this cycle. No extra RPC. + let notExamined = 0; + const afterLast = lastExamined !== undefined ? (keys.indexOf(lastExamined) + 1) % keys.length : startIdx; + for (let i = 0; i < keys.length; i++) { + const idx = (afterLast + i) % keys.length; + if (idx === startIdx) break; + const addrLc = keys[idx]; + const state = this.denyState.get(addrLc); + if (!state || state.done) continue; + if (candidateAddresses.has(addrLc)) continue; + notExamined++; + } + this.logger.warn( + `MinterGuard: sweep capped at ${MAX_SWEEP_READS_PER_CYCLE} minters() reads; ` + + `${notExamined} tracked minter(s) not examined this cycle (truncated — not a completed pass)` + ); + } } /** @@ -856,8 +1042,8 @@ export class MinterGuardService { 'precheck', `⚠️ *Minter guard pre-check failed — qualification unknown*\n\n` + `${candidates.length} unwhitelisted PROPOSED minter(s) left undenied this cycle.\n` + - `Qualification could not be determined (class: ${this.escapeMarkdown(classification.label)}).\n` + - `Detail: ${this.escapeMarkdown(classification.detail)}\n\n` + + `Qualification could not be determined (class: ${escapeMarkdownText(classification.label)}).\n` + + `Detail: ${escapeMarkdownText(classification.detail)}\n\n` + `The guard will retry next cycle; investigate RPC / helper set if this persists.` ); } @@ -868,125 +1054,180 @@ export class MinterGuardService { /** * Rate-limited skip page: at most one per kind per SKIP_ALERT_COOLDOWN_MS (in-memory, reset on restart). * Kinds have independent timers so one class of page cannot suppress another. - * Stamps the cooldown timer only when the page was delivered (or when telegram is disabled — nothing - * to deliver to and retrying every cycle would only spam the log). A failed delivery does NOT arm - * the cooldown so the next cycle can retry once Telegram recovers. + * + * Cooldown arming: + * - SUCCESS (or telegram disabled): stamp full SKIP_ALERT_COOLDOWN_MS — a delivered page must not + * be repeated for an hour. + * - FAILED delivery: stamp a short SKIP_ALERT_RETRY_BACKOFF_MS window so the page is retried, but + * not on every 5-minute cycle (which would be twelve attempts an hour, and per-candidate deadline + * pages could fire once per failing candidate within a single cycle's retries across cycles). + * Trade-off explicit: delivered → quiet for an hour; failed → bounded retry; never a per-cycle loop. */ private async maybeAlertSkip(kind: SkipAlertKind, message: string): Promise { const nowMs = Date.now(); const lastAt = this.lastSkipAlertAt[kind]; if (nowMs - lastAt < SKIP_ALERT_COOLDOWN_MS) return; - // Telegram disabled: nothing to deliver to — stamp so we do not re-log every cycle; error log is the record. + // Truncate every skip-page body too — not just terminal pages (see truncateAlertBody). + const body = this.truncateAlertBody(message); + + // Telegram disabled: nothing to deliver to — stamp full cooldown so we do not re-log every cycle. if (!this.telegramService.alertsEnabled) { this.lastSkipAlertAt[kind] = nowMs; - this.logger.error(`MinterGuard skip page not deliverable (telegram disabled): ${message}`); + this.logger.error(`MinterGuard skip page not deliverable (telegram disabled): ${body}`); return; } - const delivered = await this.telegramService.sendCriticalAlert(message); + const delivered = await this.telegramService.sendCriticalAlert(body); if (delivered) { this.lastSkipAlertAt[kind] = nowMs; } else { - // Do not stamp: page failed and will be retried on the next cycle once Telegram recovers. - this.logger.error(`MinterGuard skip page failed delivery (kind=${kind}); will retry next cycle: ${message}`); + // Short backoff only: next attempt after SKIP_ALERT_RETRY_BACKOFF_MS (see method comment). + // lastAt is stored such that (now - lastAt) reaches SKIP_ALERT_COOLDOWN_MS after the backoff. + this.lastSkipAlertAt[kind] = nowMs - SKIP_ALERT_COOLDOWN_MS + SKIP_ALERT_RETRY_BACKOFF_MS; + this.logger.error( + `MinterGuard skip page failed delivery (kind=${kind}); will retry after ` + + `${SKIP_ALERT_RETRY_BACKOFF_MS}ms backoff: ${body}` + ); } } /** - * Read-only status for the GET /guard endpoint. Fail-LOUD: a genuine on-chain read error throws (5xx) - * rather than faking 0%/false — the skip+alert graceful path lives only in the deny flow, never here. - * The private key never leaves the backend; only the derived signer address is exposed. + * Qualification half of GET /guard (and the startup probe): helper derivation, votesDelegated verdict + * with seed-less retry, NoRevertData rethrow, and additive display fallback only when the contract + * rejected the helper list. + * + * ONE SNAPSHOT when votesDelegated answers: that single number is both votingPower (for + * votingPowerPct) and the qualified verdict; totalVotes comes from the same call sequence so the + * ratio is internally consistent. An additive votes() sum is ONLY an estimate when the contract + * refused (qualified is then false anyway) — never a second live read that can disagree with the + * verdict after a helper moved equity between calls. + * + * Extracted so probeQualification does not depend on gas reads (getBalance / getFeeData) that live + * only in getStatus — a gas-read failure must not suppress the under-quorum page. */ - async getStatus(): Promise { - const chainId = this.config.blockchainId; - const equityAddress = ADDRESS[chainId].equity; - - if (!this.enabled || !this.signerAddress) { - return { - enabled: false, - signerAddress: ethers.ZeroAddress, - votingPowerPct: '0', - quorumPct: Number(QUORUM_BPS) / 100, - qualified: false, - helperCount: 0, - gasBalance: '0', - estimatedDenyCost: '0', - gasEnough: false, - equityAddress, - chainId, - }; + private async evaluateQualification(): Promise<{ + qualified: boolean; + votingPowerPct: string; + helperCount: number; + totalVotes: bigint; + votingPower: bigint; + }> { + const signerAddress = this.signerAddress; + if (!signerAddress) { + throw new Error('MinterGuard evaluateQualification: signerAddress missing while guard is enabled'); } - const signerAddress = this.signerAddress; - const provider = this.providerService.provider; + const chainId = this.config.blockchainId; + const equityAddress = ADDRESS[chainId].equity; const equity = new ethers.Contract(equityAddress, EquityABI, this.providerService.multicallProvider); - // Additive, revert-proof voting power for DISPLAY (votingPowerPct): votes(signer) + Σ votes(helper). - // This percentage is a display estimate over the helper set actually used for the contract verdict; - // `qualified` is the contract's own verdict via votesDelegated. Both now refer to the same set - // (recomputed below when a seed-less retry switches the active helper set) so the percentage and - // the verdict cannot disagree with no explanation. + const denyIface = this.denyErrorInterface; + if (!denyIface) throw new Error('MinterGuard evaluateQualification: denyErrorInterface missing while guard is enabled'); + const delegations = await this.eventsRepo.getDelegations(); const helpers = computeHelpers(delegations, signerAddress, this.helperSeed); - const voteResults = await this.providerService.callBatch([ - () => equity.totalVotes(), - ...[signerAddress, ...helpers].map((a) => () => equity.votes(a)), - ]); - const totalVotes: bigint = BigInt(voteResults[0]); - let votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); - - // Contract's own verdict for `qualified` — exact value checkQualified uses. - // On-chain rejection of the helper list (EmptyRevert / decoded contract error): retry seed-less once. - // NoRevertData is a transport/client failure — rethrow so the endpoint 5xxes (fail-loud). - const denyIface = this.denyErrorInterface; - if (!denyIface) throw new Error('MinterGuard getStatus: denyErrorInterface missing while guard is enabled'); let activeHelpers = helpers; - let qualified: boolean; + // Definite assignment: every path below either sets these via votesDelegated or the additive fallback. + let qualified!: boolean; + let votingPower!: bigint; + let totalVotes!: bigint; + // true when votesDelegated answered (primary or seed-less); false when only the additive estimate remains. + let contractAnswered = false; + try { + // Same sequence: totalVotes then votesDelegated — single snapshot for ratio + verdict. + totalVotes = BigInt(await equity.totalVotes()); const delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, helpers)); + votingPower = delegatedVotes; qualified = delegatedVotes * 10000n >= QUORUM_BPS * totalVotes; + contractAnswered = true; } catch (error) { const classification = classifyDenyError(error, denyIface); if (classification.label === 'NoRevertData') { - // Transport/client failure, not an on-chain rejection — fail loud for the endpoint. + // Transport/client failure, not an on-chain rejection — fail loud for the endpoint / probe. throw error; } // EmptyRevert or decoded contract error: helper list rejected on-chain. if (this.helperSeed.length > 0) { const seedLess = computeHelpers(delegations, signerAddress); try { + totalVotes = BigInt(await equity.totalVotes()); const delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, seedLess)); activeHelpers = seedLess; + votingPower = delegatedVotes; qualified = delegatedVotes * 10000n >= QUORUM_BPS * totalVotes; + contractAnswered = true; } catch (retryError) { const retryClass = classifyDenyError(retryError, denyIface); if (retryClass.label === 'NoRevertData') throw retryError; // Still rejected: a real deny would also revert — report qualified:false truthfully. this.logger.warn( - `MinterGuard getStatus: votesDelegated rejected helper set (${retryClass.label}): ${retryClass.detail}` + `MinterGuard evaluateQualification: votesDelegated rejected helper set (${retryClass.label}): ${retryClass.detail}` ); qualified = false; activeHelpers = seedLess; } } else { this.logger.warn( - `MinterGuard getStatus: votesDelegated rejected helper set (${classification.label}): ${classification.detail}` + `MinterGuard evaluateQualification: votesDelegated rejected helper set (${classification.label}): ${classification.detail}` ); qualified = false; } } - // If the contract path switched to a different helper set, recompute the additive display sum - // over that set so votingPowerPct, qualified and helperCount all describe the same helpers. - if (activeHelpers !== helpers) { - const powerResults = await this.providerService.callBatch( - [signerAddress, ...activeHelpers].map((a) => () => equity.votes(a)) - ); - votingPower = powerResults.reduce((sum, v) => sum + BigInt(v), 0n); + // Contract refused: additive votes() batch is display-only estimate; qualified stays false. + // totalVotes and votingPower come from this same batch so the ratio stays self-consistent. + if (!contractAnswered) { + const voteResults = await this.providerService.callBatch([ + () => equity.totalVotes(), + ...[signerAddress, ...activeHelpers].map((a) => () => equity.votes(a)), + ]); + totalVotes = BigInt(voteResults[0]); + votingPower = voteResults.slice(1).reduce((sum, v) => sum + BigInt(v), 0n); } + return { + qualified, + votingPowerPct: formatVotingPowerPct(votingPower, totalVotes), + helperCount: activeHelpers.length, + totalVotes, + votingPower, + }; + } + + /** + * Read-only status for the GET /guard endpoint. Fail-LOUD: a genuine on-chain read error throws (5xx) + * rather than faking 0%/false — the skip+alert graceful path lives only in the deny flow, never here. + * The private key never leaves the backend; only the derived signer address is exposed. + * Qualification comes from evaluateQualification(); gas fields are appended here only. + */ + async getStatus(): Promise { + const chainId = this.config.blockchainId; + const equityAddress = ADDRESS[chainId].equity; + + if (!this.enabled || !this.signerAddress) { + return { + enabled: false, + signerAddress: ethers.ZeroAddress, + votingPowerPct: '0', + quorumPct: Number(QUORUM_BPS) / 100, + qualified: false, + helperCount: 0, + gasBalance: '0', + estimatedDenyCost: '0', + gasEnough: false, + equityAddress, + chainId, + }; + } + + const signerAddress = this.signerAddress; + const provider = this.providerService.provider; + + const qual = await this.evaluateQualification(); + // Gas status: model denyMinter() cost with a fixed gas ceiling * live fee (see DENY_GAS_ESTIMATE). const balance: bigint = await provider.getBalance(signerAddress); const feeData = await provider.getFeeData(); @@ -997,10 +1238,10 @@ export class MinterGuardService { return { enabled: true, signerAddress, - votingPowerPct: formatVotingPowerPct(votingPower, totalVotes), + votingPowerPct: qual.votingPowerPct, quorumPct: Number(QUORUM_BPS) / 100, - qualified, - helperCount: activeHelpers.length, + qualified: qual.qualified, + helperCount: qual.helperCount, gasBalance: ethers.formatEther(balance), estimatedDenyCost: ethers.formatEther(estimatedDenyCost), gasEnough: balance >= estimatedDenyCost, diff --git a/src/monitoringV2/monitoring.service.ts b/src/monitoringV2/monitoring.service.ts index f0fee19..4099f80 100644 --- a/src/monitoringV2/monitoring.service.ts +++ b/src/monitoringV2/monitoring.service.ts @@ -12,7 +12,7 @@ import { CollateralService } from './collateral.service'; import { MinterService } from './minter.service'; import { MinterGuardService, GuardConfigError } from './minter-guard.service'; import { JusdService } from './jusd.service'; -import { TelegramService } from './telegram.service'; +import { TelegramService, escapeMarkdownText } from './telegram.service'; @Injectable() export class MonitoringService implements OnModuleInit { @@ -54,11 +54,18 @@ export class MonitoringService implements OnModuleInit { if (error instanceof GuardConfigError) throw error; const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); this.logger.error(`MinterGuard init failed — guard DISABLED, monitoring continues: ${errorMsg}`, error?.stack || error); - await this.telegramService.sendCriticalAlert( + // One-shot bootstrap path, not a per-cycle watcher: no retry machinery here (unlike the guard's + // own per-cycle pendingAlert retry) — this logger.error is the durable record if delivery fails. + const delivered = await this.telegramService.sendCriticalAlert( `⚠️ *Minter guard init failed — guard DISABLED*\n\n` + `The auto-deny guard is OFF for this run; monitoring continues.\n` + - `Error: ${errorMsg}` + `Error: ${escapeMarkdownText(errorMsg)}` ); + if (!delivered) { + this.logger.error( + `MinterGuard init-failure page could not be delivered — guard is OFF for this run with no notification sent` + ); + } } await this.jusdService.initialize(); setTimeout(() => this.runMonitoring(), 5000); diff --git a/src/monitoringV2/telegram.service.ts b/src/monitoringV2/telegram.service.ts index cecd5a7..a6e9abc 100644 --- a/src/monitoringV2/telegram.service.ts +++ b/src/monitoringV2/telegram.service.ts @@ -293,3 +293,14 @@ export class TelegramService implements OnModuleInit, OnModuleDestroy { return new Promise((resolve) => setTimeout(resolve, ms)); } } + +/** + * Escape Telegram legacy-Markdown specials so dynamic provider/config text cannot poison + * parse_mode: 'Markdown' delivery (Telegram rejects malformed entities, which would make those + * error classes permanently undeliverable and retry the same unsendable text forever). Escapes + * `_`, `*`, backtick and `[` by prefixing each with a backslash. Mirrors the established pattern + * used for `[` in TelegramService.envTag(). + */ +export function escapeMarkdownText(value: string): string { + return value.replace(/([_*`\[])/g, '\\$1'); +} From 17870217e9bfba8d5b42891cafc23a6bba4ac433 Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 04:58:26 +0200 Subject: [PATCH 07/11] fix(minter-guard): make registration RPC-free and bound the work per cycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fourth review round. Round 3's invariant only half held: a candidate was registered AFTER its on-chain deadline read, and that pass is capped — so a candidate whose read failed, or which sat beyond the cap, was still never tracked. Worse, the end-of-cycle assertion checked the uncapped candidate set, so ordinary truncation was reported as a code defect: it would have taught the operator to ignore the one page that says the guard is broken. Registration is bookkeeping, so nothing that can fail may come before it. Every candidate is now registered before any network call, and the candidate list is sorted by the deadline derivable from the indexed row before the cap applies, so a window with minutes left can no longer sit behind twenty-five with days left. The chain read stays authoritative for the decision; the row only decides who is examined first. Bounding the work: - The two RPC passes now have a wall-clock budget as well as a call count. A count cap does not stop twenty-five sequential reads from consuming the configured 60-second RPC timeout each, which would run a cycle ~25 minutes past its cadence and suppress several scheduled ticks while veto windows keep moving. - The gas ceiling that guarantees a shortfall pages before a doomed send now scales with the helper count. It modelled a call that loops over an unbounded helper list with a flat constant, so the balance check could pass while the transaction ran out of gas. Alerting and diagnosis: - A skip page whose delivery failed is retained and retried like a terminal page and counted in the backlog. Previously it was only stamped with a backoff, so it was delivered solely if its condition happened to recur — and when the candidate had meanwhile been approved, it never did. - A replaced or repriced transaction is no longer reported as a mined revert. Those errors carry a receipt describing the replacement, so the old check (receipt present) could state the opposite of the truth: the replacement may have succeeded. The revert branch now also requires a failed receipt status. - Before claiming manual intervention is required, the mapping is re-read. If another actor denied the minter in between, the guard records that and stays quiet instead of paging about a minter that is already denied. - The window-closed remedy uses the chain timestamp and the contract's strict comparison rather than local time, so the text cannot contradict what the contract would do. - The startup page honours its delivery result, like its three siblings. - Truncation moved next to the escaper in the telegram service so every alert site can reach it, including the bootstrap page whose body embeds a configured path of unbounded length. Two comments corrected to match the code: a failed page is retried at the end of the same cycle, not only the next one, and the qualification values come from two sequential contract reads rather than one atomic snapshot — this deployment has no Multicall3. --- src/monitoringV2/minter-guard.logic.spec.ts | 32 ++ src/monitoringV2/minter-guard.logic.ts | 55 +++- src/monitoringV2/minter-guard.service.ts | 313 +++++++++++++++----- src/monitoringV2/monitoring.service.ts | 13 +- src/monitoringV2/telegram.service.ts | 18 ++ 5 files changed, 335 insertions(+), 96 deletions(-) diff --git a/src/monitoringV2/minter-guard.logic.spec.ts b/src/monitoringV2/minter-guard.logic.spec.ts index 47c50d2..0792fcb 100644 --- a/src/monitoringV2/minter-guard.logic.spec.ts +++ b/src/monitoringV2/minter-guard.logic.spec.ts @@ -269,6 +269,38 @@ describe('classifyDenyError', () => { expect(result.label).toBe('TooLate'); }); + it('classifies TRANSACTION_REPLACED as TransactionReplaced (transient), not a mined revert', () => { + // ethers v6 TRANSACTION_REPLACED carries a receipt for the REPLACEMENT, which may have + // status===1 — must not be reported as "mined and reverted". + const hash = '0x' + 'ef'.repeat(32); + const error = { + message: 'transaction was replaced', + code: 'TRANSACTION_REPLACED', + reason: 'repriced', + replacement: { hash }, + receipt: { status: 1, hash }, + }; + const result = classifyDenyError(error, iface); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('TransactionReplaced'); + expect(result.detail).toContain(hash); + expect(result.detail).toContain('repriced'); + expect(result.detail).not.toMatch(/reverted/i); + }); + + it('does not classify a success receipt (status===1) without revert data as RevertedOnChain', () => { + // Tightened mined-revert branch requires receipt.status === 0; a status===1 receipt alone + // must fall through (no data, no CALL_EXCEPTION / missing-revert marker → NoRevertData). + const error = { + message: 'something went wrong after send', + receipt: { status: 1, hash: '0x' + '11'.repeat(32) }, + }; + const result = classifyDenyError(error, iface); + expect(result.label).not.toBe('RevertedOnChain'); + expect(result.kind).toBe('transient'); + expect(result.label).toBe('NoRevertData'); + }); + it('classifies NETWORK_ERROR with no data as NoRevertData and names the code', () => { const error = { message: 'could not detect network', code: 'NETWORK_ERROR' }; const result = classifyDenyError(error, iface); diff --git a/src/monitoringV2/minter-guard.logic.ts b/src/monitoringV2/minter-guard.logic.ts index c567ef5..2289527 100644 --- a/src/monitoringV2/minter-guard.logic.ts +++ b/src/monitoringV2/minter-guard.logic.ts @@ -5,7 +5,9 @@ export const QUORUM_BPS = 200n; export interface DenyErrorClass { kind: 'permanent' | 'transient'; - label: string; // 'TooLate' | 'NotQualified' | 'EmptyRevert' | 'RevertedOnChain' | 'NoRevertData' | a decoded error name | 'Unknown' + // 'TooLate' | 'NotQualified' | 'EmptyRevert' | 'RevertedOnChain' | 'TransactionReplaced' | + // 'NoRevertData' | a decoded error name | 'Unknown' + label: string; detail: string; // human-readable diagnosis for logs + Telegram } @@ -78,14 +80,16 @@ export function computeHelpers(delegations: Array<{ from: string; to: string }>, /** * Classifies a failed denyMinter/votesDelegated error into permanent vs transient with a diagnosis. * - * Separates three failure surfaces: + * Separates four failure surfaces: * - eth_call / estimateGas bare require (empty revert data) — helper-list rejection before send, * - mined receipt with status === 0 — ethers reports data:null even when a custom error fired, + * - TRANSACTION_REPLACED — the guard's tx was repriced/cancelled/replaced; the replacement's outcome + * decides what actually happened (must not be misread as a mined revert of the original), * - client/transport/account failure — no on-chain revert marker at all. * * Permanent (TooLate): the application window has closed — retrying forever is useless and would only - * burn gas + page. Transient: under-quorum, helper-list rejection, mined-but-reverted, RPC blips, - * unknown — may recover next cycle (or after operator action) within the attempt cap. + * burn gas + page. Transient: under-quorum, helper-list rejection, mined-but-reverted, replaced tx, + * RPC blips, unknown — may recover next cycle (or after operator action) within the attempt cap. */ export function classifyDenyError(error: unknown, iface: ethers.Interface): DenyErrorClass { const err = error as any; @@ -160,12 +164,37 @@ export function classifyDenyError(error: unknown, iface: ethers.Interface): Deny }; } - // 2. Mined receipt revert (ethers checkReceipt: status===0 throws CALL_EXCEPTION with data:null - // and a receipt). Distinct from eth_call empty-data EmptyRevert — NOT a helper-list signal. - // kind stays transient: the authoritative on-chain window check at the start of the next cycle - // decides whether anything is still deniable. + // 2. TRANSACTION_REPLACED: ethers attaches a receipt for the REPLACEMENT, not a status===0 proof + // that the guard's original tx reverted. Classifying this as RevertedOnChain is wrong (and can be + // the opposite of the truth when the replacement succeeded). Transient: the next cycle's on-chain + // deadline read decides whether anything is still deniable. + if (err?.code === 'TRANSACTION_REPLACED') { + let hashSuffix = ''; + if (typeof err?.replacement?.hash === 'string') { + hashSuffix = ` replacement=${err.replacement.hash}`; + } else if (typeof err?.receipt?.hash === 'string') { + hashSuffix = ` replacement=${err.receipt.hash}`; + } + const reasonSuffix = typeof err?.reason === 'string' && err.reason.length > 0 ? ` reason=${err.reason}` : ''; + return { + kind: 'transient', + label: 'TransactionReplaced', + detail: + "The guard's transaction was replaced or repriced; the replacement's outcome decides what " + + "actually happened on-chain. The next cycle's on-chain deadline read determines whether " + + 'anything is still deniable.' + + reasonSuffix + + hashSuffix, + }; + } + + // 3. Mined receipt revert (ethers checkReceipt: status===0 throws CALL_EXCEPTION with data:null + // and a receipt). Require status === 0 so a success receipt carried on some other error shape is + // not misclassified as a revert. Distinct from eth_call empty-data EmptyRevert — NOT a helper-list + // signal. kind stays transient: the authoritative on-chain window check at the start of the next + // cycle decides whether anything is still deniable. const receipt = err?.receipt; - if (receipt !== null && typeof receipt === 'object') { + if (receipt !== null && typeof receipt === 'object' && receipt.status === 0) { // ethers receipt uses `.hash`; some shapes expose `.transactionHash` instead. let hashSuffix = ''; if (typeof receipt.hash === 'string') { @@ -186,22 +215,22 @@ export function classifyDenyError(error: unknown, iface: ethers.Interface): Deny }; } - // 3. A data candidate exists and is exactly empty hex ('0x' / '0X') -> real empty-data eth_call revert. + // 4. A data candidate exists and is exactly empty hex ('0x' / '0X') -> real empty-data eth_call revert. if (data === '0x' || data === '0X') { return emptyRevert; } - // 4. No data candidate, but still recognisably an on-chain revert -> EmptyRevert. + // 5. No data candidate, but still recognisably an on-chain revert -> EmptyRevert. // ethers v6 surfaces a data-less CALL_EXCEPTION / "missing revert data" this way, which is what // distinguishes a real empty-data eth_call revert from a client/transport failure (no data, no - // revert marker). Reached only when there is no mined receipt (step 2 above). + // revert marker). Reached only when there is no mined failed receipt (step 3 above). const code = err?.code; const isOnChainEmptyRevert = message.toLowerCase().includes('missing revert data') || code === 'CALL_EXCEPTION'; if (isOnChainEmptyRevert) { return emptyRevert; } - // 5. No data candidate and no revert marker -> client/transport/account failure, not a helper-list + // 6. No data candidate and no revert marker -> client/transport/account failure, not a helper-list // rejection. This class exists so we do NOT attribute network/nonce/timeout faults to the helper // list (which would send the operator after GUARD_HELPER_ADDRESS and trip the pre-check seed-drop). const codeSuffix = typeof code === 'string' && code.length > 0 ? ` code=${code}` : ''; diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index 44a3250..2186af1 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -6,7 +6,7 @@ import { AppConfigService } from '../config/config.service'; import { ProviderService } from './provider.service'; import { MinterRepository } from './prisma/repositories/minter.repository'; import { EventsRepository } from './prisma/repositories/events.repository'; -import { TelegramService, escapeMarkdownText } from './telegram.service'; +import { TelegramService, escapeMarkdownText, truncateAlertBody } from './telegram.service'; import { MinterStatus } from './types'; import { computeHelpers, classifyDenyError, QUORUM_BPS } from './minter-guard.logic'; import { GuardResponse } from '../../shared/types'; @@ -33,10 +33,19 @@ const DENY_CONFIRM_MIN_USEFUL_MS = 30_000; // under-quorum page, and a precheck failure must not share a timer with votes/gas/seed). const SKIP_ALERT_COOLDOWN_MS = 60 * 60 * 1000; -// Rough denyMinter() gas ceiling used for the balance floor (pre-check) and the read-only /guard status -// display (gasEnough / estimated cost). Worst-case so an underfunded signer always hits the dedicated -// gas page rather than a silent estimateGas "insufficient funds" catch. -const DENY_GAS_ESTIMATE = 300_000n; +// Helper-count-aware denyMinter() gas ceiling for the balance floor (pre-check) and the read-only +// /guard status display (gasEnough / estimated cost). Deliberately generous upper bounds whose only +// job is to make a gas shortfall page BEFORE a doomed send; the precise estimateGas call remains the +// accurate check. Equity.votesDelegated loops over helpers (votes + recursive _canVoteFor per entry), +// so a fixed ceiling under-prices large helper lists and can let the balance check pass while the +// transaction still runs out of gas. +const DENY_GAS_BASE = 200_000n; +const DENY_GAS_PER_HELPER = 30_000n; + +/** Worst-case gas ceiling for denyMinter given the helper list length (see DENY_GAS_* constants). */ +function denyGasCeiling(helperCount: number): bigint { + return DENY_GAS_BASE + BigInt(helperCount) * DENY_GAS_PER_HELPER; +} // Safety buffer (seconds) for the just-in-time TooLate pre-check: a minter whose live block timestamp is // already within this margin of its application deadline is skipped, since denyMinter would need to be @@ -60,9 +69,11 @@ const MAX_RESOLVE_READS_PER_CYCLE = 25; // round-robin resume so a fixed start cannot starve the same tail forever under the cap. const MAX_SWEEP_READS_PER_CYCLE = 25; -// Safe Telegram body length (Telegram rejects sendMessage bodies over 4096). Comfortably under the hard -// limit so Markdown/entity overhead cannot push a page into permanent undeliverable rejection. -const MAX_ALERT_BODY_CHARS = 3500; +// Wall-clock budget for each serial RPC pass (resolve + sweep). A call-count cap alone does not bound +// duration: the provider's configured RPC timeout is 60s, so 25 sequential timing-out reads take +// ~25 minutes — the cycle overruns its 5-minute cadence, isRunning suppresses later ticks, and every +// later watcher is delayed while veto windows keep progressing. Each pass gets its own budget of this size. +const RPC_PASS_BUDGET_MS = 60_000; // Short backoff after a FAILED skip-page delivery. Full SKIP_ALERT_COOLDOWN_MS still applies on success // (and when alerts are disabled). Trade-off: a delivered page must not repeat for an hour; a failed one @@ -135,6 +146,11 @@ export class MinterGuardService { deadline: 0, invariant: 0, }; + // Last undelivered skip page per kind. Terminal pages already use denyState.pendingAlert; skip pages + // must be retained the same way so a page is not lost when the condition stops recurring (e.g. the + // candidate became APPROVED, the RPC recovered). Retried in retryPendingAlerts under the shared + // MAX_ALERT_RETRIES_PER_CYCLE budget; cleared only on confirmed delivery. + private readonly pendingSkipAlerts = new Map(); // Round-robin resume for the capped sweep: address last examined when the read cap stopped the pass. // Next cycle starts AFTER this key so entries beyond MAX_SWEEP_READS_PER_CYCLE are not starved forever // under a fixed Map iteration start. @@ -294,7 +310,15 @@ export class MinterGuardService { `Voting power: ${escapeMarkdownText(status.votingPowerPct)}% (needs >= ${quorumPct}%)\n` + `Helpers: ${status.helperCount}\n\n` + `Remedy: delegateVoteTo(${signerAddress}) on Equity, or fund the signer with JUICE.`; - await this.telegramService.sendCriticalAlert(this.truncateAlertBody(startupMsg)); + const delivered = await this.telegramService.sendCriticalAlert(truncateAlertBody(startupMsg)); + // No pendingAlert-style retry at startup: the per-cycle pre-check pages the same under-quorum + // condition as soon as a real candidate exists. + if (!delivered) { + this.logger.error( + `MinterGuard startup under-quorum page could not be delivered; guard is unqualified for this run ` + + `(signer ${signerAddress})` + ); + } } catch (error) { // Transient RPC blip at boot must not page and must not throw (see method docstring). const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); @@ -324,16 +348,6 @@ export class MinterGuardService { } } - /** - * Truncate a terminal page body to MAX_ALERT_BODY_CHARS so Telegram cannot permanently reject it - * (hard limit 4096). A truncated body always ends with an explicit marker so operators know text was cut. - * Applied before every store and every send — never retain an untruncated body as pendingAlert. - */ - private truncateAlertBody(message: string): string { - if (message.length <= MAX_ALERT_BODY_CHARS) return message; - return `${message.slice(0, MAX_ALERT_BODY_CHARS)}\n\n… (truncated)`; - } - /** * Deliver a terminal (per-minter) critical page, honouring sendCriticalAlert's return value. * On confirmed delivery sets alerted=true and clears pendingAlert. On failure keeps pendingAlert @@ -345,7 +359,7 @@ export class MinterGuardService { private async deliverTerminalAlert(addrLc: string, state: DenyStateEntry, message: string): Promise { if (state.alerted) return; - const body = this.truncateAlertBody(message); + const body = truncateAlertBody(message); // Telegram disabled: nothing to deliver to and nothing to retry — error log is the record. if (!this.telegramService.alertsEnabled) { @@ -365,14 +379,22 @@ export class MinterGuardService { } /** - * Re-send any terminal pages that failed delivery on a previous cycle. Notification-only — - * never re-sends a deny transaction. Bounded per cycle (MAX_ALERT_RETRIES_PER_CYCLE) so a - * backlog from an outage cannot delay the time-critical deny work; remainder waits for the next - * cycle (logged at warn so a truncated backlog is never mistaken for an empty one). Runs at the - * END of checkAndDeny even when there are no candidates — notifications are not time-critical. + * Re-send any terminal pages and undelivered skip pages that failed delivery. Notification-only — + * never re-sends a deny transaction. Bounded per cycle (MAX_ALERT_RETRIES_PER_CYCLE) so a backlog from + * an outage cannot delay the time-critical deny work; remainder waits for a later cycle (logged at + * warn so a truncated backlog is never mistaken for an empty one). Runs at the END of checkAndDeny + * even when there are no candidates — notifications are not time-critical. + * + * A page stored during the current checkAndDeny is retried in this same invocation's end-of-cycle + * pass, then on every later cycle until delivered or the cap defers it. That immediate second attempt + * is harmless and often useful (Telegram blips are often short-lived). + * + * Skip pages share the same per-cycle cap as terminal pages (no separate budget). A page describing a + * condition that no longer holds is still worth delivering: it tells the operator what happened while + * they could not be reached. * - * On a failed retry the entry is delete+re-set so Map insertion order moves it to the end: the next - * cycle starts with pages not tried recently. Without rotation, five permanently failing entries at + * On a failed terminal retry the entry is delete+re-set so Map insertion order moves it to the end: the + * next cycle starts with pages not tried recently. Without rotation, five permanently failing entries at * the front of the map would starve every later pending page indefinitely under the per-cycle cap. */ private async retryPendingAlerts(): Promise { @@ -382,15 +404,20 @@ export class MinterGuardService { pending.push([addrLc, state]); } } + // Snapshot skip entries so iteration is stable while the map may clear on success. + const pendingSkips: Array<[SkipAlertKind, string]> = [...this.pendingSkipAlerts.entries()]; + const totalPendingAtStart = pending.length + pendingSkips.length; let attempted = 0; + let capWarned = false; for (const [addrLc, state] of pending) { if (attempted >= MAX_ALERT_RETRIES_PER_CYCLE) { - const stillPending = pending.length - attempted; + const stillPending = totalPendingAtStart - attempted; this.logger.warn( `MinterGuard: alert retry cap reached (${MAX_ALERT_RETRIES_PER_CYCLE} per cycle); ` + `${stillPending} pending page(s) still waiting for the next cycle` ); + capWarned = true; break; } // pendingAlert is defined by the filter above; re-truncate in case an older entry predates the cap. @@ -405,17 +432,61 @@ export class MinterGuardService { } } + // Retry undelivered skip pages under the same per-cycle cap (not a separate budget). + for (const [kind, message] of pendingSkips) { + if (attempted >= MAX_ALERT_RETRIES_PER_CYCLE) { + if (!capWarned) { + const stillPending = totalPendingAtStart - attempted; + this.logger.warn( + `MinterGuard: alert retry cap reached (${MAX_ALERT_RETRIES_PER_CYCLE} per cycle); ` + + `${stillPending} pending page(s) still waiting for the next cycle` + ); + capWarned = true; + } + break; + } + // May have been cleared if maybeAlertSkip delivered the same kind later in this cycle. + if (!this.pendingSkipAlerts.has(kind)) continue; + + // Re-truncate in case an older retained body predates the helper move; never re-send oversize text. + const body = truncateAlertBody(message); + if (!this.telegramService.alertsEnabled) { + // Nothing to deliver to — drop retention (same durable record as maybeAlertSkip when disabled). + this.pendingSkipAlerts.delete(kind); + this.lastSkipAlertAt[kind] = Date.now(); + this.logger.error(`MinterGuard skip page not deliverable on retry (telegram disabled; not retained): ${body}`); + attempted++; + continue; + } + + const delivered = await this.telegramService.sendCriticalAlert(body); + attempted++; + if (delivered) { + this.pendingSkipAlerts.delete(kind); + // Confirmed delivery: arm full cooldown so the same kind does not re-page for an hour. + this.lastSkipAlertAt[kind] = Date.now(); + } else { + // Keep retained body for the next cycle; short backoff so maybeAlertSkip path stays bounded too. + this.pendingSkipAlerts.set(kind, body); + this.lastSkipAlertAt[kind] = Date.now() - SKIP_ALERT_COOLDOWN_MS + SKIP_ALERT_RETRY_BACKOFF_MS; + this.logger.error(`MinterGuard skip page retry failed delivery (kind=${kind}); retained for next cycle: ${body}`); + } + } + // Aggregate backlog line, independent of whether the cap truncated the pass: a page attempted this // cycle that STILL failed (as opposed to never attempted) would otherwise leave no summary line at // all when the pending count is at or below the cap — a backlog must never be invisible. - // Individual failures keep their own log line from deliverTerminalAlert. + // Individual failures keep their own log line from deliverTerminalAlert / skip retry. + // Include undelivered skip pages so the log names the true number of undelivered pages. let stillPendingAfter = 0; for (const state of this.denyState.values()) { if (state.pendingAlert && state.alerted !== true) stillPendingAfter++; } + stillPendingAfter += this.pendingSkipAlerts.size; if (stillPendingAfter > 0) { this.logger.warn( - `MinterGuard: ${stillPendingAfter} pending terminal page(s) still awaiting delivery after this cycle's retry pass` + `MinterGuard: ${stillPendingAfter} pending page(s) still awaiting delivery after this cycle's retry pass ` + + `(terminal + skip)` ); } } @@ -451,20 +522,51 @@ export class MinterGuardService { // Addresses still in this cycle's candidate set (handled by the deny path when actionable). const candidateAddressSet = new Set(candidates.map((m) => m.address.toLowerCase())); + // Registration is pure bookkeeping and must not depend on any chain call. Register every candidate + // BEFORE the capped resolve pass so a failed minters() read or a candidate beyond the resolve cap + // cannot leave an observed address untracked. This is what makes the end-of-cycle invariant hold by + // construction — nothing that can fail may come before it. Do not touch an existing entry + // (attempts/done/pendingAlert must survive). + for (const minter of candidates) { + const addrLc = minter.address.toLowerCase(); + if (this.denyState.get(addrLc) === undefined) { + this.denyState.set(addrLc, { attempts: 0 }); + } + } + + // Sort by the deadline derivable WITHOUT a chain call (applicationTimestamp + applicationPeriod from + // the repository row), ascending, BEFORE applying the resolve cap. The on-chain read stays + // authoritative for the deny decision; this ordering only decides who gets examined first, so the + // most urgent veto window can no longer be stranded behind the resolve cap. + const candidatesOrdered = [...candidates].sort((a, b) => { + const deadlineA = a.applicationTimestamp + a.applicationPeriod; + const deadlineB = b.applicationTimestamp + b.applicationPeriod; + return deadlineA < deadlineB ? -1 : deadlineA > deadlineB ? 1 : 0; + }); + // Resolve pass BEFORE pre-check: filter already-resolved minters, order by urgency, and give the // pre-check an honest actionable candidate count. Deadlines read here are NOT authoritative for the // send — the mapping can change while the cycle runs (another actor may deny mid-loop); the send // loop re-reads minters(address) immediately before each deny (see send loop). const workingSet: ResolvedCandidate[] = []; - if (candidates.length > 0) { - this.logger.warn(`Found ${candidates.length} unwhitelisted PROPOSED minter(s) to deny`); + if (candidatesOrdered.length > 0) { + this.logger.warn(`Found ${candidatesOrdered.length} unwhitelisted PROPOSED minter(s) to deny`); let resolveReads = 0; let resolveTruncated = false; - for (const minter of candidates) { - // Cap serial RPC so this pass cannot outgrow the 5-minute cadence (see MAX_RESOLVE_READS_PER_CYCLE). + let resolveStopReason: 'count' | 'time' | undefined; + const resolveStartedAt = Date.now(); + for (const minter of candidatesOrdered) { + // Cap serial RPC by count AND wall-clock so this pass cannot outgrow the 5-minute cadence + // (see MAX_RESOLVE_READS_PER_CYCLE and RPC_PASS_BUDGET_MS). if (resolveReads >= MAX_RESOLVE_READS_PER_CYCLE) { resolveTruncated = true; + resolveStopReason = 'count'; + break; + } + if (Date.now() - resolveStartedAt >= RPC_PASS_BUDGET_MS) { + resolveTruncated = true; + resolveStopReason = 'time'; break; } const address = ethers.getAddress(minter.address); @@ -475,6 +577,7 @@ export class MinterGuardService { onChainDeadline = BigInt(await juiceDollar.minters(address)); } catch (error) { // Sustained RPC fault must not let a veto window expire in silence — rate-limited page. + // Candidate is already registered above; drop from workingSet for this cycle only. const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); this.logger.error(`MinterGuard skip ${address}: failed to read on-chain deny deadline (minters): ${errorMsg}`); await this.maybeAlertSkip( @@ -499,21 +602,18 @@ export class MinterGuardService { ); continue; } - // INVARIANT: every minter the guard has ever considered deniable (non-zero on-chain deadline) - // is tracked in denyState. The sweep iterates denyState only; without this entry a candidate - // that hits a continue path (confirmation-budget deferral, getBlock failure) or an ok:false - // pre-check that returns before the send loop would be invisible to the sweep — and once - // syncMinters flips the row to APPROVED the veto window can pass with no page at all. - // Do not touch an existing entry (attempts/done/pendingAlert must survive). - if (this.denyState.get(addrLc) === undefined) { - this.denyState.set(addrLc, { attempts: 0 }); - } + // Already registered before any RPC (see loop above). Entry is present for the sweep even if + // this candidate later hits a continue path (confirmation-budget deferral, getBlock failure). workingSet.push({ address, onChainDeadline }); } if (resolveTruncated) { - const notExamined = candidates.length - resolveReads; + const notExamined = candidatesOrdered.length - resolveReads; + const limitDesc = + resolveStopReason === 'time' + ? `wall-clock budget ${RPC_PASS_BUDGET_MS}ms` + : `count cap ${MAX_RESOLVE_READS_PER_CYCLE} minters() reads`; this.logger.warn( - `MinterGuard: resolve pass capped at ${MAX_RESOLVE_READS_PER_CYCLE} minters() reads; ` + + `MinterGuard: resolve pass stopped (${limitDesc}); ` + `${notExamined} candidate(s) not examined this cycle (truncated — not a completed pass)` ); } @@ -649,7 +749,7 @@ export class MinterGuardService { `Tx: \`${txHash}\`\n` + `Block: ${receipt.blockNumber}\n` + `Message: ${escapeMarkdownText(message)}`; - const delivered = await this.telegramService.sendCriticalAlert(this.truncateAlertBody(successMsg)); + const delivered = await this.telegramService.sendCriticalAlert(truncateAlertBody(successMsg)); // Success page does not need pendingAlert retry (deny is already on-chain and marked done), // but log loud when delivery fails so the gap is visible. if (!delivered) { @@ -682,10 +782,41 @@ export class MinterGuardService { // FAILED critical alert ONLY on the terminal state for this minter, and only once. // NotQualified must not produce a per-attempt page — the precheck owns that page. // Non-terminal transient failures log at error level and stay silent on Telegram. - // windowClosed uses the live currentDeadline (not the resolve-pass value) so remedy - // text stays truthful after a mid-cycle mapping change. + // Re-read the mapping before claiming manual action is needed: another actor may have + // denied the minter between pre-send and this catch (TooLate / mapping 0). A false + // "manual denyMinter() required" page erodes trust in every other page the guard sends. if (done && !prev.alerted) { - const windowClosed = BigInt(Math.floor(Date.now() / 1000)) >= currentDeadline; + let deadlineForRemedy = currentDeadline; + let recheckNote = ''; + try { + const freshDeadline = BigInt(await juiceDollar.minters(address)); + if (freshDeadline === 0n) { + // Resolved by someone else — mark done, send NO page. + this.denyState.set(addrLc, { ...next, done: true }); + this.logger.warn( + `MinterGuard: deny failed for ${address} but on-chain minters(address)=0 ` + + `(resolved by another actor); no manual-action page` + ); + continue; + } + deadlineForRemedy = freshDeadline; + } catch (recheckError) { + // A read failure here must not lose the page: fall back to the pre-send value and + // say in the message that the on-chain state could not be re-checked. + const recheckMsg = + typeof recheckError?.message === 'string' && recheckError.message + ? recheckError.message + : String(recheckError); + this.logger.error( + `MinterGuard: could not re-check on-chain state for ${address} after deny failure: ${recheckMsg}` + ); + recheckNote = + ' On-chain state could not be re-checked after the failure; remedy text uses the pre-send deadline.'; + } + // Chain clock and contract comparison only: denyMinter gates on + // block.timestamp > minters[_minter]. Local Date.now() skew or >= would let the + // remedy text contradict what the contract would still accept. + const windowClosed = BigInt(latestBlock.timestamp) > deadlineForRemedy; const remedy = windowClosed ? 'The application period has ended — denyMinter is impossible; challenge/handle the minter otherwise if needed.' : 'Manual denyMinter() required before the application period ends.'; @@ -695,7 +826,8 @@ export class MinterGuardService { `Class: ${escapeMarkdownText(classification.label)} (${classification.kind})\n` + `Detail: ${escapeMarkdownText(classification.detail)}\n` + `Attempts: ${attempts}/${MAX_DENY_ATTEMPTS}\n\n` + - remedy; + remedy + + recheckNote; await this.deliverTerminalAlert(addrLc, { ...next, done: true }, alertMsg); } } @@ -712,11 +844,12 @@ export class MinterGuardService { // INVARIANT: every address OBSERVED this cycle (candidateAddressSet, built from the PROPOSED / // non-whitelisted / not-yet-done query above) must hold a denyState entry by now — "observation - // implies tracking". The resolve pass above is what establishes this for a successfully resolved - // candidate; this assertion exists because three separate review rounds found the same class of - // silent pass-through (a candidate observed but never tracked, and therefore invisible to - // sweepPassedUnchallenged once syncMinters relabels it APPROVED). A violation here means some path - // still drops a candidate without recording it — fail loud rather than patch a fourth time. + // implies tracking". Registration of every candidate happens BEFORE any RPC (pure bookkeeping), so + // this assertion can no longer fire for a capped resolve pass or a failed minters() read — it fires + // only on a genuine code defect (some path still drops a candidate without recording it). Three + // earlier review rounds found the silent pass-through class (observed but never tracked, then + // invisible to sweepPassedUnchallenged once syncMinters relabels it APPROVED); fail loud rather than + // patch a fourth time if bookkeeping is ever broken again. const untracked = [...candidateAddressSet].filter((addrLc) => this.denyState.get(addrLc) === undefined); if (untracked.length > 0) { this.logger.error( @@ -744,9 +877,10 @@ export class MinterGuardService { * those previously tracked addresses without widening the candidate query to APPROVED (which would * page once for every legitimately approved unwhitelisted minter on first run). * - * Read count is capped (MAX_SWEEP_READS_PER_CYCLE) so serial minters() calls cannot stretch a cycle - * past the 5-minute cadence. Resume is round-robin via sweepResumeAfter: with a fixed start, entries - * beyond the cap would never be examined again. + * Read count is capped (MAX_SWEEP_READS_PER_CYCLE) and wall-clock is capped (RPC_PASS_BUDGET_MS) so + * serial minters() calls cannot stretch a cycle past the 5-minute cadence — a count cap alone does + * not stop sequential RPC timeouts from overrunning. Resume is round-robin via sweepResumeAfter: + * with a fixed start, entries beyond the cap would never be examined again. */ private async sweepPassedUnchallenged(juiceDollar: ethers.Contract, candidateAddresses: Set): Promise { let latestBlock: ethers.Block | null = null; @@ -780,8 +914,10 @@ export class MinterGuardService { let sweepReads = 0; let lastExamined: string | undefined; let truncated = false; + let sweepStopReason: 'count' | 'time' | undefined; + const sweepStartedAt = Date.now(); // Eligible keys we walked past without a minters() read (done / still-candidate) do not count - // toward the read cap; only actual RPC reads do. + // toward the read cap; only actual RPC reads do. Wall-clock budget is checked at the same point. for (let i = 0; i < keys.length; i++) { const addrLc = keys[(startIdx + i) % keys.length]; const state = this.denyState.get(addrLc); @@ -792,6 +928,12 @@ export class MinterGuardService { if (sweepReads >= MAX_SWEEP_READS_PER_CYCLE) { truncated = true; + sweepStopReason = 'count'; + break; + } + if (Date.now() - sweepStartedAt >= RPC_PASS_BUDGET_MS) { + truncated = true; + sweepStopReason = 'time'; break; } @@ -864,8 +1006,12 @@ export class MinterGuardService { if (candidateAddresses.has(addrLc)) continue; notExamined++; } + const limitDesc = + sweepStopReason === 'time' + ? `wall-clock budget ${RPC_PASS_BUDGET_MS}ms` + : `count cap ${MAX_SWEEP_READS_PER_CYCLE} minters() reads`; this.logger.warn( - `MinterGuard: sweep capped at ${MAX_SWEEP_READS_PER_CYCLE} minters() reads; ` + + `MinterGuard: sweep stopped (${limitDesc}); ` + `${notExamined} tracked minter(s) not examined this cycle (truncated — not a completed pass)` ); } @@ -969,10 +1115,11 @@ export class MinterGuardService { // Worst-case gas floor FIRST, independent of estimateGas: some nodes verify balance inside // eth_estimateGas, so an underfunded signer would revert there ("insufficient funds") and fall // into the generic catch below as a SILENT skip instead of this dedicated gas page. Checking the - // balance against a fixed worst-case ceiling (DENY_GAS_ESTIMATE * fee) up front guarantees a gas - // shortfall ALWAYS pages, before estimateGas is ever attempted. Native unit on Citrea is cBTC - // (18 decimals — ethers.formatEther is still correct). - const worstCaseCost = DENY_GAS_ESTIMATE * gasPrice; + // balance against a helper-count-aware worst-case ceiling (denyGasCeiling * fee) up front + // guarantees a gas shortfall ALWAYS pages, before estimateGas is ever attempted. helpers.length + // is the post seed-drop-retry set. Native unit on Citrea is cBTC (18 decimals — + // ethers.formatEther is still correct). + const worstCaseCost = denyGasCeiling(helpers.length) * gasPrice; if (balance < worstCaseCost) { this.logger.warn( `MinterGuard SKIP: signer ${signerAddress} low on gas ` + @@ -1057,11 +1204,12 @@ export class MinterGuardService { * * Cooldown arming: * - SUCCESS (or telegram disabled): stamp full SKIP_ALERT_COOLDOWN_MS — a delivered page must not - * be repeated for an hour. - * - FAILED delivery: stamp a short SKIP_ALERT_RETRY_BACKOFF_MS window so the page is retried, but - * not on every 5-minute cycle (which would be twelve attempts an hour, and per-candidate deadline - * pages could fire once per failing candidate within a single cycle's retries across cycles). - * Trade-off explicit: delivered → quiet for an hour; failed → bounded retry; never a per-cycle loop. + * be repeated for an hour. Clear any retained undelivered body for this kind. + * - FAILED delivery: stamp a short SKIP_ALERT_RETRY_BACKOFF_MS window AND retain the truncated body + * in pendingSkipAlerts so retryPendingAlerts can re-send even if the condition does not recur + * (candidate became APPROVED, RPC recovered). A page describing a condition that no longer holds + * is still worth delivering: it tells the operator what happened while they could not be reached. + * Trade-off explicit: delivered → quiet for an hour; failed → bounded retry + retention; never a per-cycle loop. */ private async maybeAlertSkip(kind: SkipAlertKind, message: string): Promise { const nowMs = Date.now(); @@ -1069,11 +1217,12 @@ export class MinterGuardService { if (nowMs - lastAt < SKIP_ALERT_COOLDOWN_MS) return; // Truncate every skip-page body too — not just terminal pages (see truncateAlertBody). - const body = this.truncateAlertBody(message); + const body = truncateAlertBody(message); // Telegram disabled: nothing to deliver to — stamp full cooldown so we do not re-log every cycle. if (!this.telegramService.alertsEnabled) { this.lastSkipAlertAt[kind] = nowMs; + this.pendingSkipAlerts.delete(kind); this.logger.error(`MinterGuard skip page not deliverable (telegram disabled): ${body}`); return; } @@ -1081,12 +1230,15 @@ export class MinterGuardService { const delivered = await this.telegramService.sendCriticalAlert(body); if (delivered) { this.lastSkipAlertAt[kind] = nowMs; + this.pendingSkipAlerts.delete(kind); } else { // Short backoff only: next attempt after SKIP_ALERT_RETRY_BACKOFF_MS (see method comment). // lastAt is stored such that (now - lastAt) reaches SKIP_ALERT_COOLDOWN_MS after the backoff. + // Retain the truncated body so the page is not lost when the condition stops recurring. this.lastSkipAlertAt[kind] = nowMs - SKIP_ALERT_COOLDOWN_MS + SKIP_ALERT_RETRY_BACKOFF_MS; + this.pendingSkipAlerts.set(kind, body); this.logger.error( - `MinterGuard skip page failed delivery (kind=${kind}); will retry after ` + + `MinterGuard skip page failed delivery (kind=${kind}); retained and will retry after ` + `${SKIP_ALERT_RETRY_BACKOFF_MS}ms backoff: ${body}` ); } @@ -1097,11 +1249,13 @@ export class MinterGuardService { * with seed-less retry, NoRevertData rethrow, and additive display fallback only when the contract * rejected the helper list. * - * ONE SNAPSHOT when votesDelegated answers: that single number is both votingPower (for - * votingPowerPct) and the qualified verdict; totalVotes comes from the same call sequence so the - * ratio is internally consistent. An additive votes() sum is ONLY an estimate when the contract - * refused (qualified is then false anyway) — never a second live read that can disagree with the - * verdict after a helper moved equity between calls. + * When votesDelegated answers: that number is both votingPower (for votingPowerPct) and the qualified + * verdict. totalVotes and votesDelegated come from two sequential contract view reads (not Multicall3 — + * this deployment has none; multicallProvider is the plain provider). Reading both from the contract + * removes the earlier additive-vs-contract mismatch, but the pair is not atomic: a transfer between + * the two reads can still shift the ratio slightly. An additive votes() sum is ONLY an estimate when + * the contract refused (qualified is then false anyway) — never a second live path that can disagree + * with the verdict after a helper moved equity between calls. * * Extracted so probeQualification does not depend on gas reads (getBalance / getFeeData) that live * only in getStatus — a gas-read failure must not suppress the under-quorum page. @@ -1137,7 +1291,8 @@ export class MinterGuardService { let contractAnswered = false; try { - // Same sequence: totalVotes then votesDelegated — single snapshot for ratio + verdict. + // Sequential contract views (not atomic; see method doc) — both from the contract so the + // ratio matches on-chain checkQualified rather than an additive votes() estimate. totalVotes = BigInt(await equity.totalVotes()); const delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, helpers)); votingPower = delegatedVotes; @@ -1228,12 +1383,12 @@ export class MinterGuardService { const qual = await this.evaluateQualification(); - // Gas status: model denyMinter() cost with a fixed gas ceiling * live fee (see DENY_GAS_ESTIMATE). + // Gas status: model denyMinter() cost with the helper-count-aware ceiling * live fee. const balance: bigint = await provider.getBalance(signerAddress); const feeData = await provider.getFeeData(); const gasPrice = feeData.maxFeePerGas ?? feeData.gasPrice; if (gasPrice === null || gasPrice === undefined) throw new Error('feeData has neither maxFeePerGas nor gasPrice'); - const estimatedDenyCost = DENY_GAS_ESTIMATE * gasPrice; + const estimatedDenyCost = denyGasCeiling(qual.helperCount) * gasPrice; return { enabled: true, diff --git a/src/monitoringV2/monitoring.service.ts b/src/monitoringV2/monitoring.service.ts index 4099f80..a0e76a2 100644 --- a/src/monitoringV2/monitoring.service.ts +++ b/src/monitoringV2/monitoring.service.ts @@ -12,7 +12,7 @@ import { CollateralService } from './collateral.service'; import { MinterService } from './minter.service'; import { MinterGuardService, GuardConfigError } from './minter-guard.service'; import { JusdService } from './jusd.service'; -import { TelegramService, escapeMarkdownText } from './telegram.service'; +import { TelegramService, escapeMarkdownText, truncateAlertBody } from './telegram.service'; @Injectable() export class MonitoringService implements OnModuleInit { @@ -56,10 +56,15 @@ export class MonitoringService implements OnModuleInit { this.logger.error(`MinterGuard init failed — guard DISABLED, monitoring continues: ${errorMsg}`, error?.stack || error); // One-shot bootstrap path, not a per-cycle watcher: no retry machinery here (unlike the guard's // own per-cycle pendingAlert retry) — this logger.error is the durable record if delivery fails. + // Truncated as well as escaped: errorMsg can carry unbounded input (loadWhitelist embeds the + // configured path verbatim), and Telegram rejects a body over its length limit outright — which + // would make the single page announcing a disabled guard permanently undeliverable. const delivered = await this.telegramService.sendCriticalAlert( - `⚠️ *Minter guard init failed — guard DISABLED*\n\n` + - `The auto-deny guard is OFF for this run; monitoring continues.\n` + - `Error: ${escapeMarkdownText(errorMsg)}` + truncateAlertBody( + `⚠️ *Minter guard init failed — guard DISABLED*\n\n` + + `The auto-deny guard is OFF for this run; monitoring continues.\n` + + `Error: ${escapeMarkdownText(errorMsg)}` + ) ); if (!delivered) { this.logger.error( diff --git a/src/monitoringV2/telegram.service.ts b/src/monitoringV2/telegram.service.ts index a6e9abc..5fb9c2d 100644 --- a/src/monitoringV2/telegram.service.ts +++ b/src/monitoringV2/telegram.service.ts @@ -304,3 +304,21 @@ export class TelegramService implements OnModuleInit, OnModuleDestroy { export function escapeMarkdownText(value: string): string { return value.replace(/([_*`\[])/g, '\\$1'); } + +// Safe Telegram body length (Telegram rejects sendMessage bodies over 4096). Comfortably under the hard +// limit so Markdown/entity overhead cannot push a page into permanent undeliverable rejection. +// Single source of truth for every alert site (guard, monitoring bootstrap, etc.). +export const MAX_ALERT_BODY_CHARS = 3500; + +/** + * Truncate an alert body to MAX_ALERT_BODY_CHARS so Telegram cannot permanently reject it + * (hard limit 4096). A truncated body always ends with an explicit marker so operators know text was cut. + * Applied before every store and every send — never retain an untruncated body as pendingAlert. + * Exported so every alert site (minter-guard, monitoring.service guard-init failure, …) can reach it; + * an oversized body (e.g. a configured file path interpolated verbatim) must not make the single + * page announcing "the guard is disabled" permanently undeliverable. + */ +export function truncateAlertBody(body: string): string { + if (body.length <= MAX_ALERT_BODY_CHARS) return body; + return `${body.slice(0, MAX_ALERT_BODY_CHARS)}\n\n… (truncated)`; +} From 0376c03bd1925e9e3f293759c06608f5e8bb9ebc Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 05:25:30 +0200 Subject: [PATCH 08/11] fix(minter-guard): one cycle deadline, per-page alert retention, a permissive gas floor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fifth review round, and this one removes machinery rather than adding it. All three defects were introduced by the previous round's own fixes. - Three independent time meters could not express the invariant that matters: the whole cycle must fit inside the cadence, because the caller guards it with a single flag and an overrun costs the next tick entirely. A 60-second budget was applied separately to two passes, a 240-second one metered only transaction waits, and the per-candidate reads in the send loop had no limit at all — worst case well past five minutes. There is now one cycle deadline that every pass derives its remaining time from, including the previously unbounded path, and the two obsolete constants and their accumulator are gone. - The retained-skip-page map was keyed by alert kind, but some of those pages name a single minter. A later page of the same kind for a different minter therefore destroyed an earlier undelivered one: the retention built to stop pages being lost was losing them. Retention is now keyed per page; the cooldown stays per kind, since its job is to limit how often a class of page fires. - The helper-scaled gas ceiling was checked before the precise estimate and, on a shortfall, skipped every candidate for the cycle — at 30 000 gas per helper, far above the real cost of a votes() read and a delegation walk. It therefore invented shortfalls and stopped the guard from denying anything, which is a worse outcome than the out-of-gas risk it was raised to cover. The per-helper term is now realistic and the ceiling is capped absolutely; the floor stays ahead of the estimate, because an underfunded signer must page even when estimateGas itself reverts for lack of funds, but it is deliberately permissive and the estimate remains the accurate check. Also corrects a comment that still claimed candidates are registered in the resolve pass; registration moved ahead of it, RPC-free, in the previous commit. --- src/monitoringV2/minter-guard.service.ts | 235 ++++++++++++++--------- 1 file changed, 148 insertions(+), 87 deletions(-) diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index 2186af1..df84123 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -15,15 +15,19 @@ import { GuardResponse } from '../../shared/types'; // unbounded tx.wait() would block processBlocks, leaving isRunning=true so no later cycle (and none of // the sibling alert watchers) ever runs, and — because nothing throws — no stuck-alert fires. Must be // shorter than the EVERY_5_MINUTES cron. On timeout the throw is caught, the minter is NOT marked done, -// and it retries next cycle within the attempt cap. +// and it retries next cycle within the attempt cap. Per-tx wait is further capped by cycleRemainingMs. const DENY_CONFIRM_TIMEOUT_MS = 180_000; -// Per-cycle budget for sequential tx.wait confirmations across all candidates. Must stay below the -// EVERY_5_MINUTES (300s) cadence so later watchers in the same cycle are not delayed and the next -// tick does not skip on isRunning. Invariant: total confirmation wait per cycle stays under this budget. -const DENY_CYCLE_CONFIRM_BUDGET_MS = 240_000; +// Comfortably below the EVERY_5_MINUTES cadence; the whole guard cycle must fit inside it, because +// monitoring.service guards the cycle with one isRunning flag and an overrun costs the next tick. +// Single deadline for resolve pass, send-loop JIT reads + confirms, sweep, and pending-alert retries — +// replaces the earlier separate RPC_PASS_BUDGET_MS / DENY_CYCLE_CONFIRM_BUDGET_MS budgets (and the +// confirmBudgetSpentMs accumulator). Three separate meters could not express the cycle-wide invariant, +// and the per-candidate JIT reads in the send loop had no budget at all; worst case summed well past +// the cadence while veto windows kept moving. +const CYCLE_BUDGET_MS = 240_000; -// Floor: if remaining confirmation budget is below this, defer remaining candidates to the next cycle +// Floor: if remaining cycle budget is below this, defer remaining candidates to the next cycle // rather than starting a deny whose confirmation cannot complete usefully within the cadence. // A deferral is not a failure — do not mark done and do not page. const DENY_CONFIRM_MIN_USEFUL_MS = 30_000; @@ -34,17 +38,20 @@ const DENY_CONFIRM_MIN_USEFUL_MS = 30_000; const SKIP_ALERT_COOLDOWN_MS = 60 * 60 * 1000; // Helper-count-aware denyMinter() gas ceiling for the balance floor (pre-check) and the read-only -// /guard status display (gasEnough / estimated cost). Deliberately generous upper bounds whose only -// job is to make a gas shortfall page BEFORE a doomed send; the precise estimateGas call remains the -// accurate check. Equity.votesDelegated loops over helpers (votes + recursive _canVoteFor per entry), -// so a fixed ceiling under-prices large helper lists and can let the balance check pass while the -// transaction still runs out of gas. +// /guard status display (gasEnough / estimated cost). Deliberately rough upper bound whose ONLY job +// is to make an obviously underfunded signer page before a doomed send; the precise estimateGas call +// immediately afterwards is the accurate check — so the floor is intentionally permissive rather than +// protective. A too-high per-helper term with a large helper set invented a shortfall that did not +// exist and vetoed every candidate for the cycle (worse than the out-of-gas risk the ceiling covers). +// DENY_GAS_CEILING_MAX caps inflation so a large helper set cannot push the floor without bound. const DENY_GAS_BASE = 200_000n; -const DENY_GAS_PER_HELPER = 30_000n; +const DENY_GAS_PER_HELPER = 8_000n; +const DENY_GAS_CEILING_MAX = 1_000_000n; /** Worst-case gas ceiling for denyMinter given the helper list length (see DENY_GAS_* constants). */ function denyGasCeiling(helperCount: number): bigint { - return DENY_GAS_BASE + BigInt(helperCount) * DENY_GAS_PER_HELPER; + const uncapped = DENY_GAS_BASE + BigInt(helperCount) * DENY_GAS_PER_HELPER; + return uncapped > DENY_GAS_CEILING_MAX ? DENY_GAS_CEILING_MAX : uncapped; } // Safety buffer (seconds) for the just-in-time TooLate pre-check: a minter whose live block timestamp is @@ -69,12 +76,6 @@ const MAX_RESOLVE_READS_PER_CYCLE = 25; // round-robin resume so a fixed start cannot starve the same tail forever under the cap. const MAX_SWEEP_READS_PER_CYCLE = 25; -// Wall-clock budget for each serial RPC pass (resolve + sweep). A call-count cap alone does not bound -// duration: the provider's configured RPC timeout is 60s, so 25 sequential timing-out reads take -// ~25 minutes — the cycle overruns its 5-minute cadence, isRunning suppresses later ticks, and every -// later watcher is delayed while veto windows keep progressing. Each pass gets its own budget of this size. -const RPC_PASS_BUDGET_MS = 60_000; - // Short backoff after a FAILED skip-page delivery. Full SKIP_ALERT_COOLDOWN_MS still applies on success // (and when alerts are disabled). Trade-off: a delivered page must not repeat for an hour; a failed one // must be retried; neither may become a per-cycle loop (twelve attempts an hour). @@ -146,11 +147,14 @@ export class MinterGuardService { deadline: 0, invariant: 0, }; - // Last undelivered skip page per kind. Terminal pages already use denyState.pendingAlert; skip pages - // must be retained the same way so a page is not lost when the condition stops recurring (e.g. the - // candidate became APPROVED, the RPC recovered). Retried in retryPendingAlerts under the shared - // MAX_ALERT_RETRIES_PER_CYCLE budget; cleared only on confirmed delivery. - private readonly pendingSkipAlerts = new Map(); + // Last undelivered skip page, keyed by page (kind alone for cycle-global pages; `${kind}:${dedupKey}` + // for per-candidate pages such as deadline). COOLDOWN stays per kind — it limits how often a CLASS of + // page fires; only retention and retry bookkeeping are per page, so a delivered page for minter B + // cannot destroy an undelivered page for minter A of the same kind. Terminal pages already use + // denyState.pendingAlert; skip pages must be retained the same way so a page is not lost when the + // condition stops recurring. Retried in retryPendingAlerts under the shared MAX_ALERT_RETRIES_PER_CYCLE + // budget; cleared only on confirmed delivery. + private readonly pendingSkipAlerts = new Map(); // Round-robin resume for the capped sweep: address last examined when the read cap stopped the pass. // Next cycle starts AFTER this key so entries beyond MAX_SWEEP_READS_PER_CYCLE are not starved forever // under a fixed Map iteration start. @@ -397,7 +401,7 @@ export class MinterGuardService { * next cycle starts with pages not tried recently. Without rotation, five permanently failing entries at * the front of the map would starve every later pending page indefinitely under the per-cycle cap. */ - private async retryPendingAlerts(): Promise { + private async retryPendingAlerts(cycleStartedAt: number): Promise { const pending: Array<[string, DenyStateEntry]> = []; for (const [addrLc, state] of this.denyState) { if (state.pendingAlert && state.alerted !== true) { @@ -405,12 +409,26 @@ export class MinterGuardService { } } // Snapshot skip entries so iteration is stable while the map may clear on success. - const pendingSkips: Array<[SkipAlertKind, string]> = [...this.pendingSkipAlerts.entries()]; + // Keys are page-level (kind or kind:dedupKey); see pendingSkipAlerts. + const pendingSkips: Array<[string, string]> = [...this.pendingSkipAlerts.entries()]; const totalPendingAtStart = pending.length + pendingSkips.length; let attempted = 0; let capWarned = false; + let deadlineWarned = false; for (const [addrLc, state] of pending) { + // Respect the single cycle deadline so a Telegram backlog cannot overrun the cadence. + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + if (!deadlineWarned) { + const stillPending = totalPendingAtStart - attempted; + this.logger.warn( + `MinterGuard: alert retry stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `${stillPending} pending page(s) still waiting for the next cycle` + ); + deadlineWarned = true; + } + break; + } if (attempted >= MAX_ALERT_RETRIES_PER_CYCLE) { const stillPending = totalPendingAtStart - attempted; this.logger.warn( @@ -433,7 +451,18 @@ export class MinterGuardService { } // Retry undelivered skip pages under the same per-cycle cap (not a separate budget). - for (const [kind, message] of pendingSkips) { + for (const [retentionKey, message] of pendingSkips) { + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + if (!deadlineWarned) { + const stillPending = totalPendingAtStart - attempted; + this.logger.warn( + `MinterGuard: alert retry stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `${stillPending} pending page(s) still waiting for the next cycle` + ); + deadlineWarned = true; + } + break; + } if (attempted >= MAX_ALERT_RETRIES_PER_CYCLE) { if (!capWarned) { const stillPending = totalPendingAtStart - attempted; @@ -445,14 +474,18 @@ export class MinterGuardService { } break; } - // May have been cleared if maybeAlertSkip delivered the same kind later in this cycle. - if (!this.pendingSkipAlerts.has(kind)) continue; + // May have been cleared if maybeAlertSkip delivered the same page later in this cycle. + if (!this.pendingSkipAlerts.has(retentionKey)) continue; + + // Kind prefix of the retention key (cooldown is per kind, not per page — see maybeAlertSkip). + const colon = retentionKey.indexOf(':'); + const kind = (colon === -1 ? retentionKey : retentionKey.slice(0, colon)) as SkipAlertKind; // Re-truncate in case an older retained body predates the helper move; never re-send oversize text. const body = truncateAlertBody(message); if (!this.telegramService.alertsEnabled) { // Nothing to deliver to — drop retention (same durable record as maybeAlertSkip when disabled). - this.pendingSkipAlerts.delete(kind); + this.pendingSkipAlerts.delete(retentionKey); this.lastSkipAlertAt[kind] = Date.now(); this.logger.error(`MinterGuard skip page not deliverable on retry (telegram disabled; not retained): ${body}`); attempted++; @@ -462,14 +495,14 @@ export class MinterGuardService { const delivered = await this.telegramService.sendCriticalAlert(body); attempted++; if (delivered) { - this.pendingSkipAlerts.delete(kind); + this.pendingSkipAlerts.delete(retentionKey); // Confirmed delivery: arm full cooldown so the same kind does not re-page for an hour. this.lastSkipAlertAt[kind] = Date.now(); } else { // Keep retained body for the next cycle; short backoff so maybeAlertSkip path stays bounded too. - this.pendingSkipAlerts.set(kind, body); + this.pendingSkipAlerts.set(retentionKey, body); this.lastSkipAlertAt[kind] = Date.now() - SKIP_ALERT_COOLDOWN_MS + SKIP_ALERT_RETRY_BACKOFF_MS; - this.logger.error(`MinterGuard skip page retry failed delivery (kind=${kind}); retained for next cycle: ${body}`); + this.logger.error(`MinterGuard skip page retry failed delivery (key=${retentionKey}); retained for next cycle: ${body}`); } } @@ -503,6 +536,10 @@ export class MinterGuardService { const { signerKey, signerAddress, jusdAddress } = this; if (!this.enabled || !signerKey || !signerAddress || !jusdAddress || !this.denyErrorInterface) return; + // Single cycle deadline: every RPC-bearing pass and per-tx wait derives stop/timeout from this + // (see CYCLE_BUDGET_MS / cycleRemainingMs). Captured once at the start of the guard work. + const cycleStartedAt = Date.now(); + const minters = await this.minterRepo.findAll(); // Denies BRIDGE-typed proposals too: bridge type is inferred from a single // `usd()` view call, which is trivial to mimic in a malicious contract. @@ -555,16 +592,15 @@ export class MinterGuardService { let resolveReads = 0; let resolveTruncated = false; let resolveStopReason: 'count' | 'time' | undefined; - const resolveStartedAt = Date.now(); for (const minter of candidatesOrdered) { - // Cap serial RPC by count AND wall-clock so this pass cannot outgrow the 5-minute cadence - // (see MAX_RESOLVE_READS_PER_CYCLE and RPC_PASS_BUDGET_MS). + // Cap serial RPC by count AND the single cycle deadline so this pass cannot outgrow the + // 5-minute cadence (see MAX_RESOLVE_READS_PER_CYCLE and CYCLE_BUDGET_MS). if (resolveReads >= MAX_RESOLVE_READS_PER_CYCLE) { resolveTruncated = true; resolveStopReason = 'count'; break; } - if (Date.now() - resolveStartedAt >= RPC_PASS_BUDGET_MS) { + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { resolveTruncated = true; resolveStopReason = 'time'; break; @@ -578,6 +614,8 @@ export class MinterGuardService { } catch (error) { // Sustained RPC fault must not let a veto window expire in silence — rate-limited page. // Candidate is already registered above; drop from workingSet for this cycle only. + // Pass address as dedupKey so a retained deadline page for one minter cannot be destroyed + // when a later same-kind page for a different minter is delivered or retained. const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); this.logger.error(`MinterGuard skip ${address}: failed to read on-chain deny deadline (minters): ${errorMsg}`); await this.maybeAlertSkip( @@ -586,7 +624,8 @@ export class MinterGuardService { `Address: \`${address}\`\n` + `Detail: ${escapeMarkdownText(errorMsg)}\n\n` + `The on-chain application deadline could not be read this cycle; the candidate is deferred ` + - `(not marked done). Investigate RPC if this persists — a silent window expiry must not happen.` + `(not marked done). Investigate RPC if this persists — a silent window expiry must not happen.`, + address ); // Drop for this cycle only — do not mark done. continue; @@ -603,14 +642,14 @@ export class MinterGuardService { continue; } // Already registered before any RPC (see loop above). Entry is present for the sweep even if - // this candidate later hits a continue path (confirmation-budget deferral, getBlock failure). + // this candidate later hits a continue path (cycle-budget deferral, getBlock failure). workingSet.push({ address, onChainDeadline }); } if (resolveTruncated) { const notExamined = candidatesOrdered.length - resolveReads; const limitDesc = resolveStopReason === 'time' - ? `wall-clock budget ${RPC_PASS_BUDGET_MS}ms` + ? `cycle deadline ${CYCLE_BUDGET_MS}ms` : `count cap ${MAX_RESOLVE_READS_PER_CYCLE} minters() reads`; this.logger.warn( `MinterGuard: resolve pass stopped (${limitDesc}); ` + @@ -621,7 +660,7 @@ export class MinterGuardService { // Nothing actionable: skip pre-check (would page "N left undenied" about minters that need nothing). if (workingSet.length > 0) { - // Urgency, not database order, must decide when the confirmation budget runs short: serve the + // Urgency, not database order, must decide when the cycle budget runs short: serve the // soonest veto window first. workingSet.sort((a, b) => (a.onChainDeadline < b.onChainDeadline ? -1 : a.onChainDeadline > b.onChainDeadline ? 1 : 0)); @@ -631,13 +670,32 @@ export class MinterGuardService { if (precheck.ok) { const helpers = precheck.helpers; - // Track confirmation wait spent this cycle so sequential timeouts cannot overrun the cron cadence. - let confirmBudgetSpentMs = 0; let deferDeniesLogged = false; for (const { address, onChainDeadline: resolveDeadline } of workingSet) { const addrLc = address.toLowerCase(); + // Remaining cycle budget too small to be useful: defer new denies (not fail) so the sweep + // and pending-alert retry still run and the next cycle can finish the rest. Do not mark + // done; do not page. Checked BEFORE the JIT getBlock/minters reads so those previously + // unbounded-per-candidate RPCs cannot overrun the cycle deadline (the failure the single + // CYCLE_BUDGET_MS deadline exists to prevent). + // Entry was registered BEFORE the resolve pass (RPC-free bookkeeping at the start of + // checkAndDeny) so a resolve-pass failure or timeout cannot leave a candidate untracked — + // the sweep still sees this deferred minter. + const remainingBudgetMs = this.cycleRemainingMs(cycleStartedAt); + if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { + if (!deferDeniesLogged) { + this.logger.warn( + `MinterGuard: cycle budget exhausted (${CYCLE_BUDGET_MS - remainingBudgetMs}ms spent of ` + + `${CYCLE_BUDGET_MS}ms); deferring remaining deny candidate(s) including ${address} ` + + `to the next cycle (not marked done, no page — deferral is not a failure).` + ); + deferDeniesLogged = true; + } + continue; + } + // Just-in-time TooLate / already-resolved guard. denyMinter reverts TooLate once // block.timestamp > minters[_minter]. Re-read BOTH the live block timestamp and the // on-chain deadline immediately before send: the resolve-pass deadline is stale once @@ -693,44 +751,23 @@ export class MinterGuardService { continue; } - // Remaining confirmation budget too small to be useful: defer new denies (not fail) so later - // watchers still run and the next cycle can finish the rest. Do not mark done; do not page. - // JIT closed-window / already-resolved paths above still run for every candidate. - // Entry was registered in the resolve pass so the sweep still sees this deferred minter. - const remainingBudgetMs = DENY_CYCLE_CONFIRM_BUDGET_MS - confirmBudgetSpentMs; - if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { - if (!deferDeniesLogged) { - this.logger.warn( - `MinterGuard: confirmation budget exhausted (${confirmBudgetSpentMs}ms spent of ` + - `${DENY_CYCLE_CONFIRM_BUDGET_MS}ms); deferring remaining deny candidate(s) including ${address} ` + - `to the next cycle (not marked done, no page — deferral is not a failure).` - ); - deferDeniesLogged = true; - } - continue; - } - const envLabel = `${this.config.environment ?? 'unknown'}/${this.config.chain ?? 'unknown'}`; const message = `Auto-deny by minter-guard: not in whitelist (${envLabel})`; let confirmed = false; let txHash: string | undefined; - const waitTimeoutMs = Math.min(DENY_CONFIRM_TIMEOUT_MS, remainingBudgetMs); + // Per-tx wait capped by whatever is left of the cycle deadline, so a slow confirmation + // cannot push the cycle past the cadence. + const waitTimeoutMs = Math.min(DENY_CONFIRM_TIMEOUT_MS, this.cycleRemainingMs(cycleStartedAt)); try { const tx = await juiceDollar.denyMinter(address, helpers, message); txHash = tx.hash; this.logger.warn(`Submitted denyMinter for ${address}: tx=${tx.hash}`); - // Bounded wait within the per-cycle confirmation budget: on timeout this throws and the - // minter is left unmarked to retry next cycle. A retry sends a fresh-nonce tx (it does not + // Bounded wait within the cycle deadline: on timeout this throws and the minter is + // left unmarked to retry next cycle. A retry sends a fresh-nonce tx (it does not // replace a stuck one); under sustained mempool/gas pathology the deny may not land, but the // terminal FAILED alert then pages a human — an accepted limitation of the opt-in guard, // deliberately not carrying nonce/replacement state. - const waitStartedAt = Date.now(); - let receipt: ethers.ContractTransactionReceipt | null; - try { - receipt = await tx.wait(1, waitTimeoutMs); - } finally { - confirmBudgetSpentMs += Date.now() - waitStartedAt; - } + const receipt: ethers.ContractTransactionReceipt | null = await tx.wait(1, waitTimeoutMs); if (!receipt) { // wait resolved without a receipt (should be rare with confirms=1); treat as unconfirmed for retry. throw new Error(`denyMinter tx.wait returned null for ${address} (tx=${txHash})`); @@ -837,10 +874,12 @@ export class MinterGuardService { } // Sweep tracked minters that left the PROPOSED candidate set without a deny (see method). - await this.sweepPassedUnchallenged(juiceDollar, candidateAddressSet); + // Still runs after a send-loop deferral (cheap, prevents silent pass-through) but respects the deadline. + await this.sweepPassedUnchallenged(juiceDollar, candidateAddressSet, cycleStartedAt); // Pending terminal pages after deny work — notifications are not time-critical; a veto window is. - await this.retryPendingAlerts(); + // Still runs after a deferral; also respects the cycle deadline. + await this.retryPendingAlerts(cycleStartedAt); // INVARIANT: every address OBSERVED this cycle (candidateAddressSet, built from the PROPOSED / // non-whitelisted / not-yet-done query above) must hold a denyState entry by now — "observation @@ -877,12 +916,17 @@ export class MinterGuardService { * those previously tracked addresses without widening the candidate query to APPROVED (which would * page once for every legitimately approved unwhitelisted minter on first run). * - * Read count is capped (MAX_SWEEP_READS_PER_CYCLE) and wall-clock is capped (RPC_PASS_BUDGET_MS) so - * serial minters() calls cannot stretch a cycle past the 5-minute cadence — a count cap alone does - * not stop sequential RPC timeouts from overrunning. Resume is round-robin via sweepResumeAfter: - * with a fixed start, entries beyond the cap would never be examined again. + * Read count is capped (MAX_SWEEP_READS_PER_CYCLE) and wall-clock is the single cycle deadline + * (CYCLE_BUDGET_MS via cycleRemainingMs) so serial minters() calls cannot stretch a cycle past the + * 5-minute cadence — a count cap alone does not stop sequential RPC timeouts from overrunning. + * Resume is round-robin via sweepResumeAfter: with a fixed start, entries beyond the cap would never + * be examined again. */ - private async sweepPassedUnchallenged(juiceDollar: ethers.Contract, candidateAddresses: Set): Promise { + private async sweepPassedUnchallenged( + juiceDollar: ethers.Contract, + candidateAddresses: Set, + cycleStartedAt: number + ): Promise { let latestBlock: ethers.Block | null = null; try { latestBlock = await this.providerService.provider.getBlock('latest'); @@ -915,9 +959,8 @@ export class MinterGuardService { let lastExamined: string | undefined; let truncated = false; let sweepStopReason: 'count' | 'time' | undefined; - const sweepStartedAt = Date.now(); // Eligible keys we walked past without a minters() read (done / still-candidate) do not count - // toward the read cap; only actual RPC reads do. Wall-clock budget is checked at the same point. + // toward the read cap; only actual RPC reads do. Cycle deadline is checked at the same point. for (let i = 0; i < keys.length; i++) { const addrLc = keys[(startIdx + i) % keys.length]; const state = this.denyState.get(addrLc); @@ -931,7 +974,7 @@ export class MinterGuardService { sweepStopReason = 'count'; break; } - if (Date.now() - sweepStartedAt >= RPC_PASS_BUDGET_MS) { + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { truncated = true; sweepStopReason = 'time'; break; @@ -1008,7 +1051,7 @@ export class MinterGuardService { } const limitDesc = sweepStopReason === 'time' - ? `wall-clock budget ${RPC_PASS_BUDGET_MS}ms` + ? `cycle deadline ${CYCLE_BUDGET_MS}ms` : `count cap ${MAX_SWEEP_READS_PER_CYCLE} minters() reads`; this.logger.warn( `MinterGuard: sweep stopped (${limitDesc}); ` + @@ -1202,27 +1245,36 @@ export class MinterGuardService { * Rate-limited skip page: at most one per kind per SKIP_ALERT_COOLDOWN_MS (in-memory, reset on restart). * Kinds have independent timers so one class of page cannot suppress another. * + * COOLDOWN stays per kind — it limits how often a CLASS of page fires. Retention and retry are per + * page: optional dedupKey keys pendingSkipAlerts as `${kind}:${dedupKey}` so a per-candidate page + * (e.g. deadline for minter A) is not overwritten or deleted when a later same-kind page for a + * different minter is retained or delivered. Cycle-global pages (votes, gas, seed, precheck, + * invariant) omit dedupKey and stay keyed by kind alone. Do not conflate the two bookkeeping axes. + * * Cooldown arming: * - SUCCESS (or telegram disabled): stamp full SKIP_ALERT_COOLDOWN_MS — a delivered page must not - * be repeated for an hour. Clear any retained undelivered body for this kind. + * be repeated for an hour. Clear any retained undelivered body for this page key. * - FAILED delivery: stamp a short SKIP_ALERT_RETRY_BACKOFF_MS window AND retain the truncated body * in pendingSkipAlerts so retryPendingAlerts can re-send even if the condition does not recur * (candidate became APPROVED, RPC recovered). A page describing a condition that no longer holds * is still worth delivering: it tells the operator what happened while they could not be reached. * Trade-off explicit: delivered → quiet for an hour; failed → bounded retry + retention; never a per-cycle loop. */ - private async maybeAlertSkip(kind: SkipAlertKind, message: string): Promise { + private async maybeAlertSkip(kind: SkipAlertKind, message: string, dedupKey?: string): Promise { const nowMs = Date.now(); const lastAt = this.lastSkipAlertAt[kind]; if (nowMs - lastAt < SKIP_ALERT_COOLDOWN_MS) return; + // Retention key is per page; cooldown above is still per kind (see method comment). + const retentionKey = dedupKey ? `${kind}:${dedupKey}` : kind; + // Truncate every skip-page body too — not just terminal pages (see truncateAlertBody). const body = truncateAlertBody(message); // Telegram disabled: nothing to deliver to — stamp full cooldown so we do not re-log every cycle. if (!this.telegramService.alertsEnabled) { this.lastSkipAlertAt[kind] = nowMs; - this.pendingSkipAlerts.delete(kind); + this.pendingSkipAlerts.delete(retentionKey); this.logger.error(`MinterGuard skip page not deliverable (telegram disabled): ${body}`); return; } @@ -1230,20 +1282,29 @@ export class MinterGuardService { const delivered = await this.telegramService.sendCriticalAlert(body); if (delivered) { this.lastSkipAlertAt[kind] = nowMs; - this.pendingSkipAlerts.delete(kind); + this.pendingSkipAlerts.delete(retentionKey); } else { // Short backoff only: next attempt after SKIP_ALERT_RETRY_BACKOFF_MS (see method comment). // lastAt is stored such that (now - lastAt) reaches SKIP_ALERT_COOLDOWN_MS after the backoff. // Retain the truncated body so the page is not lost when the condition stops recurring. this.lastSkipAlertAt[kind] = nowMs - SKIP_ALERT_COOLDOWN_MS + SKIP_ALERT_RETRY_BACKOFF_MS; - this.pendingSkipAlerts.set(kind, body); + this.pendingSkipAlerts.set(retentionKey, body); this.logger.error( - `MinterGuard skip page failed delivery (kind=${kind}); retained and will retry after ` + + `MinterGuard skip page failed delivery (key=${retentionKey}); retained and will retry after ` + `${SKIP_ALERT_RETRY_BACKOFF_MS}ms backoff: ${body}` ); } } + /** + * Remaining wall-clock budget for the current guard cycle (see CYCLE_BUDGET_MS). Every RPC-bearing + * pass, the send-loop stop condition, and each per-tx wait timeout derive from this single deadline + * so an overrun cannot leave isRunning stuck across the next EVERY_5_MINUTES tick. + */ + private cycleRemainingMs(cycleStartedAt: number): number { + return CYCLE_BUDGET_MS - (Date.now() - cycleStartedAt); + } + /** * Qualification half of GET /guard (and the startup probe): helper derivation, votesDelegated verdict * with seed-less retry, NoRevertData rethrow, and additive display fallback only when the contract From 5acf10bed15f6bcd15583a44ab06ee2c27808e6f Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 05:49:33 +0200 Subject: [PATCH 09/11] fix(minter-guard): run the cheap checks before the budget gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verification pass on the previous commit. Three of its guards collided with each other. - The send loop checked the remaining cycle budget before the two reads that detect an already-resolved minter and a closing veto window. Since the working set is sorted soonest-deadline-first, that deferred exactly the most urgent candidate instead of recording it and paging that it is passing unchallenged. Those reads also spent budget after the usefulness check, so the per-transaction wait timeout could go non-positive — and a non-positive timeout does not disable the timer, it fires almost immediately, so the transaction was submitted and its nonce consumed while the guard booked it as a failed attempt. The reads now run first and the budget gates only the send, which makes the timeout positive by construction. - The skip-page retry loop did not rotate a failed entry to the back of the queue, unlike its terminal-page sibling. That was harmless while retention was keyed by alert kind — at most six entries — but the previous commit keyed it per page, so the deadline kind can now hold one entry per minter and five persistently failing ones would starve every later page indefinitely. - An insufficient-funds revert from the precise gas estimate was swallowed by the catch that exists to keep a candidate-specific revert from dropping the cycle. The guard then sent a transaction that could not succeed and reported it as an ordinary failure instead of the gas page the floor-before-estimate ordering is meant to guarantee. That one reason is now recognised and pages; every other estimate failure still proceeds on the floor. Also closes the two remaining spots that performed chain reads without consulting the cycle deadline, so the invariant the previous commit introduced is now literally true rather than nearly true. --- src/monitoringV2/minter-guard.service.ts | 123 +++++++++++++++++------ 1 file changed, 95 insertions(+), 28 deletions(-) diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index df84123..4f7a6a5 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -400,6 +400,8 @@ export class MinterGuardService { * On a failed terminal retry the entry is delete+re-set so Map insertion order moves it to the end: the * next cycle starts with pages not tried recently. Without rotation, five permanently failing entries at * the front of the map would starve every later pending page indefinitely under the per-cycle cap. + * The same delete+re-set rotation applies to failed skip-page retries for the same reason (deadline pages + * are keyed per minter, so more than MAX_ALERT_RETRIES_PER_CYCLE skip entries is now plausible). */ private async retryPendingAlerts(cycleStartedAt: number): Promise { const pending: Array<[string, DenyStateEntry]> = []; @@ -500,6 +502,11 @@ export class MinterGuardService { this.lastSkipAlertAt[kind] = Date.now(); } else { // Keep retained body for the next cycle; short backoff so maybeAlertSkip path stays bounded too. + // Rotate failed retries to the end of Map iteration order (starvation prevention — same as + // the terminal loop above). Without rotation, five permanently failing skip entries at the + // front would starve every later pending skip page indefinitely under the per-cycle cap — + // now plausible because deadline pages are keyed per minter. + this.pendingSkipAlerts.delete(retentionKey); this.pendingSkipAlerts.set(retentionKey, body); this.lastSkipAlertAt[kind] = Date.now() - SKIP_ALERT_COOLDOWN_MS + SKIP_ALERT_RETRY_BACKOFF_MS; this.logger.error(`MinterGuard skip page retry failed delivery (key=${retentionKey}); retained for next cycle: ${body}`); @@ -666,7 +673,7 @@ export class MinterGuardService { // Signer-global pre-check (once per cycle, before any deny): verify quorum + gas and build helpers. // Count is the number of genuinely actionable minters after the resolve pass. - const precheck = await this.runDenyPrecheck(signerAddress, wallet, workingSet); + const precheck = await this.runDenyPrecheck(signerAddress, wallet, workingSet, cycleStartedAt); if (precheck.ok) { const helpers = precheck.helpers; @@ -675,27 +682,6 @@ export class MinterGuardService { for (const { address, onChainDeadline: resolveDeadline } of workingSet) { const addrLc = address.toLowerCase(); - // Remaining cycle budget too small to be useful: defer new denies (not fail) so the sweep - // and pending-alert retry still run and the next cycle can finish the rest. Do not mark - // done; do not page. Checked BEFORE the JIT getBlock/minters reads so those previously - // unbounded-per-candidate RPCs cannot overrun the cycle deadline (the failure the single - // CYCLE_BUDGET_MS deadline exists to prevent). - // Entry was registered BEFORE the resolve pass (RPC-free bookkeeping at the start of - // checkAndDeny) so a resolve-pass failure or timeout cannot leave a candidate untracked — - // the sweep still sees this deferred minter. - const remainingBudgetMs = this.cycleRemainingMs(cycleStartedAt); - if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { - if (!deferDeniesLogged) { - this.logger.warn( - `MinterGuard: cycle budget exhausted (${CYCLE_BUDGET_MS - remainingBudgetMs}ms spent of ` + - `${CYCLE_BUDGET_MS}ms); deferring remaining deny candidate(s) including ${address} ` + - `to the next cycle (not marked done, no page — deferral is not a failure).` - ); - deferDeniesLogged = true; - } - continue; - } - // Just-in-time TooLate / already-resolved guard. denyMinter reverts TooLate once // block.timestamp > minters[_minter]. Re-read BOTH the live block timestamp and the // on-chain deadline immediately before send: the resolve-pass deadline is stale once @@ -703,6 +689,15 @@ export class MinterGuardService { // candidate's confirmation. Sending with a stale deadline reverts, marks the minter // permanently failed, and pages "manual denyMinter() required" for a minter already denied. // currentDeadline is the authoritative value for buffer comparison and windowClosed text. + // + // These cheap diagnostics run BEFORE the send-budget gate: the working set is sorted + // soonest-deadline-first, so a candidate whose window is closing right now must still + // be recorded and paged ("passing unchallenged") even when remaining budget is too + // small to send. Deferring before these reads would silently drop the most urgent + // candidate — the failure this ordering prevents. + // Entry was registered BEFORE the resolve pass (RPC-free bookkeeping at the start of + // checkAndDeny) so a resolve-pass failure or timeout cannot leave a candidate untracked — + // the sweep still sees this deferred minter. let latestBlock: ethers.Block | null = null; let currentDeadline: bigint; try { @@ -751,12 +746,34 @@ export class MinterGuardService { continue; } + // Gate on SENDING, not on the candidate: remaining cycle budget too small for a useful + // confirmation wait — defer the deny (not fail) so the sweep and pending-alert retry still + // run and the next cycle can finish the rest. Do not mark done; do not page. Candidate + // stays tracked and is actionable again next cycle. Diagnostics above already ran. + const remainingBudgetMs = this.cycleRemainingMs(cycleStartedAt); + if (remainingBudgetMs < DENY_CONFIRM_MIN_USEFUL_MS) { + if (!deferDeniesLogged) { + this.logger.warn( + `MinterGuard: cycle budget exhausted (${CYCLE_BUDGET_MS - remainingBudgetMs}ms spent of ` + + `${CYCLE_BUDGET_MS}ms); deferring remaining deny send(s) including ${address} ` + + `to the next cycle (not marked done, no page — deferral is not a failure).` + ); + deferDeniesLogged = true; + } + continue; + } + const envLabel = `${this.config.environment ?? 'unknown'}/${this.config.chain ?? 'unknown'}`; const message = `Auto-deny by minter-guard: not in whitelist (${envLabel})`; let confirmed = false; let txHash: string | undefined; // Per-tx wait capped by whatever is left of the cycle deadline, so a slow confirmation // cannot push the cycle past the cadence. + // INVARIANT: the send-budget gate above guarantees remainingBudgetMs >= DENY_CONFIRM_MIN_USEFUL_MS + // at this point, so waitTimeoutMs is always positive. A non-positive timeout must never be + // submitted: ethers passes it to setTimeout, Node clamps it to ~1 ms, tx.wait rejects almost + // immediately after the tx is already in-flight — burning a MAX_DENY_ATTEMPTS slot and risking + // a redundant fresh-nonce resend for a deny that may confirm on its own. const waitTimeoutMs = Math.min(DENY_CONFIRM_TIMEOUT_MS, this.cycleRemainingMs(cycleStartedAt)); try { const tx = await juiceDollar.denyMinter(address, helpers, message); @@ -927,6 +944,14 @@ export class MinterGuardService { candidateAddresses: Set, cycleStartedAt: number ): Promise { + // Honour the single cycle deadline BEFORE the initial getBlock so the sweep cannot start work + // it has no time for (the cycle-wide invariant: every pass derives remaining time from the + // deadline). Mid-pass deadline checks below still stop further minters() reads. + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn(`MinterGuard: sweep skipped (cycle deadline ${CYCLE_BUDGET_MS}ms); no tracked minters examined this cycle`); + return; + } + let latestBlock: ethers.Block | null = null; try { latestBlock = await this.providerService.provider.getBlock('latest'); @@ -1064,6 +1089,7 @@ export class MinterGuardService { * Signer-global deny pre-check, run once per cycle before any denyMinter(). Returns { ok, helpers }: * - ok=true => helpers are ready; proceed to per-candidate deny loop. * - ok=false => SKIP all denies this cycle. Paths that produce ok=false: + * * cycle budget already below DENY_CONFIRM_MIN_USEFUL_MS at entry — defer (warn, no page), * * under quorum (votes) — rate-limited 'votes' page when candidates exist, * * low gas — rate-limited 'gas' page when candidates exist, * * seed rejected by votesDelegated — rate-limited 'seed' page, then continues seed-less if retry works @@ -1076,8 +1102,23 @@ export class MinterGuardService { private async runDenyPrecheck( signerAddress: string, wallet: ethers.Wallet, - candidates: Array<{ address: string }> + candidates: Array<{ address: string }>, + cycleStartedAt: number ): Promise<{ ok: boolean; helpers: string[] }> { + // Honour the single cycle deadline before any pre-check RPC: if remaining budget is below the + // useful floor there is no point starting sequential votes/gas calls we cannot finish, and the + // send loop would only defer anyway. Deferral is not a failure — candidates stay tracked/unmarked, + // no page (a missed cycle of pre-check is recovered next tick). + const precheckRemainingMs = this.cycleRemainingMs(cycleStartedAt); + if (precheckRemainingMs < DENY_CONFIRM_MIN_USEFUL_MS) { + this.logger.warn( + `MinterGuard: deny pre-check deferred (cycle deadline ${CYCLE_BUDGET_MS}ms; ` + + `${precheckRemainingMs}ms remaining < ${DENY_CONFIRM_MIN_USEFUL_MS}ms useful floor); ` + + `${candidates.length} candidate(s) left for the next cycle (not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers: [] }; + } + try { const provider = this.providerService.provider; const chainId = this.config.blockchainId; @@ -1181,11 +1222,19 @@ export class MinterGuardService { } // Precise estimate against a representative denyMinter() (cost is minter-independent to first - // order), only after the balance floor above rules out an "insufficient funds" revert and after - // the votes check rules out a NotQualified revert. This is BEST-EFFORT on top of the worst-case - // floor: a sample-specific revert must NOT drop every candidate this cycle (e.g. sample just - // crossed its own deadline). The worst-case floor already guarantees gas, and the per-candidate - // TooLate guard + try/catch handle each send — so on estimate failure we simply proceed. + // order), only after the balance floor above rules out an obvious "insufficient funds" shortfall + // and after the votes check rules out a NotQualified revert. The floor catches an obviously + // underfunded signer up front; it does NOT guarantee the true cost — the band between the + // permissive worst-case floor and the real estimate is what estimateGas covers next. + // + // Split on estimate failure: + // * insufficient funds (code or message) → gas shortfall in that band: page and skip the cycle + // (same dedicated gas page the floor-before-estimate ordering exists to guarantee). Without + // this branch the catch would swallow the shortfall, the cycle would proceed, and the real + // send would fail as an ordinary per-candidate error instead of the gas page. + // * any other estimate revert → BEST-EFFORT: a sample-specific revert must NOT drop every + // candidate this cycle (e.g. sample just crossed its own deadline). Proceed on the floor; + // the per-candidate TooLate guard + try/catch handle each send. try { const jusd = new ethers.Contract(ADDRESS[chainId].juiceDollar, JuiceDollarABI, wallet); const gasEstimate: bigint = BigInt( @@ -1211,6 +1260,24 @@ export class MinterGuardService { } catch (estimateError) { const em = typeof estimateError?.message === 'string' && estimateError.message ? estimateError.message : String(estimateError); + const isInsufficientFunds = estimateError?.code === 'INSUFFICIENT_FUNDS' || /insufficient funds/i.test(em); + if (isInsufficientFunds) { + // Narrow band between the worst-case floor and true cost: floor passed, estimate did not. + this.logger.warn( + `MinterGuard SKIP: signer ${signerAddress} low on gas ` + + `(balance ${ethers.formatEther(balance)} cBTC; precise estimate reported insufficient funds: ${em})` + ); + await this.maybeAlertSkip( + 'gas', + `⚠️ *Minter guard low on cBTC — deny skipped*\n\n` + + `Signer: \`${signerAddress}\`\n` + + `Balance: ${ethers.formatEther(balance)} cBTC\n` + + `Precise estimate reported insufficient funds.\n` + + `${candidates.length} unwhitelisted PROPOSED minter(s) left undenied.\n\n` + + `Fund the signer with cBTC.` + ); + return { ok: false, helpers }; + } this.logger.warn( `MinterGuard: sample denyMinter gas estimate on ${candidates[0].address} reverted (${em}); ` + `proceeding on the worst-case gas floor — the per-candidate TooLate guard and try/catch handle each send.` From 895f7fad9f6a9cc065617bdd6d3b01b6d761ff7d Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 06:13:16 +0200 Subject: [PATCH 10/11] fix(minter-guard): bound the per-candidate reads the reordering left unbounded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit moved the budget gate after the two just-in-time reads so a candidate whose veto window is closing is recorded and paged instead of silently deferred. That ordering is right and stays — but the gate was also the only thing bounding those reads. Every candidate in the working set then performed both reads unconditionally, each bounded only by the configured RPC timeout, so a degraded endpoint could keep one cycle running for tens of minutes against a 240-second deadline, holding the caller's running flag and starving the sweep and the alert retries that follow in the same cycle. The loop now checks the cycle deadline where its sibling resolve pass already does: once before each candidate's reads, and once between the two reads so a single slow call cannot be compounded by a second. Deferred candidates keep their existing semantics — not marked, not paged, still candidates next cycle — and the send gate keeps its own floor, so the wait timeout stays positive by construction. The bound is stated honestly in the code: an in-flight call cannot be cancelled, so the cycle can still overshoot by at most one read. That is the same bound the resolve pass has, and it makes the deadline meaningful rather than exact. --- src/monitoringV2/minter-guard.service.ts | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index 4f7a6a5..2329d04 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -678,8 +678,26 @@ export class MinterGuardService { const helpers = precheck.helpers; let deferDeniesLogged = false; + let candidatesStarted = 0; for (const { address, onChainDeadline: resolveDeadline } of workingSet) { + // Cycle-deadline guard BEFORE the two just-in-time reads — distinct from the send-budget + // gate further down (that one still allows diagnostics when confirmation-wait budget is + // too small; this one refuses to start either read once the cycle is already over budget). + // Bound: the loop can still overshoot the cycle deadline by at most one in-flight read + // (same as the resolve pass); an outstanding RPC cannot be cancelled, so the deadline is + // meaningful rather than exact. + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + const remaining = workingSet.length - candidatesStarted; + this.logger.warn( + `MinterGuard: cycle deadline reached before deny-candidate JIT reads; ` + + `${remaining} remaining candidate(s) not examined this cycle ` + + `(not marked done, no page — deferred to next cycle).` + ); + break; + } + candidatesStarted++; + const addrLc = address.toLowerCase(); // Just-in-time TooLate / already-resolved guard. denyMinter reverts TooLate once @@ -702,6 +720,15 @@ export class MinterGuardService { let currentDeadline: bigint; try { latestBlock = await this.providerService.provider.getBlock('latest'); + // Same cycle-deadline discipline as the top-of-loop guard: a single slow getBlock must + // not be compounded by a second minters() read once the budget is already gone. + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard skip ${address}: cycle deadline reached between getBlock and minters() ` + + `(not marked done, no page — deferred to next cycle)` + ); + continue; + } currentDeadline = BigInt(await juiceDollar.minters(address)); } catch (error) { const errorMsg = typeof error?.message === 'string' && error.message ? error.message : String(error); From afade53188efde8c5428464d4ebe2bf57db2ac9c Mon Sep 17 00:00:00 2001 From: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Date: Tue, 28 Jul 2026 06:35:23 +0200 Subject: [PATCH 11/11] fix(minter-guard): bound the pre-check, and take fresh readings where freshness matters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final review round. Two independent lenses converged on the first item. - The pre-check consulted the cycle deadline only at entry and then made up to seven sequential chain calls. At the configured 60-second RPC timeout that is several minutes past a 240-second budget, and during it the guard takes no action at all — not even the cheap reads that would record and report a candidate whose veto window is closing — while the caller's running flag can swallow whole scheduled ticks. Every sibling pass already checks before each read; this was the one place where the previous commit's claim of a one-read bound was untrue. It now checks before each of the seven. - The wait timeout was computed before the transaction was submitted, and that submission is itself several round trips, so the value was stale before the wait began. It is now computed immediately before the wait — with a floor, because a transaction that is already broadcast must never be abandoned instantly. That distinction is the point: the deadline governs whether a send is started, not how long an in-flight transaction is awaited. - The terminal remedy re-read the deadline to avoid claiming manual action for a minter someone else had denied, but compared it against a block timestamp captured before the submission and a wait that can last minutes. A fresh deadline against a stale clock is not a fresh decision, so both sides are now read together, and the note says so when either read fails. --- src/monitoringV2/minter-guard.service.ts | 121 ++++++++++++++++++++--- 1 file changed, 105 insertions(+), 16 deletions(-) diff --git a/src/monitoringV2/minter-guard.service.ts b/src/monitoringV2/minter-guard.service.ts index 2329d04..cd56c5a 100644 --- a/src/monitoringV2/minter-guard.service.ts +++ b/src/monitoringV2/minter-guard.service.ts @@ -32,6 +32,13 @@ const CYCLE_BUDGET_MS = 240_000; // A deferral is not a failure — do not mark done and do not page. const DENY_CONFIRM_MIN_USEFUL_MS = 30_000; +// Minimum wait the guard owes a deny transaction it has already broadcast. The cycle deadline +// governs whether a send is STARTED; once the tx is in flight, the confirmation wait must not be +// abandoned instantly (or starved to near-zero by submission latency) — that would burn an attempt +// and risk a redundant fresh-nonce resend for a deny that may confirm on its own. Distinct from +// DENY_CONFIRM_MIN_USEFUL_MS (pre-send start gate). +const DENY_CONFIRM_MIN_WAIT_MS = 30_000; + // Cooldown between repeated skip pages (in-memory only, reset on restart). Independent timers per kind // so no page class may suppress another (e.g. a reassuring seed-drop page must never swallow the critical // under-quorum page, and a precheck failure must not share a timer with votes/gas/seed). @@ -794,23 +801,31 @@ export class MinterGuardService { const message = `Auto-deny by minter-guard: not in whitelist (${envLabel})`; let confirmed = false; let txHash: string | undefined; - // Per-tx wait capped by whatever is left of the cycle deadline, so a slow confirmation - // cannot push the cycle past the cadence. - // INVARIANT: the send-budget gate above guarantees remainingBudgetMs >= DENY_CONFIRM_MIN_USEFUL_MS - // at this point, so waitTimeoutMs is always positive. A non-positive timeout must never be - // submitted: ethers passes it to setTimeout, Node clamps it to ~1 ms, tx.wait rejects almost - // immediately after the tx is already in-flight — burning a MAX_DENY_ATTEMPTS slot and risking - // a redundant fresh-nonce resend for a deny that may confirm on its own. - const waitTimeoutMs = Math.min(DENY_CONFIRM_TIMEOUT_MS, this.cycleRemainingMs(cycleStartedAt)); try { const tx = await juiceDollar.denyMinter(address, helpers, message); txHash = tx.hash; this.logger.warn(`Submitted denyMinter for ${address}: tx=${tx.hash}`); - // Bounded wait within the cycle deadline: on timeout this throws and the minter is - // left unmarked to retry next cycle. A retry sends a fresh-nonce tx (it does not - // replace a stuck one); under sustained mempool/gas pathology the deny may not land, but the - // terminal FAILED alert then pages a human — an accepted limitation of the opt-in guard, - // deliberately not carrying nonce/replacement state. + // Bounded wait: on timeout this throws and the minter is left unmarked to retry next + // cycle. A retry sends a fresh-nonce tx (it does not replace a stuck one); under sustained + // mempool/gas pathology the deny may not land, but the terminal FAILED alert then pages a + // human — an accepted limitation of the opt-in guard, deliberately not carrying + // nonce/replacement state. + // + // INVARIANT: the cycle deadline governs whether a send is STARTED, not how long an + // in-flight transaction is awaited. Compute waitTimeoutMs HERE (after broadcast), not + // before denyMinter: with no gas/fee overrides that call populates the tx first (gas + // estimation, fee data, nonce), then signs and broadcasts — so a pre-send remaining-budget + // value is stale by the whole submission duration and can starve the wait. Once broadcast, + // the guard waits at least DENY_CONFIRM_MIN_WAIT_MS so the timeout is positive by + // construction and the cycle may overshoot by that floor at most. A non-positive timeout + // must never be submitted: ethers passes it to setTimeout, Node clamps it to ~1 ms, and + // tx.wait rejects almost immediately while the transaction is still in flight — burning a + // MAX_DENY_ATTEMPTS slot and risking a redundant fresh-nonce resend for a deny that may + // confirm on its own. + const waitTimeoutMs = Math.min( + DENY_CONFIRM_TIMEOUT_MS, + Math.max(this.cycleRemainingMs(cycleStartedAt), DENY_CONFIRM_MIN_WAIT_MS) + ); const receipt: ethers.ContractTransactionReceipt | null = await tx.wait(1, waitTimeoutMs); if (!receipt) { // wait resolved without a receipt (should be rare with confirms=1); treat as unconfirmed for retry. @@ -868,8 +883,19 @@ export class MinterGuardService { // "manual denyMinter() required" page erodes trust in every other page the guard sends. if (done && !prev.alerted) { let deadlineForRemedy = currentDeadline; + // Start from the pre-send block clock; replaced only when both re-reads succeed. + let blockTsForRemedy = BigInt(latestBlock.timestamp); let recheckNote = ''; try { + // Both sides of the windowClosed decision must come from the same moment: a + // fresh deadline compared against a stale pre-send block clock biases the remedy + // toward "still open" and can tell an operator to deny manually after that has + // become impossible (submission + confirmation wait can span up to the + // confirmation timeout after the pre-send getBlock). + const freshBlock = await this.providerService.provider.getBlock('latest'); + if (!freshBlock) { + throw new Error(`provider.getBlock('latest') returned null after deny failure for ${address}`); + } const freshDeadline = BigInt(await juiceDollar.minters(address)); if (freshDeadline === 0n) { // Resolved by someone else — mark done, send NO page. @@ -881,8 +907,9 @@ export class MinterGuardService { continue; } deadlineForRemedy = freshDeadline; + blockTsForRemedy = BigInt(freshBlock.timestamp); } catch (recheckError) { - // A read failure here must not lose the page: fall back to the pre-send value and + // A read failure here must not lose the page: fall back to the pre-send values and // say in the message that the on-chain state could not be re-checked. const recheckMsg = typeof recheckError?.message === 'string' && recheckError.message @@ -892,12 +919,13 @@ export class MinterGuardService { `MinterGuard: could not re-check on-chain state for ${address} after deny failure: ${recheckMsg}` ); recheckNote = - ' On-chain state could not be re-checked after the failure; remedy text uses the pre-send deadline.'; + ' On-chain state could not be re-checked after the failure; ' + + 'remedy text uses the pre-send deadline and block clock.'; } // Chain clock and contract comparison only: denyMinter gates on // block.timestamp > minters[_minter]. Local Date.now() skew or >= would let the // remedy text contradict what the contract would still accept. - const windowClosed = BigInt(latestBlock.timestamp) > deadlineForRemedy; + const windowClosed = blockTsForRemedy > deadlineForRemedy; const remedy = windowClosed ? 'The application period has ended — denyMinter is impossible; challenge/handle the minter otherwise if needed.' : 'Manual denyMinter() required before the application period ends.'; @@ -1117,6 +1145,7 @@ export class MinterGuardService { * - ok=true => helpers are ready; proceed to per-candidate deny loop. * - ok=false => SKIP all denies this cycle. Paths that produce ok=false: * * cycle budget already below DENY_CONFIRM_MIN_USEFUL_MS at entry — defer (warn, no page), + * * cycle deadline exhausted between sequential pre-check chain calls — defer (warn, no page), * * under quorum (votes) — rate-limited 'votes' page when candidates exist, * * low gas — rate-limited 'gas' page when candidates exist, * * seed rejected by votesDelegated — rate-limited 'seed' page, then continues seed-less if retry works @@ -1165,17 +1194,53 @@ export class MinterGuardService { // reads like an RPC fault. If EmptyRevert fires and we have a seed, retry once seed-less so a // config typo cannot silently disable the guard forever. (A seed equal to the signer is detected // and cleared in initialize — it never reaches this path.) + // + // Deadline between each sequential chain call (same convention as the resolve pass / sweep): an + // outstanding RPC cannot be cancelled, so the cycle may still overshoot by at most one call — + // not by the full remaining chain of four-to-six reads when only the entry check ran. let totalVotes: bigint; let delegatedVotes: bigint; try { + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard: deny pre-check stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `totalVotes() not reached — deferring ${candidates.length} candidate(s) to the next cycle ` + + `(not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers }; + } totalVotes = BigInt(await equity.totalVotes()); + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard: deny pre-check stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `votesDelegated() not reached — deferring ${candidates.length} candidate(s) to the next cycle ` + + `(not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers }; + } delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, helpers)); } catch (votesError) { const classification = classifyDenyError(votesError, denyErrorInterface); if (classification.label === 'EmptyRevert' && this.helperSeed.length > 0) { const seedLess = computeHelpers(delegations, signerAddress); try { + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard: deny pre-check stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `seed-drop totalVotes() not reached — deferring ${candidates.length} ` + + `candidate(s) to the next cycle (not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers }; + } totalVotes = BigInt(await equity.totalVotes()); + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard: deny pre-check stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `seed-drop votesDelegated() not reached — deferring ${candidates.length} ` + + `candidate(s) to the next cycle (not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers }; + } delegatedVotes = BigInt(await equity.votesDelegated(signerAddress, seedLess)); helpers = seedLess; this.logger.error( @@ -1218,9 +1283,25 @@ export class MinterGuardService { return { ok: false, helpers }; } + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard: deny pre-check stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `getFeeData() not reached — deferring ${candidates.length} candidate(s) to the next cycle ` + + `(not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers }; + } const feeData = await provider.getFeeData(); const gasPrice = feeData.maxFeePerGas ?? feeData.gasPrice; if (gasPrice === null || gasPrice === undefined) throw new Error('feeData has neither maxFeePerGas nor gasPrice'); + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard: deny pre-check stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `getBalance() not reached — deferring ${candidates.length} candidate(s) to the next cycle ` + + `(not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers }; + } const balance: bigint = await provider.getBalance(signerAddress); // Worst-case gas floor FIRST, independent of estimateGas: some nodes verify balance inside @@ -1263,6 +1344,14 @@ export class MinterGuardService { // candidate this cycle (e.g. sample just crossed its own deadline). Proceed on the floor; // the per-candidate TooLate guard + try/catch handle each send. try { + if (this.cycleRemainingMs(cycleStartedAt) <= 0) { + this.logger.warn( + `MinterGuard: deny pre-check stopped (cycle deadline ${CYCLE_BUDGET_MS}ms); ` + + `estimateGas() not reached — deferring ${candidates.length} candidate(s) to the next cycle ` + + `(not marked done, no page — deferral is not a failure).` + ); + return { ok: false, helpers }; + } const jusd = new ethers.Contract(ADDRESS[chainId].juiceDollar, JuiceDollarABI, wallet); const gasEstimate: bigint = BigInt( await jusd.denyMinter.estimateGas(candidates[0].address, helpers, 'minter-guard gas estimate')