diff --git a/.github/workflows/070_lint_and_test_go_bridge.yaml b/.github/workflows/070_lint_and_test_go_bridge.yaml index 7daee5d59..53dec4dc8 100644 --- a/.github/workflows/070_lint_and_test_go_bridge.yaml +++ b/.github/workflows/070_lint_and_test_go_bridge.yaml @@ -23,9 +23,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.20" + go-version: "1.21" cache: false - # cache-dependency-path: bridge/tfchain_bridge/go.sum id: go - name: golangci-lint @@ -35,9 +34,10 @@ jobs: working-directory: bridge/tfchain_bridge - name: staticcheck - uses: dominikh/staticcheck-action@v1.3.0 + uses: dominikh/staticcheck-action@v1.4.0 with: - version: "latest" + version: "2023.1.6" + install-go: false working-directory: bridge/tfchain_bridge env: GO111MODULE: on diff --git a/Makefile b/Makefile index cc94da7cf..f74cb2edf 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,323 @@ +SHELL := /bin/bash +.SHELLFLAGS := -eu -o pipefail -c + +# ───────────────────────────────────────────────────────────────────────────── +# Bridge local development environment +# ───────────────────────────────────────────────────────────────────────────── +# +# Quick start (first run builds TFChain — takes 20-40 min): +# make bridge-dev # single validator +# make bridge-mv-dev # 3 validators, 2-of-3 multi-sig +# +# Configurable: +# TFCHAIN_URL WebSocket URL (default: ws://localhost:9944) +# BRIDGE_ENV_FILE Env file path (default: /tmp/bridge_local_env.sh) +# BRIDGE_MV_ENV_FILE MV env file (default: /tmp/bridge_mv_env.sh) +# TFCHAIN_BIN TFChain binary path (default: release; use debug to skip 30-min build) +# e.g. TFCHAIN_BIN=substrate-node/target/debug/tfchain make bridge-dev +# BRIDGE_NETWORK Bridge --network flag (default: local) +# TFCHAIN_PORT TFChain WS port (default: 9944) + +BRIDGE_DIR := bridge/tfchain_bridge +BRIDGE_BIN := $(BRIDGE_DIR)/tfchain_bridge_local +TFCHAIN_BIN ?= substrate-node/target/release/tfchain +SCRIPTS_DIR := scripts + +BRIDGE_LOG := /tmp/bridge_local.log +BRIDGE_PID_FILE := /tmp/bridge_local.pid +TFCHAIN_LOG := /tmp/tfchain_local.log +TFCHAIN_PID_FILE := /tmp/tfchain_local.pid + +BRIDGE_ENV_FILE ?= /tmp/bridge_local_env.sh +BRIDGE_MV_ENV_FILE ?= /tmp/bridge_mv_env.sh +TFCHAIN_URL ?= ws://localhost:9944 +BRIDGE_NETWORK ?= local +TFCHAIN_PORT ?= 9944 + +# Bridge validator dev seeds (from substrate-node/node/src/chain_spec.rs) +# Single source of truth — passed as env vars to scripts via Makefile recipes. +VAL1_TFCHAIN_SEED ?= quarter between satisfy three sphere six soda boss cute decade old trend +VAL2_TFCHAIN_SEED ?= employ split promote annual couple elder remain cricket company fitness senior fiscal +VAL3_TFCHAIN_SEED ?= remind bird banner word spread volume card keep want faith insect mind + +.PHONY: bridge-help bridge-build bridge-build-tfchain \ + bridge-accounts bridge-mv-accounts \ + bridge-tfchain-start bridge-tfchain-stop \ + bridge-setup bridge-mv-setup \ + bridge-start bridge-stop bridge-test bridge-clean bridge-dev \ + bridge-mv-start bridge-mv-stop bridge-mv-test bridge-mv-clean bridge-mv-dev + +# ───────────────────────────────────────────────────────────────────────────── +# Daemon helpers +# ───────────────────────────────────────────────────────────────────────────── + +# Start a daemon, write its PID, verify it's alive after 1s. +# Usage: $(call start_daemon,Name,command,logfile,pidfile) +define start_daemon + @echo "==> Starting $(1)..." + @nohup $(2) > $(3) 2>&1 & echo $$! > $(4) + @sleep 1 + @PID=$$(cat $(4)); \ + if kill -0 $$PID 2>/dev/null; then \ + echo "==> $(1) started (PID $$PID)"; \ + else \ + echo "ERROR: $(1) failed to start. Check $(3)"; \ + exit 1; \ + fi +endef + +# Like start_daemon but sources an env file first (in the same shell as nohup, +# so exported variables are inherited by the child process). +# +# IMPORTANT: uses ";" not "&&" between the source and nohup commands. +# With "&&", bash backgrounds the entire compound command as a subshell, +# so $! captures the subshell PID — not the bridge PID. When stop_daemon +# later kills that (already-exited) subshell PID, the bridge process +# becomes an orphan and keeps running. With ";", the source runs in the +# foreground shell and only "nohup cmd &" is backgrounded, so $! is the +# actual bridge PID. set -e (from .SHELLFLAGS) still ensures a failed +# source aborts before nohup runs. +# +# Usage: $(call start_daemon_with_env,Name,envfile,command,logfile,pidfile) +define start_daemon_with_env + @echo "==> Starting $(1)..." + @. $(2); nohup $(3) > $(4) 2>&1 & echo $$! > $(5) + @sleep 1 + @PID=$$(cat $(5)); \ + if kill -0 $$PID 2>/dev/null; then \ + echo "==> $(1) started (PID $$PID)"; \ + else \ + echo "ERROR: $(1) failed to start. Check $(4)"; \ + exit 1; \ + fi +endef + +# Stop a daemon via its PID file and wait for it to fully exit. +# Sends SIGTERM, waits up to 10s, then SIGKILL if still alive. +# No pkill — avoids terminating the make shell. +# Usage: $(call stop_daemon,Name,pidfile) +define stop_daemon + @if [ -f $(2) ]; then \ + PID=$$(cat $(2)); \ + if kill -0 $$PID 2>/dev/null; then \ + kill $$PID; \ + i=0; \ + while kill -0 $$PID 2>/dev/null && [ $$i -lt 10 ]; do \ + sleep 1; i=$$((i+1)); \ + done; \ + if kill -0 $$PID 2>/dev/null; then \ + echo "==> $(1): SIGTERM ignored, sending SIGKILL..."; \ + kill -9 $$PID 2>/dev/null || true; \ + fi; \ + echo "==> $(1) stopped (PID $$PID)"; \ + else \ + echo "==> $(1) process not running (stale PID $$PID)"; \ + fi; \ + rm -f $(2); \ + else \ + echo "==> $(1) not running (no PID file)"; \ + fi +endef + +# ───────────────────────────────────────────────────────────────────────────── +# Help +# ───────────────────────────────────────────────────────────────────────────── + +bridge-help: + @grep -E '^bridge-[a-z-]+:' $(MAKEFILE_LIST) | sed 's/:.*//' | sort + +# ───────────────────────────────────────────────────────────────────────────── +# Build +# ───────────────────────────────────────────────────────────────────────────── + +bridge-build: + @echo "==> Building bridge..." + cd $(BRIDGE_DIR) && go build -o tfchain_bridge_local . + @echo "==> Bridge binary: $(BRIDGE_BIN)" + +bridge-build-tfchain: + @echo "==> Building TFChain (may take 20-40 min first time)..." + cd substrate-node && cargo build --release + @echo "==> TFChain binary: $(TFCHAIN_BIN)" + +# ───────────────────────────────────────────────────────────────────────────── +# Accounts +# ───────────────────────────────────────────────────────────────────────────── + +bridge-accounts: + cd $(SCRIPTS_DIR) && npm install --silent + BRIDGE_ENV_FILE=$(BRIDGE_ENV_FILE) node $(SCRIPTS_DIR)/bridge_accounts.js + +bridge-mv-accounts: + cd $(SCRIPTS_DIR) && npm install --silent + BRIDGE_MV_ENV_FILE=$(BRIDGE_MV_ENV_FILE) node $(SCRIPTS_DIR)/bridge_mv_accounts.js + +# ───────────────────────────────────────────────────────────────────────────── +# TFChain +# ───────────────────────────────────────────────────────────────────────────── + +bridge-tfchain-start: + @test -f $(TFCHAIN_BIN) || { echo "Run: make bridge-build-tfchain"; exit 1; } + $(call stop_daemon,TFChain,$(TFCHAIN_PID_FILE)) + @lsof -ti tcp:$(TFCHAIN_PORT) | xargs kill 2>/dev/null && sleep 1 || true + $(call start_daemon,TFChain,$(TFCHAIN_BIN) --dev --tmp,$(TFCHAIN_LOG),$(TFCHAIN_PID_FILE)) + @echo "==> Waiting for node..." + TFCHAIN_URL=$(TFCHAIN_URL) node $(SCRIPTS_DIR)/wait_for_node.js + +bridge-tfchain-stop: + $(call stop_daemon,TFChain,$(TFCHAIN_PID_FILE)) + @lsof -ti tcp:$(TFCHAIN_PORT) | xargs kill 2>/dev/null && sleep 1 || true + +# ───────────────────────────────────────────────────────────────────────────── +# Bridge setup +# ───────────────────────────────────────────────────────────────────────────── + +bridge-setup: + @test -f $(BRIDGE_ENV_FILE) || { echo "Run: make bridge-accounts"; exit 1; } + TFCHAIN_URL=$(TFCHAIN_URL) \ + BRIDGE_ENV_FILE=$(BRIDGE_ENV_FILE) \ + node $(SCRIPTS_DIR)/bridge_setup.js + +bridge-mv-setup: + @test -f $(BRIDGE_MV_ENV_FILE) || { echo "Run: make bridge-mv-accounts"; exit 1; } + TFCHAIN_URL=$(TFCHAIN_URL) \ + BRIDGE_MV_ENV_FILE=$(BRIDGE_MV_ENV_FILE) \ + VAL2_TFCHAIN_SEED="$(VAL2_TFCHAIN_SEED)" \ + VAL3_TFCHAIN_SEED="$(VAL3_TFCHAIN_SEED)" \ + node $(SCRIPTS_DIR)/bridge_mv_setup.js + +# ───────────────────────────────────────────────────────────────────────────── +# Bridge daemon (single-validator) +# ───────────────────────────────────────────────────────────────────────────── +# +# Note: start_daemon_with_env sources BRIDGE_ENV_FILE inside the same shell +# as nohup so that BRIDGE_SECRET and BRIDGE_ADDRESS are inherited by the +# child process. Sourcing in a separate @-line would not work — each @-line +# is an independent shell invocation. + +bridge-start: + @test -f $(BRIDGE_BIN) || { echo "Run: make bridge-build"; exit 1; } + @test -f $(BRIDGE_ENV_FILE) || { echo "Run: make bridge-accounts"; exit 1; } + $(call start_daemon_with_env,Bridge,$(BRIDGE_ENV_FILE),$(BRIDGE_BIN) \ + --secret "$$BRIDGE_SECRET" \ + --tfchainurl $(TFCHAIN_URL) \ + --tfchainseed "$(VAL1_TFCHAIN_SEED)" \ + --bridgewallet "$$BRIDGE_ADDRESS" \ + --persistency $(BRIDGE_DIR)/signer_local.json \ + --network $(BRIDGE_NETWORK),$(BRIDGE_LOG),$(BRIDGE_PID_FILE)) + @echo "==> Waiting for bridge to be ready..." + @i=0; \ + while [ $$i -lt 30 ] && ! grep -q "bridge_started" $(BRIDGE_LOG) 2>/dev/null; do \ + sleep 1; i=$$((i+1)); \ + done; \ + if grep -q "bridge_started" $(BRIDGE_LOG) 2>/dev/null; then \ + echo "==> Bridge ready."; \ + else \ + echo "==> Warning: bridge_started not seen in 30s, check $(BRIDGE_LOG)"; \ + fi + +bridge-stop: + $(call stop_daemon,Bridge,$(BRIDGE_PID_FILE)) + +bridge-test: + @test -f $(BRIDGE_ENV_FILE) || { echo "Run: make bridge-accounts"; exit 1; } + @echo "==> Running bridge E2E tests..." + TFCHAIN_URL=$(TFCHAIN_URL) \ + BRIDGE_ENV_FILE=$(BRIDGE_ENV_FILE) \ + BRIDGE_PID_FILE=$(BRIDGE_PID_FILE) \ + BRIDGE_LOG_FILE=$(BRIDGE_LOG) \ + BRIDGE_BIN=$(BRIDGE_BIN) \ + VAL1_TFCHAIN_SEED="$(VAL1_TFCHAIN_SEED)" \ + node $(SCRIPTS_DIR)/bridge_tests.js + +bridge-clean: bridge-stop bridge-tfchain-stop + rm -f $(BRIDGE_DIR)/signer_local.json + rm -f $(BRIDGE_DIR)/signer_local.json.idem.db + rm -f $(BRIDGE_LOG) $(TFCHAIN_LOG) + rm -f $(BRIDGE_PID_FILE) $(TFCHAIN_PID_FILE) + rm -f $(BRIDGE_ENV_FILE) + +bridge-dev: bridge-clean bridge-build $(TFCHAIN_BIN) bridge-accounts \ + bridge-tfchain-start bridge-setup bridge-start bridge-test + +# ───────────────────────────────────────────────────────────────────────────── +# Multi-validator bridge +# ───────────────────────────────────────────────────────────────────────────── + +bridge-mv-start: + @test -f $(BRIDGE_BIN) || { echo "Run: make bridge-build"; exit 1; } + @test -f $(BRIDGE_MV_ENV_FILE) || { echo "Run: make bridge-mv-accounts"; exit 1; } + $(call start_daemon_with_env,Val1,$(BRIDGE_MV_ENV_FILE),$(BRIDGE_BIN) \ + --secret "$$VAL1_STELLAR_SECRET" \ + --tfchainurl $(TFCHAIN_URL) \ + --tfchainseed "$(VAL1_TFCHAIN_SEED)" \ + --bridgewallet "$$BRIDGE_ADDRESS" \ + --persistency $(BRIDGE_DIR)/signer_mv_1.json \ + --network $(BRIDGE_NETWORK),/tmp/bridge_mv_1.log,/tmp/bridge_mv_1.pid) + $(call start_daemon_with_env,Val2,$(BRIDGE_MV_ENV_FILE),$(BRIDGE_BIN) \ + --secret "$$VAL2_STELLAR_SECRET" \ + --tfchainurl $(TFCHAIN_URL) \ + --tfchainseed "$(VAL2_TFCHAIN_SEED)" \ + --bridgewallet "$$BRIDGE_ADDRESS" \ + --persistency $(BRIDGE_DIR)/signer_mv_2.json \ + --network $(BRIDGE_NETWORK),/tmp/bridge_mv_2.log,/tmp/bridge_mv_2.pid) + $(call start_daemon_with_env,Val3,$(BRIDGE_MV_ENV_FILE),$(BRIDGE_BIN) \ + --secret "$$VAL3_STELLAR_SECRET" \ + --tfchainurl $(TFCHAIN_URL) \ + --tfchainseed "$(VAL3_TFCHAIN_SEED)" \ + --bridgewallet "$$BRIDGE_ADDRESS" \ + --persistency $(BRIDGE_DIR)/signer_mv_3.json \ + --network $(BRIDGE_NETWORK),/tmp/bridge_mv_3.log,/tmp/bridge_mv_3.pid) + @echo "==> Waiting for validators to be ready..." + @for i in 1 2 3; do \ + j=0; \ + while [ $$j -lt 30 ] && ! grep -q bridge_started /tmp/bridge_mv_$$i.log 2>/dev/null; do \ + sleep 1; j=$$((j+1)); \ + done; \ + if grep -q bridge_started /tmp/bridge_mv_$$i.log 2>/dev/null; then \ + echo "==> Val$$i ready"; \ + else \ + echo "==> WARNING: Val$$i bridge_started not seen in 30s. Last lines:"; \ + tail -5 /tmp/bridge_mv_$$i.log 2>/dev/null || echo "(no log)"; \ + fi; \ + done + +bridge-mv-stop: + $(call stop_daemon,Val1,/tmp/bridge_mv_1.pid) + $(call stop_daemon,Val2,/tmp/bridge_mv_2.pid) + $(call stop_daemon,Val3,/tmp/bridge_mv_3.pid) + +bridge-mv-test: + @test -f $(BRIDGE_MV_ENV_FILE) || { echo "Run: make bridge-mv-accounts"; exit 1; } + @echo "==> Running multi-validator E2E tests..." + TFCHAIN_URL=$(TFCHAIN_URL) \ + BRIDGE_MV_ENV_FILE=$(BRIDGE_MV_ENV_FILE) \ + BRIDGE_BIN=$(BRIDGE_BIN) \ + BRIDGE_DIR=$(BRIDGE_DIR) \ + VAL1_TFCHAIN_SEED="$(VAL1_TFCHAIN_SEED)" \ + VAL2_TFCHAIN_SEED="$(VAL2_TFCHAIN_SEED)" \ + VAL3_TFCHAIN_SEED="$(VAL3_TFCHAIN_SEED)" \ + node $(SCRIPTS_DIR)/bridge_mv_tests.js + +bridge-mv-clean: bridge-mv-stop bridge-tfchain-stop + rm -f $(BRIDGE_DIR)/signer_mv_*.json + rm -f $(BRIDGE_DIR)/signer_mv_*.json.idem.db + rm -f /tmp/bridge_mv_*.log + rm -f /tmp/bridge_mv_*.pid + rm -f $(TFCHAIN_LOG) $(TFCHAIN_PID_FILE) + rm -f $(BRIDGE_MV_ENV_FILE) + +bridge-mv-dev: bridge-mv-clean bridge-build $(TFCHAIN_BIN) bridge-mv-accounts \ + bridge-tfchain-start bridge-mv-setup bridge-mv-start bridge-mv-test + +# Build TFChain only if binary is missing (expensive Rust build) +$(TFCHAIN_BIN): + $(MAKE) bridge-build-tfchain + +# ───────────────────────────────────────────────────────────────────────────── +# End bridge local development environment +# ───────────────────────────────────────────────────────────────────────────── + .PHONY: version-bump # * Usage Examples:* @@ -16,8 +336,8 @@ version-bump: branch_name="$$default_branch-bump-version-to-$$new_version"; \ git checkout -b $$branch_name; \ current_spec_version=$$(sed -n -e 's/^.*spec_version: \([0-9]\+\),$$/\1/p' substrate-node/runtime/src/lib.rs); \ - if [ -z "$${retain_spec_version}" ]; then \ - current_spec_version=$$(sed -n -e 's/^.*spec_version: \([0-9]\+\),$$/\1/p' substrate-node/runtime/src/lib.rs); \ + new_spec_version=""; \ + if [ -z "$${retain_spec_version:-}" ]; then \ echo "Current spec_version: $$current_spec_version"; \ new_spec_version=$$((current_spec_version + 1)); \ echo "New spec_version: $$new_spec_version"; \ @@ -36,7 +356,7 @@ version-bump: sed -i "s/^appVersion: .*/appVersion: '$$new_version'/" activation-service/helm/tfchainactivationservice/Chart.yaml; \ cd substrate-node && cargo metadata -q 1> /dev/null && cd ..; \ git add substrate-node/Cargo.toml substrate-node/Cargo.lock substrate-node/charts/substrate-node/Chart.yaml bridge/tfchain_bridge/chart/tfchainbridge/Chart.yaml activation-service/helm/tfchainactivationservice/Chart.yaml activation-service/package.json clients/tfchain-client-js/package.json scripts/package.json tools/fork-off-substrate/package.json substrate-node/runtime/src/lib.rs; \ - if [ -z "$${new_spec_version}" ]; then \ + if [ -z "$${new_spec_version:-}" ]; then \ git commit -m "Bump version to $$new_version"; \ else \ git commit -m "Bump version to $$new_version (spec v$$new_spec_version)"; \ diff --git a/bridge/docs/bridging.md b/bridge/docs/bridging.md index 22dc3dbe6..17ae125ac 100644 --- a/bridge/docs/bridging.md +++ b/bridge/docs/bridging.md @@ -11,7 +11,7 @@ This document will explain how you can transfer TFT from TF Chain to Stellar and ## Stellar to TF Chain -Transfer the TFT from your Stellar wallet to bridge wallet address that you configured. A depositfee of 1 TFT will be taken, so make sure you send a larger amount as 1 TFT. +Transfer the TFT from your Stellar wallet to bridge wallet address that you configured. A depositfee will be taken (1 TFT on mainnet/testnet by default), so make sure you send a larger amount than the fee. ### Transfer to TF Chain @@ -29,7 +29,7 @@ To deposit to a TF Grid object, this object **must** exists. If the object is no ## TF Chain to Stellar Browse to https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Ftfchain.grid.tf#/extrinsics (for mainnet), select tftBridgeModule and extrinsic: `swap_to_stellar()`. Provide your stellar target address and amount and sign it with your account holding the tft balance. -Again, a withdrawfee of 1 TFT will be taken, so make sure you send a larger amount as 1 TFT. +Again, a withdrawfee will be taken (1 TFT on mainnet/testnet by default), so make sure you send a larger amount than the fee. The amount withdrawn from TF Chain will be sent to your Stellar wallet. diff --git a/bridge/docs/local_development_setup.md b/bridge/docs/local_development_setup.md new file mode 100644 index 000000000..a3e35b181 --- /dev/null +++ b/bridge/docs/local_development_setup.md @@ -0,0 +1,256 @@ +# Bridge Local Development Setup + +This document describes how to run a complete local bridge environment for development and +testing — single-validator and multi-validator — using the Make targets provided in the +repository root. + +--- + +## Prerequisites + +| Tool | Minimum version | Notes | +|---|---|---| +| Go | 1.21 | `go version` | +| Rust + Cargo | stable | via [rustup](https://rustup.rs/) | +| Node.js + npm | 18 | `node --version` | +| Internet | — | Stellar testnet (Friendbot + Horizon) | + +--- + +## Quick Start + +### Single-validator + +```bash +make bridge-dev +``` + +That's it. On first run this builds TFChain (Rust, ~20–40 min). Every subsequent run reuses +the existing binary and takes ~1 min. + +### Multi-validator (3 daemons, 2-of-3 threshold) + +```bash +make bridge-mv-dev +``` + +Same one-shot command. Spins up 3 bridge daemons (Alice, Bob, Charlie), configures the bridge +Stellar account as a 2-of-3 multi-sig, and runs the full MV test suite. + +--- + +## What `make bridge-dev` Does + +| Step | Target | What happens | +|---|---|---| +| 1 | `bridge-clean` | Kill any running bridge/TFChain processes, delete persistency files and logs | +| 2 | `bridge-build` | `go build` the bridge binary (fast, ~5s) | +| 3 | _(auto)_ | Build TFChain node if binary missing (`substrate-node/target/release/tfchain`) | +| 4 | `bridge-accounts` | Generate fresh Stellar keypairs; fund via Friendbot; create TFT trustlines; issue TFT to bridge via `path_payment_strict_send` and to user via `payment`; write `/tmp/bridge_local_env.sh` | +| 5 | `bridge-tfchain-start` | Start TFChain `--dev --tmp`; poll WS until node is ready | +| 6 | `bridge-setup` | Register Alice as bridge validator; set bridge wallet, fee account, deposit/withdraw fees via sudo | +| 7 | `bridge-start` | Start bridge daemon; wait for `bridge_started` log entry | +| 8 | `bridge-test` | Run 4-scenario E2E test suite | + +TFChain is built once via Make's file-dependency model. If `substrate-node/target/release/tfchain` +already exists, the Rust build step is skipped entirely. + +--- + +## Individual Targets + +```bash +# Build +make bridge-build # Go build only (fast) +make bridge-build-tfchain # Rust build (slow, one-time) + +# Environment lifecycle +make bridge-accounts # (Re)generate Stellar accounts → /tmp/bridge_local_env.sh +make bridge-tfchain-start # Start TFChain dev node +make bridge-setup # Configure bridge pallet on TFChain +make bridge-start # Start bridge daemon +make bridge-stop # Stop bridge daemon +make bridge-tfchain-stop # Stop TFChain node +make bridge-clean # Stop everything + delete all local state + +# Testing +make bridge-test # Run E2E tests against a running environment +``` + +### Configuration overrides + +All targets accept environment variable overrides: + +```bash +TFCHAIN_URL=ws://localhost:9944 \ # default +BRIDGE_TFT_FLOAT=20000 \ # TFT issued to bridge wallet +USER_TFT_AMOUNT=1000 \ # TFT issued to test user +DEPOSIT_FEE=10000000 \ # 1 TFT (7 decimal places) +WITHDRAW_FEE=10000000 \ # 1 TFT +BRIDGE_ENV_FILE=/tmp/bridge_local_env.sh \ + make bridge-dev +``` + +--- + +## Account Sharing Between Steps + +All scripts share account details via a single env file written by `make bridge-accounts`: + +``` +/tmp/bridge_local_env.sh # single-validator +/tmp/bridge_mv_env.sh # multi-validator +``` + +Each subsequent script (`bridge_setup.js`, `bridge_tests.js`, etc.) calls `loadEnv()` at +startup to read this file into `process.env`. The Makefile shell targets source it for +`BRIDGE_SECRET`, `BRIDGE_ADDRESS`, etc. + +Re-running `make bridge-accounts` generates fresh Stellar keypairs and invalidates the current +environment — you would need to re-run `bridge-setup` and `bridge-start` as well. The +`make bridge-clean` + `make bridge-dev` cycle handles this automatically. + +--- + +## Logs + +```bash +tail -f /tmp/bridge_local.log # bridge daemon +tail -f /tmp/tfchain_local.log # TFChain node + +# Multi-validator +tail -f /tmp/bridge_mv_1.log # Val1 (Alice) +tail -f /tmp/bridge_mv_2.log # Val2 (Bob) +tail -f /tmp/bridge_mv_3.log # Val3 (Charlie) +``` + +--- + +## Tests + +### Single-validator tests (`make bridge-test`) + +| Test | Description | Expected outcome | +|---|---|---| +| 1 | Normal withdraw | Swap 2 TFT on TFChain → receive 1 TFT on Stellar (1 TFT fee) | +| 2 | Batch withdraws | 5 simultaneous swaps in one block → all 5 delivered | +| 3 | Bad deposit | Send TFT to bridge without memo → full refund on Stellar | +| 4 | Crash recovery | SIGKILL bridge mid-withdraw → restart → delivery completes | + +### Multi-validator tests (`make bridge-mv-test`) + +| Test | Description | Expected outcome | +|---|---|---| +| MV1 | Normal withdraw | 3 validators, threshold=2; 1 TFT delivered | +| MV2 | Deposit/mint | All 3 validators propose; mint threshold met | +| MV3 | Bad deposit | All 3 detect bad deposit; full refund delivered | +| MV4 | Validator offline | Val3 killed; Val1+Val2 meet threshold=2; refund works; Val3 restarted after | +| MV5 | Batch withdraws | 3 simultaneous burns; all 3 delivered (uses expiry recovery if sequence collision) | + +The test runner exits non-zero on any failure, making it composable with CI. + +--- + +## Multi-validator Setup Details + +### What `make bridge-mv-dev` does + +| Step | Target | What happens | +|---|---|---| +| 1 | `bridge-mv-clean` | Kill all 3 daemons, delete MV persistency files and logs | +| 2 | `bridge-build` | Build bridge binary | +| 3 | _(auto)_ | Build TFChain if binary missing | +| 4 | `bridge-mv-accounts` | Generate 4 keypairs (val1=bridge, val2, val3, user); fund via Friendbot; create trustlines; fund bridge via `path_payment_strict_send`; configure bridge as 2-of-3 multi-sig (val1 master key, val2+val3 added as signers, thresholds: low=1, med=2, high=3); write `/tmp/bridge_mv_env.sh` | +| 5 | `bridge-tfchain-start` | Start TFChain dev node | +| 6 | `bridge-mv-setup` | Create twins for Alice, Bob, Charlie; register all 3 as validators; set bridge wallet, fee account, fees | +| 7 | `bridge-mv-start` | Start 3 bridge daemons; wait for all 3 to log `bridge_started` | +| 8 | `bridge-mv-test` | Run MV1–MV5 test suite | + +### Multi-sig architecture + +``` +Bridge Stellar account = Val1's keypair (master key, weight=1) +Val2 keypair added as signer (weight=1) +Val3 keypair added as signer (weight=1) + +Thresholds: + low = 1 (any 1 of 3 can change account options) + med = 2 (any 2 of 3 must sign TFT payment transactions) + high = 3 (all 3 must sign for account deletion etc.) +``` + +Each bridge daemon signs with its own validator Stellar key and stores its signature on TFChain. +When `BurnTransactionReady` or `RefundTransactionReady` fires, the first validator to receive it +fetches all stored signatures from TFChain and builds a multi-sig Stellar transaction meeting +the threshold. + +--- + +## Fee Mechanics + +TFT uses 7 decimal places: `1 TFT = 10,000,000 base units`. +Default fees are 1 TFT each (configurable via `DEPOSIT_FEE` and `WITHDRAW_FEE`). + +### Deposit (Stellar → TFChain) + +Two enforcement layers: + +1. **Bridge** (`mint.go`): if incoming Stellar amount ≤ `DepositFee`, bridge refunds on Stellar + without proposing a mint (avoids an on-chain tx that would fail anyway). +2. **Pallet** (`execute_mint_transaction`): deducts `DepositFee` and mints `amount - deposit_fee` + to the user's TFChain account. + +### Withdraw (TFChain → Stellar) + +One enforcement layer: + +1. **Pallet** (`swap_to_stellar`): rejects if `amount ≤ WithdrawFee` with + `AmountIsLessThanWithdrawFee`. If valid, stores `burn_amount = amount - withdraw_fee` in the + event. The bridge reads this value directly and sends it to Stellar — no additional fee applied. + +--- + +## Bridge Funding: Why `path_payment_strict_send` + +The bridge Stellar deposit monitor only watches `payment` operations on the bridge account. +A `path_payment_strict_send` operation is structurally different and is not picked up by the +monitor — so funding the bridge wallet this way does not trigger a spurious refund on startup +rescan. Always use `path_payment_strict_send` when issuing TFT to the bridge account in test +setup. + +--- + +## Idempotency and Crash Recovery + +The bridge writes an idempotency record (bbolt DB at `.idem.db`) before submitting +any Stellar transaction: + +- `PROCESSING`: Stellar tx may or may not have been submitted +- `COMPLETED`: Stellar tx submitted and TFChain confirmation done + +On restart, `reconcilePendingTransactions` scans all `PROCESSING` entries. For each: + +1. Fetch recent outgoing Stellar transactions from Horizon (single request, reused for all checks) +2. Search by memo text (primary) then by sequence number (fallback) +3. If found: proceed directly to TFChain confirmation — no double-spend +4. If not found: log `"no Stellar tx found by memo or sequence, safe to retry"` — re-submit on + next Ready event + +--- + +## Known Limitations + +1. **Sequence coordination under load**: All validators sign Stellar transactions at proposal time + with a fixed sequence number. If another Stellar transaction from the bridge account is + submitted between proposal and `Ready` events, the stored signatures reference a stale + sequence → all validators get `tx_bad_seq` on first attempt. Recovery is automatic via the + expiry cycle (~20 blocks, ~2 min delay). This is a pre-existing design constraint, not + introduced by this PR. + +2. **Stellar testnet Friendbot rate limits**: If Friendbot rejects account funding (rate limited + or account already exists with funds), re-running `make bridge-accounts` generates fresh + keypairs. This requires re-running `make bridge-setup` and `make bridge-start` as well — + or simply re-run `make bridge-dev` for a clean slate. + +3. **`--tmp` chain**: State is lost on TFChain restart. For persistent sessions, replace + `--tmp` with `--base-path /tmp/tfchain-data` in the `bridge-tfchain-start` Makefile target. diff --git a/bridge/docs/multinode.md b/bridge/docs/multinode.md index 4fa876109..df3f62af6 100644 --- a/bridge/docs/multinode.md +++ b/bridge/docs/multinode.md @@ -41,9 +41,13 @@ Following predefined tfchain keys can be used to start a bridge daemon: By default, only 1 bridge validator is inserted in the tfchain runtime. In this example we will run a 3 node bridge setup, so we need to add 2 keys to the bridge validators on tfchain. +> **Note:** tfchain does **not** have a `sudo` pallet. `addBridgeValidator` is a restricted call +> gated by `EnsureRootOrCouncilApproval` (council 3/5 threshold). In `--dev` mode, Alice is the +> sole council member so a council proposal from Alice satisfies the threshold immediately. + - Open: https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/extrinsics - Select "Alice" account -- Select `Sudo` -> `Call` -> `tftBridgeModule` -> `addBridgeValidator` and input: `5CGQ6zra7qXw4RNVwUYM4bxHjxdj9VZH7DKsDwhfBCgVPmUZ` +- Select `council` -> `propose` (with threshold=1) -> `tftBridgeModule` -> `addBridgeValidator` and input: `5CGQ6zra7qXw4RNVwUYM4bxHjxdj9VZH7DKsDwhfBCgVPmUZ` - Do the same for: `5CVLaAHnvdX1CBsaKFKgfyfb91y5ApNP7FLSaczKginip1ho` We inserted 2 addresses as authorized bridge validators, these addresses map to the last 2 of the predefined keys listed above @@ -97,7 +101,13 @@ Now Open a second terminal pane and execute: Now open a third teminal pane and execute: ```sh -./tfchain_bridge --secret SBFMRNGJQ5NMVXJKDMSBHDDCHVXOJE4E7A62A4MHAD4A5DH5RU5ONWVK --tfchainurl ws://localhost:9944 --tfchainseed "employ split promote annual couple elder remain cricket company fitness senior fiscal" --bridgewallet GAYJSBPBQ3J32CZZ72OM3GZP646KSVD3V5QB3WBJSSGPYHYS5MZSS4Z6 --persistency ./signer3.json --network testnet +./tfchain_bridge \ + --secret SBFMRNGJQ5NMVXJKDMSBHDDCHVXOJE4E7A62A4MHAD4A5DH5RU5ONWVK \ + --tfchainurl ws://localhost:9944 \ + --tfchainseed "remind bird banner word spread volume card keep want faith insect mind" \ + --bridgewallet GAYJSBPBQ3J32CZZ72OM3GZP646KSVD3V5QB3WBJSSGPYHYS5MZSS4Z6 \ + --persistency ./signer3.json \ + --network testnet ``` If all goes well, you should see something similar to following output: @@ -144,6 +154,11 @@ Now, request some Testnet TFT by doing a swap on the stellar dex using the same Given this command did not give an error, your account you just generated now has 100 TFT. +> **Note:** The `stellar-utils faucet` command may fail on Stellar testnet if there are no active +> TFT orders on the DEX order book. See +> [setup_issues_and_workarounds.md](./setup_issues_and_workarounds.md#11-stellar-testnet-tft-faucet-has-no-liquidity) +> for the workaround. + ## Step 4: Deposit TFT to the bridge Make sure that @@ -162,4 +177,4 @@ Now construct a memo message indicating which twin you will deposit to: "twin_TW ./stellar-utils transfer 50 "twin_1" GAYJSBPBQ3J32CZZ72OM3GZP646KSVD3V5QB3WBJSSGPYHYS5MZSS4Z6 --secret SDGRCA63GSP4MSASFAWX5FORTS6ATQMK63YL6ZMF7YIFEJVBTLJDJA3M ``` -Now you should have received the tokens minus the depositfee on your account on tfchain (the default depositfee is 10 TFT). +Now you should have received the tokens minus the depositfee on your account on tfchain (the default depositfee is 1 TFT). diff --git a/bridge/docs/observability.md b/bridge/docs/observability.md index 0d5202877..4dce75fcc 100644 --- a/bridge/docs/observability.md +++ b/bridge/docs/observability.md @@ -70,6 +70,27 @@ For example, if a customer is complaining that their deposit never bridged, you - `withdraw_proposed`: a withdraw has proposed or signed by the bridge instance. - `withdraw_postponed`: a withdraw has postponed due to a problem in sending this transaction to the stellar network and will be retried later. - `withdraw_completed`: a withdraw has completed and received on the target stellar account. +- `withdraw_crash_recovery`: The bridge detected a withdraw in `PROCESSING` state from a previous + run (possible crash between Stellar submission and TFChain confirmation). The bridge queries + Horizon to determine if the Stellar tx was already submitted before deciding whether to retry. +- `withdraw_recovered`: The bridge found an existing Stellar tx for a PROCESSING withdraw (by memo + or by sequence number for pre-upgrade txs) and completed the TFChain confirmation without + re-submitting. +- `withdraw_proposal_failed`: A specific burn proposal within a `force_batch` extrinsic failed + (index known via `BatchInterrupted`). The remaining proposals in the batch were still executed. +- `refund_proposal_failed`: A specific refund proposal within a `force_batch` extrinsic failed. + Analogous to `withdraw_proposal_failed`. +- `batch_proposal_started`: The bridge is processing proposal events from the current block + (BurnTransactionCreated, BurnTransactionExpired, RefundTransactionCreated, + RefundTransactionExpired) and will submit them all as a single `Utility.force_batch` extrinsic. + Check `withdraws` and `refunds` fields for event counts. +- `batch_proposal_completed`: The bridge submitted a unified batch of burn and refund proposals. + Check `succeeded` and `failed` fields for counts. +- `event_refund_tx_created_received`: The bridge received a `RefundTransactionCreated` event + from TFChain. Other validators use this to add their signature without waiting for expiry. +- `refund_crash_recovery`: The bridge detected a refund in `PROCESSING` state from a previous run. +- `refund_recovered`: The bridge found an existing Stellar refund tx (by MemoReturn hash or by + sequence number for pre-upgrade txs) and completed the TFChain confirmation without re-submitting. ##### Bridge vault account related - `payment_received` : This event represents successful payment to the bridge account (a deposit). diff --git a/bridge/docs/single_node.md b/bridge/docs/single_node.md index 88b24b9d2..de85c5db1 100644 --- a/bridge/docs/single_node.md +++ b/bridge/docs/single_node.md @@ -82,6 +82,13 @@ Now, request some Testnet TFT by doing a swap on the stellar dex using the same Given this command did not give an error, your account you just generated now has 100 TFT. +> **Note:** The `stellar-utils faucet` command performs a DEX path payment (XLM → TFT). On +> Stellar testnet this may fail with a path payment error if there are no active TFT orders on +> the testnet DEX order book. If you encounter this, ask a team member who operates the testnet +> TFT issuer to send you testnet TFT directly, or refer to +> [setup_issues_and_workarounds.md](./setup_issues_and_workarounds.md#11-stellar-testnet-tft-faucet-has-no-liquidity) +> for the custom issuer workaround. + ## Step 4: Deposit TFT to the bridge Make sure that @@ -100,4 +107,4 @@ Now construct a memo message indicating which twin you will deposit to: "twin_TW ./stellar-utils transfer GAYJSBPBQ3J32CZZ72OM3GZP646KSVD3V5QB3WBJSSGPYHYS5MZSS4Z6 50 "twin_1" --secret SDGRCA63GSP4MSASFAWX5FORTS6ATQMK63YL6ZMF7YIFEJVBTLJDJA3M ``` -Now you should have received the tokens minus the depositfee on your account on tfchain (the default depositfee is 10 TFT). +Now you should have received the tokens minus the depositfee on your account on tfchain (the default depositfee is 1 TFT). diff --git a/bridge/tfchain_bridge/go.mod b/bridge/tfchain_bridge/go.mod index f84a4a519..252ae18b7 100644 --- a/bridge/tfchain_bridge/go.mod +++ b/bridge/tfchain_bridge/go.mod @@ -15,7 +15,7 @@ require ( github.com/rs/zerolog v1.26.0 github.com/sirupsen/logrus v1.4.2 // indirect github.com/stellar/go v0.0.0-20210922122349-e6f322c047c5 - github.com/stretchr/objx v0.3.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/vedhavyas/go-subkey v1.0.3 ) @@ -50,7 +50,8 @@ require ( github.com/rs/cors v1.8.2 // indirect github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 // indirect github.com/stellar/go-xdr v0.0.0-20201028102745-f80a23dac78a // indirect - github.com/stretchr/testify v1.7.2 // indirect + github.com/stretchr/testify v1.8.1 // indirect + go.etcd.io/bbolt v1.3.9 golang.org/x/crypto v0.7.0 // indirect golang.org/x/sys v0.20.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect diff --git a/bridge/tfchain_bridge/go.sum b/bridge/tfchain_bridge/go.sum index 0e71ad6be..c425ec063 100644 --- a/bridge/tfchain_bridge/go.sum +++ b/bridge/tfchain_bridge/go.sum @@ -341,13 +341,20 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= @@ -381,6 +388,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= diff --git a/bridge/tfchain_bridge/pkg/bridge/bridge.go b/bridge/tfchain_bridge/pkg/bridge/bridge.go index 92c9d48d7..6f722390d 100644 --- a/bridge/tfchain_bridge/pkg/bridge/bridge.go +++ b/bridge/tfchain_bridge/pkg/bridge/bridge.go @@ -2,12 +2,15 @@ package bridge import ( "context" + "fmt" "strconv" "time" + "github.com/centrifuge/go-substrate-rpc-client/v4/types" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + hProtocol "github.com/stellar/go/protocols/horizon" "github.com/threefoldtech/tfchain/bridge/tfchain_bridge/pkg" "github.com/threefoldtech/tfchain/bridge/tfchain_bridge/pkg/stellar" subpkg "github.com/threefoldtech/tfchain/bridge/tfchain_bridge/pkg/substrate" @@ -16,6 +19,12 @@ import ( const ( BridgeNetwork = "stellar" MinimumBalance = 0 + + // maxExecutionBatchSize caps the number of Ready events processed per cycle. + // This limits the number of PROCESSING entries in BoltDB at any time, ensuring + // crash recovery can find all Stellar payments within the 200-tx outgoing page. + // 100 burns + 100 refunds = 200 max, matching the Stellar query limit. + maxExecutionBatchSize = 100 ) // Bridge is a high lvl structure which listens on contract events and bridge-related @@ -24,6 +33,7 @@ type Bridge struct { wallet *stellar.StellarWallet subClient *subpkg.SubstrateClient blockPersistency *pkg.ChainPersistency + idempotency *pkg.IdempotencyStore config *pkg.BridgeConfig depositFee int64 } @@ -64,9 +74,17 @@ func NewBridge(ctx context.Context, cfg pkg.BridgeConfig) (*Bridge, string, erro return nil, "", err } + // Initialize idempotency store alongside the persistency file + idempotencyPath := cfg.PersistencyFile + ".idem.db" + idempotency, err := pkg.NewIdempotencyStore(idempotencyPath) + if err != nil { + return nil, "", err + } + bridge := &Bridge{ subClient: subClient, blockPersistency: blockPersistency, + idempotency: idempotency, wallet: wallet, config: &cfg, depositFee: depositFee, @@ -106,13 +124,22 @@ func (bridge *Bridge) Start(ctx context.Context) error { Bool("rescan_flag", bridge.config.RescanBridgeAccount). Int64("deposit_fee", bridge.depositFee)). Msg("the bridge instance has started") + + // Close idempotency store when Start returns + defer bridge.idempotency.Close() + + // Reconcile any PROCESSING transactions from a previous run that may have + // crashed between Stellar submit and TFChain confirmation + if err := bridge.reconcilePendingTransactions(ctx); err != nil { + return errors.Wrap(err, "startup reconciliation failed") + } + height, err := bridge.blockPersistency.GetHeight() if err != nil { return errors.Wrap(err, "an error occurred while reading block height from persistency") } - log.Debug(). - Msg("The Stellar subscription is starting") + log.Debug().Msg("The Stellar subscription is starting") stellarSub := make(chan stellar.MintEventSubscription) go func() { defer close(stellarSub) @@ -146,51 +173,110 @@ func (bridge *Bridge) Start(ctx context.Context) error { select { case data := <-tfchainSub: if data.Err != nil { - return errors.Wrap(err, "failed to get tfchain events") + return errors.Wrap(data.Err, "failed to get tfchain events") } - for _, withdrawCreatedEvent := range data.Events.WithdrawCreatedEvents { - err := bridge.handleWithdrawCreated(ctx, withdrawCreatedEvent) - if err != nil { - // If the TX is already withdrawn or refunded (minted on tfchain) skip - if errors.Is(err, pkg.ErrTransactionAlreadyBurned) || errors.Is(err, pkg.ErrTransactionAlreadyMinted) { - continue - } - return errors.Wrap(err, "an error occurred while handling WithdrawCreatedEvents") - } - } - for _, withdrawExpiredEvent := range data.Events.WithdrawExpiredEvents { - err := bridge.handleWithdrawExpired(ctx, withdrawExpiredEvent) - if err != nil { - return errors.Wrap(err, "an error occurred while handling WithdrawExpiredEvents") - } + + // Process Ready events — submit all to Stellar first, then batch TFChain confirmations. + // Cap at maxExecutionBatchSize to stay within the 200-tx Stellar reconciliation window. + var confirmedBurnIDs []uint64 + withdrawEvents := data.Events.WithdrawReadyEvents + if len(withdrawEvents) > maxExecutionBatchSize { + withdrawEvents = withdrawEvents[:maxExecutionBatchSize] } - for _, withdawReadyEvent := range data.Events.WithdrawReadyEvents { - err := bridge.handleWithdrawReady(ctx, withdawReadyEvent) + for _, withdrawReadyEvent := range withdrawEvents { + txID, err := bridge.handleWithdrawReady(ctx, withdrawReadyEvent) if err != nil { if errors.Is(err, pkg.ErrTransactionAlreadyBurned) { continue } return errors.Wrap(err, "an error occurred while handling WithdrawReadyEvents") } - } - for _, refundExpiredEvent := range data.Events.RefundExpiredEvents { - err := bridge.handleRefundExpired(ctx, refundExpiredEvent) - if err != nil { - return errors.Wrap(err, "an error occurred while handling RefundExpiredEvents") + if txID > 0 { + confirmedBurnIDs = append(confirmedBurnIDs, txID) } } - for _, refundReadyEvent := range data.Events.RefundReadyEvents { - err := bridge.handleRefundReady(ctx, refundReadyEvent) + + var confirmedRefundHashes []string + refundEvents := data.Events.RefundReadyEvents + if len(refundEvents) > maxExecutionBatchSize { + refundEvents = refundEvents[:maxExecutionBatchSize] + } + for _, refundReadyEvent := range refundEvents { + txHash, err := bridge.handleRefundReady(ctx, refundReadyEvent) if err != nil { if errors.Is(err, pkg.ErrTransactionAlreadyRefunded) { continue } return errors.Wrap(err, "an error occurred while handling RefundReadyEvents") } + if txHash != "" { + confirmedRefundHashes = append(confirmedRefundHashes, txHash) + } + } + + // Batch all TFChain confirmations into single force_batch extrinsics. + // This confirms all burns/refunds in one block instead of N sequential blocks. + if err := bridge.subClient.BatchSetWithdrawExecuted(ctx, confirmedBurnIDs); err != nil { + return errors.Wrap(err, "failed to batch set withdraws executed") + } + for _, txID := range confirmedBurnIDs { + if err := bridge.idempotency.MarkWithdrawCompleted(txID); err != nil { + log.Warn().Err(err).Uint64("tx_id", txID).Msg("idempotency: failed to mark withdraw completed") + } + log.Info(). + Str("event_action", "withdraw_completed"). + Str("event_kind", "event"). + Str("category", "withdraw"). + Str("trace_id", fmt.Sprint(txID)). + Msg("the withdraw has proceed") + log.Info(). + Str("event_action", "transfer_completed"). + Str("event_kind", "event"). + Str("category", "transfer"). + Str("trace_id", fmt.Sprint(txID)). + Dict("metadata", zerolog.Dict(). + Str("outcome", "bridged")). + Msg("the transfer has completed") + } + + if err := bridge.subClient.BatchSetRefundTransactionExecuted(ctx, confirmedRefundHashes); err != nil { + return errors.Wrap(err, "failed to batch set refunds executed") + } + for _, txHash := range confirmedRefundHashes { + if err := bridge.idempotency.MarkRefundCompleted(txHash); err != nil { + log.Warn().Err(err).Str("tx_hash", txHash).Msg("idempotency: failed to mark refund completed") + } + log.Info(). + Str("event_action", "refund_completed"). + Str("event_kind", "event"). + Str("category", "refund"). + Str("trace_id", txHash). + Msg("the transaction has refunded") + log.Info(). + Str("event_action", "transfer_completed"). + Str("event_kind", "event"). + Str("category", "transfer"). + Str("trace_id", txHash). + Dict("metadata", zerolog.Dict(). + Str("outcome", "refunded")). + Msg("the transfer has completed") + } + + // Batch all proposal events (BurnCreated, BurnExpired, RefundExpired) + // into a single Utility.force_batch extrinsic. This drains backlogs from + // bridge outages in one block rather than N sequential blocks. + // Note: RefundCreated is intentionally excluded — see handleProposalsBatch. + if err := bridge.handleProposalsBatch( + ctx, + data.Events.WithdrawCreatedEvents, + data.Events.WithdrawExpiredEvents, + data.Events.RefundExpiredEvents, + ); err != nil { + return errors.Wrap(err, "an error occurred while handling proposal events") } case data := <-stellarSub: if data.Err != nil { - return errors.Wrap(err, "failed to get stellar payments") + return errors.Wrap(data.Err, "failed to get stellar payments") } for _, mEvent := range data.Events { @@ -222,3 +308,116 @@ func (bridge *Bridge) Start(ctx context.Context) error { time.Sleep(1 * time.Second) } } + +// reconcilePendingTransactions handles crash recovery by checking all transactions +// that were in PROCESSING state when the bridge last shut down. For each one, +// it checks whether the Stellar tx was actually submitted, and if so, completes +// the TFChain confirmation step. +func (bridge *Bridge) reconcilePendingTransactions(ctx context.Context) error { + log.Info().Msg("reconciling pending transactions from previous run...") + + pendingWithdraws, err := bridge.idempotency.GetPendingWithdraws() + if err != nil { + return errors.Wrap(err, "failed to get pending withdraws") + } + pendingRefunds, err := bridge.idempotency.GetPendingRefunds() + if err != nil { + return errors.Wrap(err, "failed to get pending refunds") + } + + // If there are no pending transactions, skip the Horizon fetch entirely. + if len(pendingWithdraws) == 0 && len(pendingRefunds) == 0 { + log.Info().Msg("reconciliation complete: no pending transactions") + return nil + } + + // Fetch outgoing transactions once and reuse the page for all lookups, + // avoiding one Horizon HTTP call per pending transaction. + outgoingPage, err := bridge.wallet.FetchOutgoingTransactionsPage(ctx) + if err != nil { + log.Warn().Err(err).Msg("failed to fetch Horizon transactions for reconciliation, pending transactions will retry on next event") + // Non-fatal: pending txs will be retried when the next Ready event fires. + outgoingPage = hProtocol.TransactionsPage{} + } + + // Reconcile pending withdraws + for _, txID := range pendingWithdraws { + log.Info().Uint64("tx_id", txID).Msg("reconciling pending withdraw") + + var stellarTx *hProtocol.Transaction + + // Primary: find by memo (current bridge behaviour) + if tx := bridge.wallet.FindPaymentByMemoInPage(outgoingPage, fmt.Sprint(txID)); tx != nil { + stellarTx = tx + } + + // Fallback: find by sequence number (pre-upgrade compatibility, no memo) + if stellarTx == nil { + burnTx, err := bridge.subClient.GetBurnTransaction(types.U64(txID)) + if err != nil { + log.Warn().Err(err).Uint64("tx_id", txID).Msg("failed to get burn tx for sequence lookup during reconciliation") + } else if tx := bridge.wallet.FindPaymentBySequenceInPage(outgoingPage, int64(burnTx.SequenceNumber)); tx != nil { + log.Info().Uint64("tx_id", txID).Int64("sequence_number", int64(burnTx.SequenceNumber)). + Msg("reconcile: found pre-upgrade Stellar tx by sequence number (no memo)") + stellarTx = tx + } + } + + if stellarTx != nil { + log.Info().Uint64("tx_id", txID).Msg("found existing Stellar tx, completing TFChain confirmation") + if err := bridge.subClient.RetrySetWithdrawExecuted(ctx, txID); err != nil { + log.Warn().Err(err).Uint64("tx_id", txID).Msg("failed to set withdraw executed during reconciliation") + continue + } + if err := bridge.idempotency.MarkWithdrawCompleted(txID); err != nil { + log.Warn().Err(err).Uint64("tx_id", txID).Msg("failed to mark withdraw completed during reconciliation") + } + } else { + log.Info().Uint64("tx_id", txID).Msg("no Stellar tx found by memo or sequence, will retry on next event") + } + } + + // Reconcile pending refunds + for _, txHash := range pendingRefunds { + log.Info().Str("tx_hash", txHash).Msg("reconciling pending refund") + + var stellarTx *hProtocol.Transaction + + // Primary: find by MemoReturn hash (current bridge behaviour) + if tx := bridge.wallet.FindRefundByReturnHashInPage(outgoingPage, txHash); tx != nil { + stellarTx = tx + } + + // Fallback: find by sequence number (pre-upgrade compatibility, no memo) + if stellarTx == nil { + refundTx, err := bridge.subClient.GetRefundTransaction(txHash) + if err != nil { + log.Warn().Err(err).Str("tx_hash", txHash).Msg("failed to get refund tx for sequence lookup during reconciliation") + } else if tx := bridge.wallet.FindPaymentBySequenceInPage(outgoingPage, int64(refundTx.SequenceNumber)); tx != nil { + log.Info().Str("tx_hash", txHash).Int64("sequence_number", int64(refundTx.SequenceNumber)). + Msg("reconcile: found pre-upgrade Stellar refund tx by sequence number (no memo)") + stellarTx = tx + } + } + + if stellarTx != nil { + log.Info().Str("tx_hash", txHash).Msg("found existing Stellar refund tx, completing TFChain confirmation") + if err := bridge.subClient.RetrySetRefundTransactionExecutedTx(ctx, txHash); err != nil { + log.Warn().Err(err).Str("tx_hash", txHash).Msg("failed to set refund executed during reconciliation") + continue + } + if err := bridge.idempotency.MarkRefundCompleted(txHash); err != nil { + log.Warn().Err(err).Str("tx_hash", txHash).Msg("failed to mark refund completed during reconciliation") + } + } else { + log.Info().Str("tx_hash", txHash).Msg("no Stellar tx found by return hash or sequence, will retry on next event") + } + } + + log.Info(). + Int("pending_withdraws", len(pendingWithdraws)). + Int("pending_refunds", len(pendingRefunds)). + Msg("reconciliation complete") + + return nil +} diff --git a/bridge/tfchain_bridge/pkg/bridge/refund.go b/bridge/tfchain_bridge/pkg/bridge/refund.go index d659825a4..f840b4555 100644 --- a/bridge/tfchain_bridge/pkg/bridge/refund.go +++ b/bridge/tfchain_bridge/pkg/bridge/refund.go @@ -12,9 +12,13 @@ import ( subpkg "github.com/threefoldtech/tfchain/bridge/tfchain_bridge/pkg/substrate" ) -// refund handler for stellar +// refund is called from mint.go when a bad Stellar deposit is detected (wrong memo, +// insufficient amount, etc.). It proposes a refund directly — not via the batch — +// so that all online validators propose at the same time with a consistent Stellar +// sequence number. This is the sequence anchor: if one validator batched while another +// called directly, they would sync at different times and produce incompatible signatures. func (bridge *Bridge) refund(ctx context.Context, destination string, amount int64, tx hProtocol.Transaction) error { - err := bridge.handleRefundExpired(ctx, subpkg.RefundTransactionExpiredEvent{ + err := bridge.proposeRefundDirect(ctx, subpkg.RefundTransactionExpiredEvent{ Hash: tx.Hash, Amount: uint64(amount), Target: destination, @@ -30,7 +34,10 @@ func (bridge *Bridge) refund(ctx context.Context, destination string, amount int return errors.Wrap(err, "an error occurred while saving stellar cursor") } -func (bridge *Bridge) handleRefundExpired(ctx context.Context, refundExpiredEvent subpkg.RefundTransactionExpiredEvent) error { +// proposeRefundDirect proposes a single refund transaction immediately (not via the batch). +// Used by the deposit-triggered path (mint.go → refund()) where all validators detect +// the same Stellar event at the same time and sync their Stellar sequence independently. +func (bridge *Bridge) proposeRefundDirect(ctx context.Context, refundExpiredEvent subpkg.RefundTransactionExpiredEvent) error { logger := log.Logger.With().Str("trace_id", refundExpiredEvent.Hash).Logger() refunded, err := bridge.subClient.IsRefundedAlready(refundExpiredEvent.Hash) @@ -47,6 +54,10 @@ func (bridge *Bridge) handleRefundExpired(ctx context.Context, refundExpiredEven return nil } + // Sync sequence counter before signing (see SyncSequenceNumber for rationale). + if err := bridge.wallet.SyncSequenceNumber(); err != nil { + return err + } signature, sequenceNumber, err := bridge.wallet.CreateRefundAndReturnSignature(ctx, refundExpiredEvent.Target, refundExpiredEvent.Amount, refundExpiredEvent.Hash) if err != nil { return err @@ -68,57 +79,119 @@ func (bridge *Bridge) handleRefundExpired(ctx context.Context, refundExpiredEven return nil } -func (bridge *Bridge) handleRefundReady(ctx context.Context, refundReadyEvent subpkg.RefundTransactionReadyEvent) error { +func (bridge *Bridge) handleRefundReady(ctx context.Context, refundReadyEvent subpkg.RefundTransactionReadyEvent) (string, error) { logger := log.Logger.With().Str("trace_id", refundReadyEvent.Hash).Logger() - refunded, err := bridge.subClient.IsRefundedAlready(refundReadyEvent.Hash) + txHash := refundReadyEvent.Hash + + // 1. Check idempotency store + state, err := bridge.idempotency.GetRefundState(txHash) if err != nil { - return err + return "", err + } + if state == pkg.TxStateCompleted { + logger.Info(). + Str("event_action", "refund_skipped"). + Str("event_kind", "event"). + Str("category", "refund"). + Msg("idempotency: refund already completed, skipping") + return "", pkg.ErrTransactionAlreadyRefunded } + // 2. If PROCESSING, check if Stellar tx was already submitted (crash recovery) + if state == pkg.TxStateProcessing { + logger.Warn(). + Str("event_action", "refund_crash_recovery"). + Str("event_kind", "event"). + Str("category", "refund"). + Msg("idempotency: refund in PROCESSING state (possible crash recovery)") + + // Fetch the outgoing transactions page once and reuse it for both lookups. + // Non-fatal on error: match the reconciler's behavior. Leave tx as PROCESSING; + // the next RefundTransactionReady event will retry the Horizon lookup. + outgoingPage, err := bridge.wallet.FetchOutgoingTransactionsPage(ctx) + if err != nil { + logger.Warn().Err(err).Str("tx_hash", txHash). + Msg("failed to fetch Horizon transactions for PROCESSING check; will retry on next event") + return "", nil + } + + // Primary check: look for a refund tx with matching MemoReturn hash (current bridge behaviour) + if stellarTx := bridge.wallet.FindRefundByReturnHashInPage(outgoingPage, txHash); stellarTx != nil { + logger.Info(). + Str("event_action", "refund_recovered"). + Str("event_kind", "event"). + Str("category", "refund"). + Msg("idempotency: found existing Stellar tx by return hash, deferring TFChain confirmation to batch") + return txHash, nil + } + + // Fallback: look for a tx by sequence number, covering pre-upgrade submissions + // that were made without a memo. See FindPaymentBySequenceInPage for rationale. + refundTxForSeq, err := bridge.subClient.GetRefundTransaction(txHash) + if err != nil { + return "", err + } + if stellarTxBySeq := bridge.wallet.FindPaymentBySequenceInPage(outgoingPage, int64(refundTxForSeq.SequenceNumber)); stellarTxBySeq != nil { + logger.Info(). + Str("event_action", "refund_recovered"). + Str("event_kind", "event"). + Str("category", "refund"). + Int64("sequence_number", int64(refundTxForSeq.SequenceNumber)). + Msg("idempotency: found pre-upgrade Stellar tx by sequence number (no memo), deferring TFChain confirmation to batch") + return txHash, nil + } + + logger.Info().Msg("idempotency: no Stellar tx found by return hash or sequence, safe to retry") + } + + // 3. Check TFChain: already refunded? + refunded, err := bridge.subClient.IsRefundedAlready(txHash) + if err != nil { + return "", err + } if refunded { + _ = bridge.idempotency.MarkRefundCompleted(txHash) logger.Info(). Str("event_action", "refund_skipped"). Str("event_kind", "event"). Str("category", "refund"). Msg("the transaction has already been refunded") - return pkg.ErrTransactionAlreadyRefunded + return "", pkg.ErrTransactionAlreadyRefunded } - refund, err := bridge.subClient.GetRefundTransaction(refundReadyEvent.Hash) + // 4. Get refund tx with signatures + refund, err := bridge.subClient.GetRefundTransaction(txHash) if err != nil { - return err + return "", err } - if len(refund.Signatures) == 0 { logger.Info(). Str("event_action", "refund_postponed"). Str("event_kind", "event"). Str("category", "refund"). Msg("the refund has been postponed due to the transaction signatures being removed on the TFChain side while the bridge was processing the transaction") - return nil + return "", nil } - // Todo, retry here? - if err = bridge.wallet.CreateRefundPaymentWithSignaturesAndSubmit(ctx, refund.Target, uint64(refund.Amount), refund.TxHash, refund.Signatures, int64(refund.SequenceNumber)); err != nil { - return err + // 5. Mark PROCESSING before Stellar submit + if err := bridge.idempotency.MarkRefundProcessing(txHash); err != nil { + return "", err } - err = bridge.subClient.RetrySetRefundTransactionExecutedTx(ctx, refund.TxHash) - if err != nil { - return err + // 6. Submit to Stellar + if err = bridge.wallet.CreateRefundPaymentWithSignaturesAndSubmit(ctx, refund.Target, uint64(refund.Amount), refund.TxHash, refund.Signatures, int64(refund.SequenceNumber)); err != nil { + logger.Info(). + Str("event_action", "refund_postponed"). + Str("event_kind", "event"). + Str("category", "refund"). + Dict("metadata", zerolog.Dict(). + Str("reason", err.Error())). + Msgf("the refund has been postponed due to a problem in sending this transaction to the stellar network. error was %s", err.Error()) + return "", nil // leave as PROCESSING, will reconcile on next attempt } - logger.Info(). - Str("event_action", "refund_completed"). - Str("event_kind", "event"). - Str("category", "refund"). - Msg("the transaction has refunded") - logger.Info(). - Str("event_action", "transfer_completed"). - Str("event_kind", "event"). - Str("category", "transfer"). - Dict("metadata", zerolog.Dict(). - Str("outcome", "refunded")). - Msg("the transfer has completed") - return nil + // 7. Stellar refund submitted — return txHash for batch TFChain confirmation. + // The caller (bridge.go) collects all returned txHashes and submits them as a + // single Utility.force_batch extrinsic, confirming all refunds in one block. + return refund.TxHash, nil } diff --git a/bridge/tfchain_bridge/pkg/bridge/withdraw.go b/bridge/tfchain_bridge/pkg/bridge/withdraw.go index 8f25ce531..920577fa2 100644 --- a/bridge/tfchain_bridge/pkg/bridge/withdraw.go +++ b/bridge/tfchain_bridge/pkg/bridge/withdraw.go @@ -15,155 +15,384 @@ import ( substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" ) -func (bridge *Bridge) handleWithdrawCreated(ctx context.Context, withdraw subpkg.WithdrawCreatedEvent) error { - logger := log.Logger.With().Str("trace_id", fmt.Sprint(withdraw.ID)).Logger() +// handleProposalsBatch processes all proposal events from a single TFChain block in one +// Utility.force_batch extrinsic. This covers: +// - BurnTransactionCreated → propose_burn_transaction_or_add_sig +// - BurnTransactionExpired → same (re-sign with fresh Stellar sequence; pre-runtime-147 dropped) +// - RefundTransactionExpired → create_refund_transaction_or_add_sig (offline-validator recovery) +// +// Note: RefundTransactionCreated is intentionally NOT handled here. +// In a multi-validator setup every online validator monitors the Stellar cursor independently +// and calls proposeRefundDirect as soon as it detects a bad deposit — before +// RefundTransactionCreated is even emitted on TFChain. By the time this event fires, the +// proposing validator has already signed. Processing it here would only ever help a validator +// that somehow missed its entire Stellar cursor history, which is not a realistic scenario. +// If "catch-up" mode for validators joining mid-stream without Stellar history is ever needed, +// RefundTransactionCreated handling should be re-added here. +// +// Ready events (BurnTransactionReady, RefundTransactionReady) are NOT handled here — they +// involve actual Stellar submissions, not TFChain extrinsics, and remain sequential. +// Deposit-triggered refunds (mint.go → refund()) are also NOT batched here; they call +// proposeRefundDirect directly so that all validators propose at the same time with a +// consistent Stellar sequence number. +func (bridge *Bridge) handleProposalsBatch( + ctx context.Context, + withdrawCreated []subpkg.WithdrawCreatedEvent, + withdrawExpired []subpkg.WithdrawExpiredEvent, + refundExpired []subpkg.RefundTransactionExpiredEvent, +) error { + // Step 1: Convert BurnTransactionExpired (≥runtime-147) to WithdrawCreatedEvent. + // Pre-runtime-147 events (no source address) are no longer supported and are dropped. + for _, e := range withdrawExpired { + ok, source := e.Source.Unwrap() + if !ok { + log.Warn(). + Str("event_action", "withdraw_skipped"). + Str("event_kind", "alert"). + Str("category", "withdraw"). + Uint64("tx_id", e.ID). + Msg("ignoring pre-runtime-147 expired withdraw (no source address); network should have no such transfers") + continue + } + withdrawCreated = append(withdrawCreated, subpkg.WithdrawCreatedEvent{ + ID: e.ID, + Source: source, + Target: e.Target, + Amount: e.Amount, + }) + } - burned, err := bridge.subClient.IsBurnedAlready(types.U64(withdraw.ID)) - if err != nil { - return err + // Step 2: Normalise refund expired events. + type refundItem struct { + Hash string + Target string + Amount uint64 + } + var allRefunds []refundItem + for _, e := range refundExpired { + allRefunds = append(allRefunds, refundItem{e.Hash, e.Target, e.Amount}) } - if burned { - logger.Info(). - Str("event_action", "withdraw_skipped"). - Str("event_kind", "event"). - Str("category", "withdraw"). - Msg("the withdraw transaction has already been processed") - return pkg.ErrTransactionAlreadyBurned + // Step 3: Early return if nothing to do. + if len(withdrawCreated) == 0 && len(allRefunds) == 0 { + return nil } - logger.Info(). - Str("event_action", "transfer_initiated"). + log.Info(). + Str("event_action", "batch_proposal_started"). Str("event_kind", "event"). - Str("category", "transfer"). - Dict("metadata", zerolog.Dict(). - Str("type", "burn")). - Msg("a transfer has initiated") - - // check if it can hold tft : TODO check trust line TFT limit if it can receive the amount - if err := bridge.wallet.CheckAccount(withdraw.Target); err != nil { - ctx = _logger.WithRefundReason(ctx, err.Error()) - return bridge.handleBadWithdraw(ctx, withdraw) - } + Str("category", "bridge"). + Int("withdraws", len(withdrawCreated)). + Int("refunds", len(allRefunds)). + Msg("batch processing proposal events") - signature, sequenceNumber, err := bridge.wallet.CreatePaymentAndReturnSignature(ctx, withdraw.Target, withdraw.Amount, withdraw.ID) - if err != nil { + // Step 4: Sync the Stellar sequence counter ONCE before signing anything. + // All proposals in this batch get consecutive sequence numbers from this base. + // Syncing once (rather than per-proposal) is critical: proposals are TFChain + // extrinsics, not Stellar submissions — the Stellar account sequence does not + // advance between signing calls, so all signers of a given proposal must use + // the same sequence. A fresh sync here ensures we start from the current live + // account sequence, not a value that may have been advanced by a prior Ready event. + if err := bridge.wallet.SyncSequenceNumber(); err != nil { return err } - log.Debug().Msgf("stellar account sequence number: %d", sequenceNumber) - err = bridge.subClient.RetryProposeWithdrawOrAddSig(ctx, withdraw.ID, withdraw.Target, big.NewInt(int64(withdraw.Amount)), signature, bridge.wallet.GetKeypair().Address(), sequenceNumber) - if err != nil { - return nil + // Step 5: Build burn proposals. + var burnProposals []subpkg.BurnProposal + // Track which WithdrawCreatedEvent each proposal came from (for logging). + type burnMeta struct { + event subpkg.WithdrawCreatedEvent + index int // index into burnProposals } + var burnMetas []burnMeta - logger.Info(). - Str("event_action", "withdraw_proposed"). - Str("event_kind", "event"). - Str("category", "withdraw"). - Dict("metadata", zerolog.Dict(). - Uint64("amount", withdraw.Amount). - Str("tx_id", fmt.Sprint(withdraw.ID)). - Str("to", withdraw.Target)). - Msgf("a withdraw has proposed with the target stellar address of %s", withdraw.Target) - return nil -} - -func (bridge *Bridge) handleWithdrawExpired(ctx context.Context, withdrawExpired subpkg.WithdrawExpiredEvent) error { - logger := log.Logger.With().Str("trace_id", fmt.Sprint(withdrawExpired.ID)).Logger() + for _, w := range withdrawCreated { + logger := log.Logger.With().Str("trace_id", fmt.Sprint(w.ID)).Logger() - ok, source := withdrawExpired.Source.Unwrap() // transfers from the previous runtime before 147 has no source address + burned, err := bridge.subClient.IsBurnedAlready(types.U64(w.ID)) + if err != nil { + return err + } + if burned { + logger.Info(). + Str("event_action", "withdraw_skipped"). + Str("event_kind", "event"). + Str("category", "withdraw"). + Msg("the withdraw transaction has already been processed") + continue + } - if !ok { - // This path is intended solely for processing transfers that lack a source address - // and should be retained until the network has been verified to have no transfers from the previous runtime before 147. + if err := bridge.wallet.CheckAccount(w.Target); err != nil { + ctx := _logger.WithRefundReason(ctx, err.Error()) + if err := bridge.handleBadWithdraw(ctx, w); err != nil { + if errors.Is(err, pkg.ErrTransactionAlreadyMinted) { + continue + } + return err + } + continue + } - if err := bridge.wallet.CheckAccount(withdrawExpired.Target); err != nil { - logger.Warn(). - Str("event_action", "transfer_failed"). - Str("event_kind", "alert"). - Str("category", "transfer"). - Dict("metadata", zerolog.Dict(). - Str("reason", err.Error())). - Str("type", "burn"). - Msg("a withdraw failed with no way to refund!") - return bridge.subClient.RetrySetWithdrawExecuted(ctx, withdrawExpired.ID) + sig, seqNum, err := bridge.wallet.CreatePaymentAndReturnSignature(ctx, w.Target, w.Amount, w.ID) + if err != nil { + logger.Warn().Err(err).Msg("failed to create Stellar signature for withdraw proposal, skipping") + continue } - signature, sequenceNumber, err := bridge.wallet.CreatePaymentAndReturnSignature(ctx, withdrawExpired.Target, withdrawExpired.Amount, withdrawExpired.ID) + burnMetas = append(burnMetas, burnMeta{event: w, index: len(burnProposals)}) + burnProposals = append(burnProposals, subpkg.BurnProposal{ + TxID: w.ID, + Target: w.Target, + Amount: new(big.Int).SetUint64(w.Amount), + Signature: sig, + StellarAddress: bridge.wallet.GetKeypair().Address(), + SequenceNumber: seqNum, + }) + } + + // Step 6: Build refund proposals. + var refundProposals []subpkg.RefundProposal + type refundMeta struct { + item refundItem + index int // index into refundProposals (offset by len(burnProposals) in the batch) + } + var refundMetas []refundMeta + + for _, r := range allRefunds { + logger := log.Logger.With().Str("trace_id", r.Hash).Logger() + + refunded, err := bridge.subClient.IsRefundedAlready(r.Hash) if err != nil { return err } - log.Debug().Msgf("stellar account sequence number: %d", sequenceNumber) + if refunded { + logger.Info(). + Str("event_action", "refund_skipped"). + Str("event_kind", "event"). + Str("category", "refund"). + Msg("the transaction has already been refunded") + continue + } - err = bridge.subClient.RetryProposeWithdrawOrAddSig(ctx, withdrawExpired.ID, withdrawExpired.Target, big.NewInt(int64(withdrawExpired.Amount)), signature, bridge.wallet.GetKeypair().Address(), sequenceNumber) + sig, seqNum, err := bridge.wallet.CreateRefundAndReturnSignature(ctx, r.Target, r.Amount, r.Hash) if err != nil { - return err + logger.Warn().Err(err).Msg("failed to create Stellar signature for refund proposal, skipping") + continue } - logger.Info(). - Str("event_action", "transfer_initiated"). - Str("event_kind", "event"). - Str("category", "transfer"). - Dict("metadata", zerolog.Dict(). - Str("type", "burn")). - Msg("a transfer has initiated") - logger.Info(). + + refundMetas = append(refundMetas, refundMeta{item: r, index: len(refundProposals)}) + refundProposals = append(refundProposals, subpkg.RefundProposal{ + TxHash: r.Hash, + Target: r.Target, + Amount: int64(r.Amount), + Signature: sig, + StellarAddress: bridge.wallet.GetKeypair().Address(), + SequenceNumber: seqNum, + }) + } + + // Step 7: Submit unified force_batch. + if len(burnProposals) == 0 && len(refundProposals) == 0 { + return nil + } + + result, err := bridge.subClient.BatchProposeAll(ctx, burnProposals, refundProposals) + if err != nil { + // Wholesale RPC failure — sequential fallback would also fail. + // BurnTransactionExpired / RefundTransactionExpired will re-emit and retry. + log.Warn().Err(err).Msg("force_batch proposal failed; proposals will be retried via expiry events") + return nil + } + + // Step 8: Log per-proposal outcomes. + // Calls are ordered: burns[0..N-1], refunds[N..N+M-1]. + // FailedIndexes is populated only for BatchInterrupted (older runtimes that use + // Utility.batch); with force_batch we get ItemFailed events without call indices, + // so FailedIndexes will be empty and we fall back to the FailedCount flag. + failedSet := make(map[int]bool, len(result.FailedIndexes)) + for _, idx := range result.FailedIndexes { + failedSet[idx] = true + } + batchHadFailures := result.FailedCount > 0 + + if batchHadFailures { + log.Warn(). + Int("failed", result.FailedCount). + Int("total", len(burnProposals)+len(refundProposals)). + Msg("some proposals failed within batch (may already be signed or expired)") + } + + for _, m := range burnMetas { + if failedSet[m.index] { + log.Warn(). + Str("event_action", "withdraw_proposal_failed"). + Str("event_kind", "alert"). + Str("category", "withdraw"). + Uint64("tx_id", m.event.ID). + Msg("withdraw proposal failed within batch") + continue + } + log.Info(). + Str("trace_id", fmt.Sprint(m.event.ID)). Str("event_action", "withdraw_proposed"). Str("event_kind", "event"). Str("category", "withdraw"). + Bool("batch_had_failures", batchHadFailures && len(result.FailedIndexes) == 0). Dict("metadata", zerolog.Dict(). - Uint64("amount", withdrawExpired.Amount). - Str("tx_id", fmt.Sprint(withdrawExpired.ID)). - Str("to", withdrawExpired.Target)). - Msgf("a withdraw has proposed with the target stellar address of %s", withdrawExpired.Target) - return nil + Uint64("amount", m.event.Amount). + Str("tx_id", fmt.Sprint(m.event.ID)). + Str("to", m.event.Target)). + Msgf("a withdraw has proposed with the target stellar address of %s", m.event.Target) } - // refundable path (starting from tfchain runtime 147) - return bridge.handleWithdrawCreated(ctx, subpkg.WithdrawCreatedEvent{ - ID: withdrawExpired.ID, - Source: source, - Target: withdrawExpired.Target, - Amount: withdrawExpired.Amount, - }) + // Refund indices in the batch are offset by the number of burn proposals. + burnOffset := len(burnProposals) + for _, m := range refundMetas { + batchIdx := burnOffset + m.index + if failedSet[batchIdx] { + log.Warn(). + Str("event_action", "refund_proposal_failed"). + Str("event_kind", "alert"). + Str("category", "refund"). + Str("tx_hash", m.item.Hash). + Msg("refund proposal failed within batch") + continue + } + log.Info(). + Str("trace_id", m.item.Hash). + Str("event_action", "refund_proposed"). + Str("event_kind", "event"). + Str("category", "refund"). + Bool("batch_had_failures", batchHadFailures && len(result.FailedIndexes) == 0). + Dict("metadata", zerolog.Dict(). + Uint64("amount", m.item.Amount). + Str("tx_hash", m.item.Hash). + Str("to", m.item.Target)). + Msgf("a refund has proposed for target stellar address %s", m.item.Target) + } + + log.Info(). + Str("event_action", "batch_proposal_completed"). + Str("event_kind", "event"). + Str("category", "bridge"). + Int("total", len(burnProposals)+len(refundProposals)). + Int("succeeded", result.SuccessCount). + Int("failed", result.FailedCount). + Msg("batch proposal completed") + + return nil } -func (bridge *Bridge) handleWithdrawReady(ctx context.Context, withdrawReady subpkg.WithdrawReadyEvent) error { +func (bridge *Bridge) handleWithdrawReady(ctx context.Context, withdrawReady subpkg.WithdrawReadyEvent) (uint64, error) { logger := log.Logger.With().Str("trace_id", fmt.Sprint(withdrawReady.ID)).Logger() - // ctx_with_trace_id := context.WithValue(ctx, "trace_id", fmt.Sprint(withdrawReady.ID)) - burned, err := bridge.subClient.IsBurnedAlready(types.U64(withdrawReady.ID)) + txID := withdrawReady.ID + txKey := fmt.Sprint(txID) + + // 1. Check idempotency store + state, err := bridge.idempotency.GetWithdrawState(txID) if err != nil { - return err + return 0, err + } + if state == pkg.TxStateCompleted { + logger.Info(). + Str("event_action", "withdraw_skipped"). + Str("event_kind", "event"). + Str("category", "withdraw"). + Msg("idempotency: withdraw already completed, skipping") + return 0, pkg.ErrTransactionAlreadyBurned + } + + // 2. If PROCESSING, check if Stellar tx was already submitted (crash recovery) + if state == pkg.TxStateProcessing { + logger.Warn(). + Str("event_action", "withdraw_crash_recovery"). + Str("event_kind", "event"). + Str("category", "withdraw"). + Msg("idempotency: withdraw in PROCESSING state (possible crash recovery)") + + // Fetch the outgoing transactions page once and reuse it for both lookups. + // Non-fatal on error: match the reconciler's behavior. Leave tx as PROCESSING; + // the next BurnTransactionReady event will retry the Horizon lookup. + // Returning an error here would crash the bridge on every PROCESSING event + // during a Horizon outage, which is worse than gracefully skipping. + outgoingPage, err := bridge.wallet.FetchOutgoingTransactionsPage(ctx) + if err != nil { + logger.Warn().Err(err).Uint64("tx_id", txID). + Msg("failed to fetch Horizon transactions for PROCESSING check; will retry on next event") + return 0, nil + } + + // Primary check: look for a tx with matching memo (current bridge behaviour) + if stellarTx := bridge.wallet.FindPaymentByMemoInPage(outgoingPage, txKey); stellarTx != nil { + logger.Info(). + Str("event_action", "withdraw_recovered"). + Str("event_kind", "event"). + Str("category", "withdraw"). + Msg("idempotency: found existing Stellar tx by memo, deferring TFChain confirmation to batch") + return txID, nil + } + + // Fallback: look for a tx by sequence number, covering pre-upgrade submissions + // that were made without a memo. The sequence number stored in the TFChain burn tx + // is the exact sequence used when building the Stellar tx, uniquely identifying it. + // Since the bridge is stopped during upgrades, no new outgoing txs can appear + // between the old submission and this lookup, so 200 records is always sufficient. + // NOTE: adding memo to Stellar txs is a breaking change — all validators must be + // upgraded together. A mixed-version cluster will produce invalid signature sets. + burnTxForSeq, err := bridge.subClient.GetBurnTransaction(types.U64(txID)) + if err != nil { + return 0, err + } + if stellarTxBySeq := bridge.wallet.FindPaymentBySequenceInPage(outgoingPage, int64(burnTxForSeq.SequenceNumber)); stellarTxBySeq != nil { + logger.Info(). + Str("event_action", "withdraw_recovered"). + Str("event_kind", "event"). + Str("category", "withdraw"). + Int64("sequence_number", int64(burnTxForSeq.SequenceNumber)). + Msg("idempotency: found pre-upgrade Stellar tx by sequence number (no memo), deferring TFChain confirmation to batch") + return txID, nil + } + + logger.Info().Msg("idempotency: no Stellar tx found by memo or sequence, safe to retry") } + // 3. Check TFChain: already burned? + burned, err := bridge.subClient.IsBurnedAlready(types.U64(txID)) + if err != nil { + return 0, err + } if burned { + _ = bridge.idempotency.MarkWithdrawCompleted(txID) logger.Info(). Str("event_action", "withdraw_skipped"). Str("event_kind", "event"). Str("category", "withdraw"). Msg("the withdraw transaction has already been processed") - return pkg.ErrTransactionAlreadyBurned + return 0, pkg.ErrTransactionAlreadyBurned } - burnTx, err := bridge.subClient.GetBurnTransaction(types.U64(withdrawReady.ID)) + // 4. Get burn tx with signatures + burnTx, err := bridge.subClient.GetBurnTransaction(types.U64(txID)) if err != nil { - return err + return 0, err } - if len(burnTx.Signatures) == 0 { logger.Info(). Str("event_action", "withdraw_postponed"). Str("event_kind", "event"). Str("category", "withdraw"). Msg("the withdraw has been postponed due to the transaction signatures being removed on the TFChain side while the bridge was processing the transaction") - return nil + return 0, nil + } + + // 5. Mark PROCESSING before Stellar submit + if err := bridge.idempotency.MarkWithdrawProcessing(txID); err != nil { + return 0, err } - // todo add memo hash - err = bridge.wallet.CreatePaymentWithSignaturesAndSubmit(ctx, burnTx.Target, uint64(burnTx.Amount), fmt.Sprint(withdrawReady.ID), burnTx.Signatures, int64(burnTx.SequenceNumber)) + // 6. Submit to Stellar + err = bridge.wallet.CreatePaymentWithSignaturesAndSubmit(ctx, burnTx.Target, uint64(burnTx.Amount), txKey, burnTx.Signatures, int64(burnTx.SequenceNumber)) if err != nil { - // we can log and skip here as we could depend on tfcahin retry mechanism - // to notify us again about related burn tx logger.Info(). Str("event_action", "withdraw_postponed"). Str("event_kind", "event"). @@ -171,22 +400,13 @@ func (bridge *Bridge) handleWithdrawReady(ctx context.Context, withdrawReady sub Dict("metadata", zerolog.Dict(). Str("reason", err.Error())). Msgf("the withdraw has been postponed due to a problem in sending this transaction to the stellar network. error was %s", err.Error()) - return nil + return 0, nil // leave as PROCESSING, will reconcile on next attempt } - logger.Info(). - Str("event_action", "withdraw_completed"). - Str("event_kind", "event"). - Str("category", "withdraw"). - Msg("the withdraw has proceed") - logger.Info(). - Str("event_action", "transfer_completed"). - Str("event_kind", "event"). - Str("category", "transfer"). - Dict("metadata", zerolog.Dict(). - Str("outcome", "bridged")). - Msg("the transfer has completed") - return bridge.subClient.RetrySetWithdrawExecuted(ctx, withdrawReady.ID) + // 7. Stellar payment submitted — return txID for batch TFChain confirmation. + // The caller (bridge.go) collects all returned txIDs and submits them as a + // single Utility.force_batch extrinsic, confirming all burns in one block. + return txID, nil } func (bridge *Bridge) handleBadWithdraw(ctx context.Context, withdraw subpkg.WithdrawCreatedEvent) error { diff --git a/bridge/tfchain_bridge/pkg/idempotency.go b/bridge/tfchain_bridge/pkg/idempotency.go new file mode 100644 index 000000000..6d9fa9b7c --- /dev/null +++ b/bridge/tfchain_bridge/pkg/idempotency.go @@ -0,0 +1,177 @@ +package pkg + +import ( + "encoding/json" + "fmt" + "strconv" + + bolt "go.etcd.io/bbolt" +) + +// TxState represents the processing state of a bridge transaction. +type TxState string + +const ( + // TxStateProcessing means the Stellar transaction has been (or is being) submitted, + // but TFChain confirmation has not yet been recorded. + TxStateProcessing TxState = "PROCESSING" + // TxStateCompleted means both Stellar submission and TFChain confirmation are done. + TxStateCompleted TxState = "COMPLETED" +) + +var ( + bucketWithdraw = []byte("withdraw") + bucketRefund = []byte("refund") +) + +// IdempotencyStore provides crash-safe tracking of transaction processing state +// using a bbolt (BoltDB) embedded database. It prevents double Stellar submissions +// when the bridge crashes between Stellar tx submit and TFChain confirmation. +// IdempotencyStore is a bbolt-backed persistent store for transaction states. +// Keys are never deleted — COMPLETED entries accumulate over time. At ~50 bytes +// per entry, growth is negligible even at high transaction volumes (e.g. 1000 +// txs/day → ~18 MB/year). Pruning is intentionally omitted for simplicity and +// auditability. +type IdempotencyStore struct { + db *bolt.DB +} + +// NewIdempotencyStore opens or creates the bbolt database at the given path. +func NewIdempotencyStore(path string) (*IdempotencyStore, error) { + db, err := bolt.Open(path, 0600, nil) + if err != nil { + return nil, fmt.Errorf("failed to open idempotency store at %s: %w", path, err) + } + + err = db.Update(func(tx *bolt.Tx) error { + if _, err := tx.CreateBucketIfNotExists(bucketWithdraw); err != nil { + return err + } + _, err := tx.CreateBucketIfNotExists(bucketRefund) + return err + }) + if err != nil { + db.Close() + return nil, err + } + + return &IdempotencyStore{db: db}, nil +} + +// MarkWithdrawProcessing records that a withdraw is about to be submitted to Stellar. +func (s *IdempotencyStore) MarkWithdrawProcessing(txID uint64) error { + return s.setState(bucketWithdraw, strconv.FormatUint(txID, 10), TxStateProcessing) +} + +// MarkWithdrawCompleted records that a withdraw has been fully processed +// (Stellar tx submitted AND TFChain confirmation recorded). +func (s *IdempotencyStore) MarkWithdrawCompleted(txID uint64) error { + return s.setState(bucketWithdraw, strconv.FormatUint(txID, 10), TxStateCompleted) +} + +// GetWithdrawState returns the current state of a withdraw transaction. +// Returns empty string if the transaction has never been tracked. +func (s *IdempotencyStore) GetWithdrawState(txID uint64) (TxState, error) { + return s.getState(bucketWithdraw, strconv.FormatUint(txID, 10)) +} + +// GetPendingWithdraws returns all withdraw transaction IDs that are in PROCESSING state. +// These are candidates for crash recovery reconciliation. +func (s *IdempotencyStore) GetPendingWithdraws() ([]uint64, error) { + var pending []uint64 + err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket(bucketWithdraw) + return b.ForEach(func(k, v []byte) error { + var state TxState + if err := json.Unmarshal(v, &state); err != nil { + return nil // skip corrupted entries + } + if state == TxStateProcessing { + id, err := strconv.ParseUint(string(k), 10, 64) + if err != nil { + return nil // skip non-numeric keys + } + pending = append(pending, id) + } + return nil + }) + }) + return pending, err +} + +// MarkRefundProcessing records that a refund is about to be submitted to Stellar. +func (s *IdempotencyStore) MarkRefundProcessing(txHash string) error { + return s.setState(bucketRefund, txHash, TxStateProcessing) +} + +// MarkRefundCompleted records that a refund has been fully processed. +func (s *IdempotencyStore) MarkRefundCompleted(txHash string) error { + return s.setState(bucketRefund, txHash, TxStateCompleted) +} + +// GetRefundState returns the current state of a refund transaction. +func (s *IdempotencyStore) GetRefundState(txHash string) (TxState, error) { + return s.getState(bucketRefund, txHash) +} + +// GetPendingRefunds returns all refund transaction hashes that are in PROCESSING state. +func (s *IdempotencyStore) GetPendingRefunds() ([]string, error) { + var pending []string + err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket(bucketRefund) + return b.ForEach(func(k, v []byte) error { + var state TxState + if err := json.Unmarshal(v, &state); err != nil { + return nil + } + if state == TxStateProcessing { + pending = append(pending, string(k)) + } + return nil + }) + }) + return pending, err +} + +// Close closes the underlying bbolt database. +func (s *IdempotencyStore) Close() error { + return s.db.Close() +} + +func (s *IdempotencyStore) setState(bucket []byte, key string, state TxState) error { + return s.db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket(bucket) + + // Guard against downgrading a COMPLETED entry back to PROCESSING. + // This should never happen via normal code paths (callers check state first), + // but we enforce it at the store level as a safety net. + if state == TxStateProcessing { + existing := b.Get([]byte(key)) + if existing != nil { + var cur TxState + if err := json.Unmarshal(existing, &cur); err == nil && cur == TxStateCompleted { + return fmt.Errorf("refusing to downgrade completed tx %q to PROCESSING", key) + } + } + } + + val, err := json.Marshal(state) + if err != nil { + return err + } + return b.Put([]byte(key), val) + }) +} + +func (s *IdempotencyStore) getState(bucket []byte, key string) (TxState, error) { + var state TxState + err := s.db.View(func(tx *bolt.Tx) error { + b := tx.Bucket(bucket) + val := b.Get([]byte(key)) + if val == nil { + return nil // state remains zero value (empty string) + } + return json.Unmarshal(val, &state) + }) + return state, err +} diff --git a/bridge/tfchain_bridge/pkg/stellar/stellar.go b/bridge/tfchain_bridge/pkg/stellar/stellar.go index 94f55e616..a16b47882 100644 --- a/bridge/tfchain_bridge/pkg/stellar/stellar.go +++ b/bridge/tfchain_bridge/pkg/stellar/stellar.go @@ -4,7 +4,9 @@ import ( "context" "encoding/base64" "encoding/hex" + "encoding/json" "fmt" + "io" "math/big" "net/http" "strconv" @@ -42,6 +44,12 @@ type StellarWallet struct { config *pkg.StellarConfig signatureCount int sequenceNumber int64 + // resolvedAsset caches the TFT asset code and issuer actually held by the + // bridge wallet. Normally matches the network-configured default, but may + // differ in test/dev environments that use a custom issuer. + resolvedAssetCode string + resolvedAssetIssuer string + httpClient *http.Client } type TraceIdKey struct{} @@ -58,6 +66,24 @@ func NewStellarWallet(ctx context.Context, config *pkg.StellarConfig) (*StellarW config: config, } + retryClient := retryablehttp.NewClient() + retryClient.RetryMax = 3 + retryClient.RetryWaitMin = 2 * time.Second + retryClient.RetryWaitMax = 5 * time.Second + retryClient.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) { + if ctx.Err() != nil { + return false, ctx.Err() + } + if err != nil { + return true, nil + } + if resp.StatusCode == 429 || (resp.StatusCode >= 500 && resp.StatusCode <= 599) { + return true, nil + } + return false, nil + } + w.httpClient = retryClient.StandardClient() + account, err := w.getAccountDetails(config.StellarBridgeAccount) if err != nil { return nil, err @@ -77,6 +103,29 @@ func NewStellarWallet(ctx context.Context, config *pkg.StellarConfig) (*StellarW } log.Info().Msgf("account %s loaded with sequence number %d", account.AccountID, w.sequenceNumber) + // Discover the TFT asset actually held by the bridge wallet. + // On production and testnet, the issuer is always the hardcoded constant. + // On local dev (--network local), resolve from the wallet's actual balance + // to support custom TFT issuers created per dev session. + configuredAsset := w.getAssetCodeAndIssuer() + w.resolvedAssetCode = configuredAsset[0] + w.resolvedAssetIssuer = configuredAsset[1] + if w.config.StellarNetwork == "local" { + for _, balance := range account.Balances { + if balance.Code == "TFT" { + if balance.Issuer != w.resolvedAssetIssuer { + log.Warn(). + Str("configured_issuer", w.resolvedAssetIssuer). + Str("actual_issuer", balance.Issuer). + Msg("local dev: bridge wallet holds TFT from a different issuer than the network default; using actual issuer for all payments") + w.resolvedAssetIssuer = balance.Issuer + } + break + } + } + } + log.Info().Str("asset_code", w.resolvedAssetCode).Str("asset_issuer", w.resolvedAssetIssuer).Msg("bridge wallet TFT asset resolved") + return w, nil } @@ -86,6 +135,12 @@ func (w *StellarWallet) CreatePaymentAndReturnSignature(ctx context.Context, tar return "", 0, err } + // Include the burn tx ID as a text memo so the signed transaction hash matches + // the one that will be submitted (with the same memo) in CreatePaymentWithSignaturesAndSubmit. + // This is required for crash recovery: FindPaymentByMemo can locate the submitted + // Stellar tx by memo to avoid double-submission. + txnBuild.Memo = txnbuild.MemoText(fmt.Sprint(txID)) + txn, err := w.createTransaction(ctx, txnBuild, true) if err != nil { return "", 0, err @@ -104,6 +159,10 @@ func (w *StellarWallet) CreatePaymentWithSignaturesAndSubmit(ctx context.Context return err } + // Set text memo to the burn transaction ID so crash recovery can identify + // this Stellar tx on Horizon via FindPaymentByMemo + txnBuild.Memo = txnbuild.MemoText(txHash) + txn, err := w.createTransaction(ctx, txnBuild, false) if err != nil { return err @@ -189,6 +248,146 @@ func (w *StellarWallet) CreateRefundAndReturnSignature(ctx context.Context, targ return base64.StdEncoding.EncodeToString(signatures[0].Signature), uint64(txn.SequenceNumber()), nil } +// fetchOutgoingTransactions queries Horizon's /transactions?source_account= endpoint +// which filters server-side to only transactions where the bridge account is the source. +// This guarantees the limit covers that many actual outgoing (withdraw/refund) transactions, +// regardless of how many incoming deposit transactions exist on the account. +// It reuses the Horizon client's HTTP transport (which has timeouts configured) and +// validates the HTTP status code before decoding to avoid misreporting Horizon errors +// as "no transaction found". +func (w *StellarWallet) fetchOutgoingTransactions(ctx context.Context, limit uint) (hProtocol.TransactionsPage, error) { + client, err := w.getHorizonClient() + if err != nil { + return hProtocol.TransactionsPage{}, errors.Wrap(err, "failed to get horizon client") + } + + reqURL := fmt.Sprintf("%stransactions?source_account=%s&order=desc&limit=%d", + strings.TrimRight(client.HorizonURL, "/")+"/", + w.config.StellarBridgeAccount, + limit, + ) + + httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil) + if err != nil { + return hProtocol.TransactionsPage{}, errors.Wrap(err, "failed to build horizon request") + } + + // Reuse the Horizon client's HTTP transport which has timeouts configured + httpResp, err := client.HTTP.Do(httpReq) + if err != nil { + return hProtocol.TransactionsPage{}, errors.Wrap(err, "failed to execute horizon request") + } + defer httpResp.Body.Close() + + // Validate status before decoding — a non-200 response would decode as an empty + // TransactionsPage and cause crash recovery to falsely conclude "no tx found" + if httpResp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(io.LimitReader(httpResp.Body, 1024)) + return hProtocol.TransactionsPage{}, fmt.Errorf("horizon returned HTTP %d: %s", httpResp.StatusCode, strings.TrimSpace(string(body))) + } + + // Limit body size to protect against unexpectedly large responses + var page hProtocol.TransactionsPage + if err := json.NewDecoder(io.LimitReader(httpResp.Body, 4*1024*1024)).Decode(&page); err != nil { + return hProtocol.TransactionsPage{}, errors.Wrap(err, "failed to decode horizon response") + } + + return page, nil +} + +// FetchOutgoingTransactionsPage fetches the 200 most recent outgoing transactions +// from the bridge account in a single Horizon request. Callers that need multiple +// lookups (memo + sequence) should fetch once and use the page-based helpers below +// to avoid redundant HTTP round-trips. +func (w *StellarWallet) FetchOutgoingTransactionsPage(ctx context.Context) (hProtocol.TransactionsPage, error) { + page, err := w.fetchOutgoingTransactions(ctx, 200) + if err != nil { + return hProtocol.TransactionsPage{}, errors.Wrap(err, "failed to fetch outgoing transactions from Horizon") + } + return page, nil +} + +// FindPaymentByMemoInPage scans a pre-fetched transactions page for a text memo match. +// Use this when you already have a page from FetchOutgoingTransactionsPage to avoid +// redundant Horizon API calls. +func (w *StellarWallet) FindPaymentByMemoInPage(page hProtocol.TransactionsPage, memo string) *hProtocol.Transaction { + for _, tx := range page.Embedded.Records { + if tx.MemoType == "text" && tx.Memo == memo { + txCopy := tx + return &txCopy + } + } + return nil +} + +// FindRefundByReturnHashInPage scans a pre-fetched transactions page for a MemoReturn hash match. +// Horizon encodes MemoReturn as base64 in its JSON API, while txHash from TFChain is hex-encoded. +// This function decodes the hex hash to raw bytes and re-encodes as base64 before comparing. +func (w *StellarWallet) FindRefundByReturnHashInPage(page hProtocol.TransactionsPage, txHash string) *hProtocol.Transaction { + hashBytes, err := hex.DecodeString(txHash) + if err != nil { + log.Warn().Err(err).Str("tx_hash", txHash).Msg("failed to hex-decode refund tx hash for memo comparison") + return nil + } + hashBase64 := base64.StdEncoding.EncodeToString(hashBytes) + + for _, tx := range page.Embedded.Records { + if tx.MemoType == "return" && tx.Memo == hashBase64 { + txCopy := tx + return &txCopy + } + } + return nil +} + +// FindPaymentBySequenceInPage scans a pre-fetched transactions page for a source account +// sequence number match. Used as a fallback for pre-upgrade txs submitted without a memo. +func (w *StellarWallet) FindPaymentBySequenceInPage(page hProtocol.TransactionsPage, sequenceNumber int64) *hProtocol.Transaction { + seqStr := strconv.FormatInt(sequenceNumber, 10) + for _, tx := range page.Embedded.Records { + if tx.AccountSequence == seqStr { + txCopy := tx + return &txCopy + } + } + return nil +} + +// FindPaymentByMemo fetches outgoing transactions and searches for a text memo match. +// For callers that only need a single lookup; use FetchOutgoingTransactionsPage + +// FindPaymentByMemoInPage when multiple lookups are needed. +func (w *StellarWallet) FindPaymentByMemo(ctx context.Context, memo string) (*hProtocol.Transaction, error) { + page, err := w.FetchOutgoingTransactionsPage(ctx) + if err != nil { + return nil, err + } + return w.FindPaymentByMemoInPage(page, memo), nil +} + +// FindRefundByReturnHash fetches outgoing transactions and searches for a MemoReturn hash match. +func (w *StellarWallet) FindRefundByReturnHash(ctx context.Context, txHash string) (*hProtocol.Transaction, error) { + page, err := w.FetchOutgoingTransactionsPage(ctx) + if err != nil { + return nil, err + } + return w.FindRefundByReturnHashInPage(page, txHash), nil +} + +// FindPaymentBySequence fetches outgoing transactions and searches by source account sequence. +// This is used as a fallback during crash recovery when the transaction was submitted +// by an older bridge version that did not include a memo (pre-upgrade compatibility). +// Since the bridge is stopped during upgrades, it cannot submit any outgoing transactions +// while down, so the target tx is guaranteed to be within the 200 most recent records. +// The sequence number stored in the TFChain burn tx is exactly the sequence used when +// building the Stellar tx, making it a reliable unique identifier regardless of memo presence. +func (w *StellarWallet) FindPaymentBySequence(ctx context.Context, sequenceNumber int64) (*hProtocol.Transaction, error) { + page, err := w.FetchOutgoingTransactionsPage(ctx) + if err != nil { + return nil, err + } + return w.FindPaymentBySequenceInPage(page, sequenceNumber), nil +} + func (w *StellarWallet) CheckAccount(account string) error { acc, err := w.getAccountDetails(account) if err != nil { @@ -298,7 +497,7 @@ func (w *StellarWallet) submitTransaction(ctx context.Context, txn *txnbuild.Tra return errors.Wrap(err, "an error occurred while submitting the transaction") } log.Info(). - Str("trace_id", fmt.Sprint(ctx.Value("trace_id"))). + Str("trace_id", fmt.Sprint(ctx.Value(TraceIdKey{}))). Str("event_action", "stellar_transaction_submitted"). Str("event_kind", "event"). Str("category", "vault"). @@ -339,6 +538,26 @@ type MintEvent struct { } // getAccountDetails gets account details based an a Stellar address +// SyncSequenceNumber resets the internal sequence counter to the current Stellar account +// sequence. Call this at the start of each Created event handler (before signing any new +// transactions) to ensure the counter is not stale from a preceding Ready event handler, +// which sets w.sequenceNumber to a historical value when submitting a stored Stellar tx. +// For a batch of N proposals, call this once — subsequent generatePaymentOperation(0) calls +// will increment normally, producing unique consecutive sequences. +func (w *StellarWallet) SyncSequenceNumber() error { + acc, err := w.getAccountDetails(w.config.StellarBridgeAccount) + if err != nil { + return errors.Wrap(err, "failed to get bridge account details for sequence sync") + } + seq, err := acc.GetSequenceNumber() + if err != nil { + return errors.Wrap(err, "failed to parse account sequence number") + } + w.sequenceNumber = seq + log.Debug().Int64("sequence", w.sequenceNumber).Msg("synced Stellar sequence number from account") + return nil +} + func (w *StellarWallet) getAccountDetails(address string) (account hProtocol.Account, err error) { client, err := w.getHorizonClient() if err != nil { @@ -442,7 +661,7 @@ func (w *StellarWallet) processTransaction(tx hProtocol.Transaction) ([]MintEven } creditedEffect := effect.(horizoneffects.AccountCredited) - if creditedEffect.Code != asset[0] && creditedEffect.Issuer != asset[1] { + if creditedEffect.Code != asset[0] || creditedEffect.Issuer != asset[1] { continue } @@ -454,7 +673,7 @@ func (w *StellarWallet) processTransaction(tx hProtocol.Transaction) ([]MintEven senders := make(map[string]*big.Int) for _, op := range ops.Embedded.Records { if op.GetType() != "payment" { - return nil, nil + continue } PaymentOperation := op.(operations.Payment) @@ -462,6 +681,20 @@ func (w *StellarWallet) processTransaction(tx hProtocol.Transaction) ([]MintEven continue } + // Reject non-TFT payments — log as suspicious + if PaymentOperation.Code != asset[0] || PaymentOperation.Issuer != asset[1] { + logger.Warn(). + Str("event_action", "non_tft_payment_rejected"). + Str("event_kind", "alert"). + Str("from", PaymentOperation.From). + Str("asset_code", PaymentOperation.Code). + Str("asset_issuer", PaymentOperation.Issuer). + Str("amount", PaymentOperation.Amount). + Str("tx_hash", PaymentOperation.TransactionHash). + Msg("non-TFT payment to bridge detected — skipping") + continue + } + parsedAmount, err := amount.ParseInt64(PaymentOperation.Amount) if err != nil { continue @@ -531,16 +764,13 @@ func (w *StellarWallet) getOperationEffect(txHash string) (ops operations.Operat return ops, nil } -// getHorizonClient gets the horizon client based on the wallet's network +// getHorizonClient gets the horizon client based on the wallet's network. +// If StellarHorizonUrl is set, it takes precedence over the network default. func (w *StellarWallet) getHorizonClient() (*horizonclient.Client, error) { var client *horizonclient.Client - if w.config.StellarHorizonUrl != "" { - client = &horizonclient.Client{HorizonURL: w.config.StellarHorizonUrl} - } - switch w.config.StellarNetwork { - case "testnet": + case "testnet", "local": client = horizonclient.DefaultTestNetClient case "production": client = horizonclient.DefaultPublicNetClient @@ -548,29 +778,11 @@ func (w *StellarWallet) getHorizonClient() (*horizonclient.Client, error) { return nil, errors.New("network is not supported") } - // custom HTTP client with retry logic - retryClient := retryablehttp.NewClient() - retryClient.RetryMax = 3 - retryClient.RetryWaitMin = 2 * time.Second - retryClient.RetryWaitMax = 5 * time.Second - - retryClient.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) { - if ctx.Err() != nil { - return false, ctx.Err() - } - - if err != nil { - return true, nil - } - - if resp.StatusCode == 429 || (resp.StatusCode >= 500 && resp.StatusCode <= 599) { - return true, nil - } - - return false, nil + // Custom Horizon URL takes precedence over network defaults + if w.config.StellarHorizonUrl != "" { + client = &horizonclient.Client{HorizonURL: w.config.StellarHorizonUrl} } - - client.HTTP = retryClient.StandardClient() + client.HTTP = w.httpClient return client, nil } @@ -578,7 +790,7 @@ func (w *StellarWallet) getHorizonClient() (*horizonclient.Client, error) { // getNetworkPassPhrase gets the Stellar network passphrase based on the wallet's network func (w *StellarWallet) getNetworkPassPhrase() string { switch w.config.StellarNetwork { - case "testnet": + case "testnet", "local": return network.TestNetworkPassphrase case "production": return network.PublicNetworkPassphrase @@ -588,8 +800,16 @@ func (w *StellarWallet) getNetworkPassPhrase() string { } func (w *StellarWallet) getAssetCodeAndIssuer() []string { + // If the wallet has been initialised with a resolved asset (discovered from + // the bridge account's actual balance), use that. This handles dev/test + // environments that use a custom TFT issuer. + if w.resolvedAssetCode != "" { + return []string{w.resolvedAssetCode, w.resolvedAssetIssuer} + } + // Pre-init fallback: derive from network config (used only during NewStellarWallet + // before resolvedAsset is populated). switch w.config.StellarNetwork { - case "testnet": + case "testnet", "local": return strings.Split(TFTTest, ":") case "production": return strings.Split(TFTMainnet, ":") @@ -607,7 +827,7 @@ func (w *StellarWallet) StatBridgeAccount() (string, error) { asset := w.getAssetCodeAndIssuer() for _, balance := range acc.Balances { - if balance.Code == asset[0] || balance.Issuer == asset[1] { + if balance.Code == asset[0] && balance.Issuer == asset[1] { return balance.Balance, nil } } diff --git a/bridge/tfchain_bridge/pkg/substrate/client.go b/bridge/tfchain_bridge/pkg/substrate/client.go index d3461b458..c2883ddb4 100644 --- a/bridge/tfchain_bridge/pkg/substrate/client.go +++ b/bridge/tfchain_bridge/pkg/substrate/client.go @@ -63,13 +63,24 @@ func NewSubstrateClient(url string, seed string) (*SubstrateClient, error) { func (s *SubstrateClient) RetrySetWithdrawExecuted(ctx context.Context, tixd uint64) error { err := s.SetBurnTransactionExecuted(s.identity, tixd) for err != nil { - log.Err(err).Msg("error while setting refund transaction as executed") + // BurnTransactionAlreadyExecuted is expected in multi-validator: another validator + // won the submission race. Check immediately before sleeping. + burnedAlready, bErr := s.IsBurnedAlready(types.U64(tixd)) + if bErr != nil { + return bErr + } + if burnedAlready { + log.Warn().Err(err).Msg("burn transaction already executed by another validator; skipping") + return nil + } + + log.Err(err).Msg("error while setting burn transaction as executed") select { case <-ctx.Done(): return err case <-time.After(10 * time.Second): - burnedAlready, bErr := s.IsBurnedAlready(types.U64(tixd)) + burnedAlready, bErr = s.IsBurnedAlready(types.U64(tixd)) if bErr != nil { return bErr } @@ -113,13 +124,24 @@ func (s *SubstrateClient) RetryProposeWithdrawOrAddSig(ctx context.Context, txID func (s *SubstrateClient) RetryCreateRefundTransactionOrAddSig(ctx context.Context, txHash string, target string, amount int64, signature string, stellarAddress string, sequence_number uint64) error { err := s.CreateRefundTransactionOrAddSig(s.identity, txHash, target, amount, signature, stellarAddress, sequence_number) for err != nil { + // EnoughRefundSignaturesPresent / RefundTransactionAlreadyExecuted are expected in + // multi-validator: threshold already met by other validators. Check immediately. + refundedAlready, rErr := s.IsRefundedAlready(txHash) + if rErr != nil { + return rErr + } + if refundedAlready { + log.Warn().Err(err).Msg("refund already handled by another validator; skipping") + return nil + } + log.Err(err).Msg("error while creating refund tx or adding signature") select { case <-ctx.Done(): return err case <-time.After(10 * time.Second): - refundedAlready, rErr := s.IsRefundedAlready(txHash) + refundedAlready, rErr = s.IsRefundedAlready(txHash) if rErr != nil { return rErr } @@ -129,7 +151,6 @@ func (s *SubstrateClient) RetryCreateRefundTransactionOrAddSig(ctx context.Conte } else { err = nil } - } } @@ -139,13 +160,24 @@ func (s *SubstrateClient) RetryCreateRefundTransactionOrAddSig(ctx context.Conte func (s *SubstrateClient) RetrySetRefundTransactionExecutedTx(ctx context.Context, txHash string) error { err := s.SetRefundTransactionExecuted(s.identity, txHash) for err != nil { + // RefundTransactionAlreadyExecuted is expected in multi-validator: another validator + // won the submission race. Check immediately before sleeping. + refundedAlready, rErr := s.IsRefundedAlready(txHash) + if rErr != nil { + return rErr + } + if refundedAlready { + log.Warn().Err(err).Msg("refund transaction already executed by another validator; skipping") + return nil + } + log.Err(err).Msg("error while setting refund transaction as executed") select { case <-ctx.Done(): return err case <-time.After(10 * time.Second): - refundedAlready, rErr := s.IsRefundedAlready(txHash) + refundedAlready, rErr = s.IsRefundedAlready(txHash) if rErr != nil { return rErr } @@ -161,6 +193,171 @@ func (s *SubstrateClient) RetrySetRefundTransactionExecutedTx(ctx context.Contex return nil } +// BurnProposal holds the parameters for a single propose_burn_transaction_or_add_sig call. +type BurnProposal struct { + TxID uint64 + Target string + Amount *big.Int + Signature string + StellarAddress string + SequenceNumber uint64 +} + +// RefundProposal holds the parameters for a single create_refund_transaction_or_add_sig call. +type RefundProposal struct { + TxHash string + Target string + Amount int64 + Signature string + StellarAddress string + SequenceNumber uint64 +} + +// BatchProposeAll submits all burn and refund proposal calls as a single +// Utility.force_batch extrinsic. Burns are submitted first, then refunds. +// Individual call failures do not abort the batch. +func (s *SubstrateClient) BatchProposeAll(ctx context.Context, burnProposals []BurnProposal, refundProposals []RefundProposal) (*substrate.BatchResult, error) { + total := len(burnProposals) + len(refundProposals) + if total == 0 { + return &substrate.BatchResult{}, nil + } + + _, meta, err := s.GetClient() + if err != nil { + return nil, err + } + + calls := make([]types.Call, 0, total) + for _, p := range burnProposals { + c, err := types.NewCall(meta, "TFTBridgeModule.propose_burn_transaction_or_add_sig", + p.TxID, p.Target, types.U64(p.Amount.Uint64()), p.Signature, p.StellarAddress, p.SequenceNumber, + ) + if err != nil { + return nil, err + } + calls = append(calls, c) + } + for _, p := range refundProposals { + c, err := types.NewCall(meta, "TFTBridgeModule.create_refund_transaction_or_add_sig", + p.TxHash, p.Target, types.U64(uint64(p.Amount)), p.Signature, p.StellarAddress, p.SequenceNumber, + ) + if err != nil { + return nil, err + } + calls = append(calls, c) + } + + return s.BatchCalls(s.identity, calls) +} + +// BatchSetWithdrawExecuted batches multiple set_burn_transaction_executed calls +// into a single Utility.force_batch extrinsic. Falls back to individual +// RetrySetWithdrawExecuted calls if the batch submission fails. +// +// Safety: set_burn_transaction_executed is idempotent (first-writer-wins). +// ItemFailed events in the batch are expected when another validator already +// executed the burn — force_batch continues through all calls regardless. +func (s *SubstrateClient) BatchSetWithdrawExecuted(ctx context.Context, txIDs []uint64) error { + if len(txIDs) == 0 { + return nil + } + // Single item — use existing retry path (more robust error handling + // with IsBurnedAlready polling for multi-validator races) + if len(txIDs) == 1 { + return s.RetrySetWithdrawExecuted(ctx, txIDs[0]) + } + + _, meta, err := s.GetClient() + if err != nil { + log.Warn().Err(err).Msg("batch SetWithdrawExecuted: client error, falling back to individual") + return s.fallbackRetryWithdraws(ctx, txIDs) + } + + calls := make([]types.Call, 0, len(txIDs)) + for _, txID := range txIDs { + call, err := types.NewCall(meta, "TFTBridgeModule.set_burn_transaction_executed", types.U64(txID)) + if err != nil { + return err + } + calls = append(calls, call) + } + + result, err := s.BatchCalls(s.identity, calls) + if err != nil { + log.Warn().Err(err).Int("count", len(txIDs)). + Msg("batch SetWithdrawExecuted failed, falling back to individual retries") + return s.fallbackRetryWithdraws(ctx, txIDs) + } + + log.Info(). + Int("success", result.SuccessCount). + Int("failed", result.FailedCount). + Int("total", len(txIDs)). + Msg("batch SetWithdrawExecuted completed") + + return nil +} + +func (s *SubstrateClient) fallbackRetryWithdraws(ctx context.Context, txIDs []uint64) error { + for _, txID := range txIDs { + if err := s.RetrySetWithdrawExecuted(ctx, txID); err != nil { + return err + } + } + return nil +} + +// BatchSetRefundTransactionExecuted batches multiple set_refund_transaction_executed +// calls into a single Utility.force_batch extrinsic. Falls back to individual +// RetrySetRefundTransactionExecutedTx calls if the batch submission fails. +func (s *SubstrateClient) BatchSetRefundTransactionExecuted(ctx context.Context, txHashes []string) error { + if len(txHashes) == 0 { + return nil + } + if len(txHashes) == 1 { + return s.RetrySetRefundTransactionExecutedTx(ctx, txHashes[0]) + } + + _, meta, err := s.GetClient() + if err != nil { + log.Warn().Err(err).Msg("batch SetRefundExecuted: client error, falling back to individual") + return s.fallbackRetryRefunds(ctx, txHashes) + } + + calls := make([]types.Call, 0, len(txHashes)) + for _, txHash := range txHashes { + call, err := types.NewCall(meta, "TFTBridgeModule.set_refund_transaction_executed", txHash) + if err != nil { + return err + } + calls = append(calls, call) + } + + result, err := s.BatchCalls(s.identity, calls) + if err != nil { + log.Warn().Err(err).Int("count", len(txHashes)). + Msg("batch SetRefundExecuted failed, falling back to individual retries") + return s.fallbackRetryRefunds(ctx, txHashes) + } + + log.Info(). + Int("success", result.SuccessCount). + Int("failed", result.FailedCount). + Int("total", len(txHashes)). + Msg("batch SetRefundTransactionExecuted completed") + + return nil +} + +func (s *SubstrateClient) fallbackRetryRefunds(ctx context.Context, txHashes []string) error { + for _, txHash := range txHashes { + if err := s.RetrySetRefundTransactionExecutedTx(ctx, txHash); err != nil { + return err + } + } + return nil +} + func (s *SubstrateClient) RetryProposeMintOrVote(ctx context.Context, txID string, target substrate.AccountID, amount *big.Int) error { err := s.ProposeOrVoteMintTransaction(s.identity, txID, target, amount) for err != nil { diff --git a/bridge/tfchain_bridge/pkg/substrate/events.go b/bridge/tfchain_bridge/pkg/substrate/events.go index 466ac8655..0fa0f4ccc 100644 --- a/bridge/tfchain_bridge/pkg/substrate/events.go +++ b/bridge/tfchain_bridge/pkg/substrate/events.go @@ -130,12 +130,27 @@ func (client *SubstrateClient) processEventsForHeight(height uint32) (Events, er } func (client *SubstrateClient) processEventRecords(events *substrate.EventRecords) Events { + var refundCreatedEvents []RefundTransactionCreatedEvent var refundTransactionReadyEvents []RefundTransactionReadyEvent var refundTransactionExpiredEvents []RefundTransactionExpiredEvent var withdrawCreatedEvents []WithdrawCreatedEvent var withdrawReadyEvents []WithdrawReadyEvent var withdrawExpiredEvents []WithdrawExpiredEvent + for _, e := range events.TFTBridgeModule_RefundTransactionCreated { + log.Info(). + Str("trace_id", string(e.RefundTransactionHash)). + Str("event_action", "event_refund_tx_created_received"). + Str("event_kind", "event"). + Str("category", "refund"). + Msg("found RefundTransactionCreated event") + refundCreatedEvents = append(refundCreatedEvents, RefundTransactionCreatedEvent{ + Hash: string(e.RefundTransactionHash), + Target: string(e.Target), + Amount: uint64(e.Amount), + }) + } + for _, e := range events.TFTBridgeModule_RefundTransactionReady { log.Info(). Str("trace_id", string(e.RefundTransactionHash)). @@ -233,6 +248,7 @@ func (client *SubstrateClient) processEventRecords(events *substrate.EventRecord WithdrawCreatedEvents: withdrawCreatedEvents, WithdrawReadyEvents: withdrawReadyEvents, WithdrawExpiredEvents: withdrawExpiredEvents, + RefundCreatedEvents: refundCreatedEvents, RefundReadyEvents: refundTransactionReadyEvents, RefundExpiredEvents: refundTransactionExpiredEvents, } diff --git a/clients/tfchain-client-go/batch.go b/clients/tfchain-client-go/batch.go new file mode 100644 index 000000000..742003328 --- /dev/null +++ b/clients/tfchain-client-go/batch.go @@ -0,0 +1,69 @@ +package substrate + +import ( + "github.com/centrifuge/go-substrate-rpc-client/v4/types" + "github.com/pkg/errors" +) + +// BatchResult holds the outcome of a Utility.batch call. +type BatchResult struct { + SuccessCount int + FailedCount int + // FailedIndexes is only populated for BatchInterrupted (older runtimes), + // where we know the exact index that caused interruption. + FailedIndexes []int +} + +// BatchCalls submits multiple calls in a single Utility.force_batch extrinsic. +// Unlike Utility.batch_all (aborts + reverts on first failure) and Utility.batch +// (stops at first failure, remaining calls not executed), Utility.force_batch +// continues through all calls regardless of individual failures. Failed calls emit +// Utility.ItemFailed events; the overall batch always completes. +// This is the correct choice for bridge proposals: a BurnSignatureExists error on +// one proposal must not prevent the remaining proposals from being submitted. +func (s *Substrate) BatchCalls(identity Identity, calls []types.Call) (*BatchResult, error) { + if len(calls) == 0 { + return &BatchResult{}, nil + } + + cl, meta, err := s.GetClient() + if err != nil { + return nil, err + } + + batchCall, err := types.NewCall(meta, "Utility.force_batch", calls) + if err != nil { + return nil, errors.Wrap(err, "failed to create force_batch call") + } + + resp, err := s.Call(cl, meta, identity, batchCall) + if err != nil { + return nil, errors.Wrap(err, "failed to execute force_batch call") + } + + result := &BatchResult{ + SuccessCount: len(calls), + } + + if resp.Events != nil { + // ItemFailed events are emitted by force_batch for each failed call. + // The event payload does not carry the batch-call index — only a DispatchError. + // We count failures but cannot reliably map them to specific call positions. + // + // IMPORTANT: getEventRecords reads ALL events from the block, not just + // those emitted by this extrinsic. In multi-validator setups, multiple + // validators may submit force_batch extrinsics in the same block. Each + // validator sees ItemFailed events from ALL batches, inflating the count. + // Cap failedCount at len(calls) to prevent negative SuccessCount. + failedCount := len(resp.Events.Utility_ItemFailed) + if failedCount > len(calls) { + failedCount = len(calls) + } + if failedCount > 0 { + result.FailedCount = failedCount + result.SuccessCount = len(calls) - failedCount + } + } + + return result, nil +} diff --git a/scripts/bridge_accounts.js b/scripts/bridge_accounts.js new file mode 100644 index 000000000..004e76a4c --- /dev/null +++ b/scripts/bridge_accounts.js @@ -0,0 +1,165 @@ +#!/usr/bin/env node +/** + * bridge_accounts.js + * + * Sets up all Stellar accounts needed for a local bridge dev environment: + * - Issuer account (mints local TFT) + * - Bridge account (multi-sig wallet; holds TFT float) + * - User account (sends/receives TFT) + * + * Steps: + * 1. Generate fresh keypairs for issuer, bridge, user + * 2. Fund all three via Stellar testnet Friendbot + * 3. Create TFT trustlines on bridge and user accounts + * 4. Issue TFT from issuer → bridge (via path_payment_strict_send so the bridge + * deposit monitor ignores it — it only watches `payment` ops) + * 5. Issue TFT from issuer → user (regular payment; user is not monitored by bridge) + * 6. Write /tmp/bridge_local_env.sh for sourcing by Make targets and other scripts + * + * Usage: + * node scripts/bridge_accounts.js + * + * Override any account by setting env vars before running: + * BRIDGE_SECRET=S... BRIDGE_ADDRESS=G... node scripts/bridge_accounts.js + */ + +'use strict' + +const StellarSdk = require('@stellar/stellar-sdk') +const fs = require('fs') +const { friendbot, waitForAccount } = require('./bridge_helpers') + +const HORIZON_URL = process.env.STELLAR_HORIZON_URL || 'https://horizon-testnet.stellar.org' +const NETWORK_PASSPHRASE = StellarSdk.Networks.TESTNET +const ENV_FILE = process.env.BRIDGE_ENV_FILE || '/tmp/bridge_local_env.sh' + +const BRIDGE_TFT_FLOAT = process.env.BRIDGE_TFT_FLOAT || '20000' +const USER_TFT_AMOUNT = process.env.USER_TFT_AMOUNT || '1000' +const TFT_ASSET_CODE = 'TFT' + +const server = new StellarSdk.Horizon.Server(HORIZON_URL) + +function log (msg) { console.log(`[accounts] ${msg}`) } +function err (msg) { console.error(`[accounts] ERROR: ${msg}`); process.exit(1) } + +async function main () { + // 1. Generate or reuse keypairs + const issuerKp = process.env.ISSUER_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.ISSUER_SECRET) + : StellarSdk.Keypair.random() + + const bridgeKp = process.env.BRIDGE_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.BRIDGE_SECRET) + : StellarSdk.Keypair.random() + + const userKp = process.env.USER_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.USER_SECRET) + : StellarSdk.Keypair.random() + + log(`Issuer: ${issuerKp.publicKey()}`) + log(`Bridge: ${bridgeKp.publicKey()}`) + log(`User: ${userKp.publicKey()}`) + + const TFT = new StellarSdk.Asset(TFT_ASSET_CODE, issuerKp.publicKey()) + + // 2. Fund all three via Friendbot + log('Funding accounts via Friendbot...') + await Promise.all([ + friendbot(issuerKp.publicKey()), + friendbot(bridgeKp.publicKey()), + friendbot(userKp.publicKey()) + ]) + log('Friendbot done. Waiting for accounts to appear on Horizon...') + + const [, bridgeAcc, userAcc] = await Promise.all([ + waitForAccount(issuerKp.publicKey(), server), + waitForAccount(bridgeKp.publicKey(), server), + waitForAccount(userKp.publicKey(), server) + ]) + + // 3. Create TFT trustlines on bridge and user + log('Creating TFT trustlines on bridge and user accounts...') + + async function addTrustline (kp, acc) { + const tx = new StellarSdk.TransactionBuilder(acc, { + fee: '1000', + networkPassphrase: NETWORK_PASSPHRASE + }) + .addOperation(StellarSdk.Operation.changeTrust({ asset: TFT })) + .setTimeout(30) + .build() + tx.sign(kp) + await server.submitTransaction(tx) + } + + await Promise.all([ + addTrustline(bridgeKp, bridgeAcc), + addTrustline(userKp, userAcc) + ]) + log('Trustlines created.') + + // Reload accounts after trustline txs + const [issuerAcc2] = await Promise.all([ + waitForAccount(issuerKp.publicKey(), server), + waitForAccount(bridgeKp.publicKey(), server), + waitForAccount(userKp.publicKey(), server) + ]) + + // 4. Fund bridge via path_payment_strict_send (invisible to bridge deposit monitor) + log(`Issuing ${BRIDGE_TFT_FLOAT} TFT to bridge via path_payment_strict_send...`) + const bridgeFundTx = new StellarSdk.TransactionBuilder(issuerAcc2, { + fee: '1000', + networkPassphrase: NETWORK_PASSPHRASE + }) + .addOperation(StellarSdk.Operation.pathPaymentStrictSend({ + sendAsset: TFT, + sendAmount: BRIDGE_TFT_FLOAT, + destination: bridgeKp.publicKey(), + destAsset: TFT, + destMin: String(Number(BRIDGE_TFT_FLOAT) - 1), + path: [] + })) + .setTimeout(30) + .build() + bridgeFundTx.sign(issuerKp) + await server.submitTransaction(bridgeFundTx) + log(`Bridge funded with ${BRIDGE_TFT_FLOAT} TFT.`) + + // Reload issuer after bridge funding tx + const issuerAcc3 = await waitForAccount(issuerKp.publicKey(), server) + + // 5. Fund user via regular payment (user account is not monitored by bridge) + log(`Issuing ${USER_TFT_AMOUNT} TFT to user...`) + const userFundTx = new StellarSdk.TransactionBuilder(issuerAcc3, { + fee: '1000', + networkPassphrase: NETWORK_PASSPHRASE + }) + .addOperation(StellarSdk.Operation.payment({ + destination: userKp.publicKey(), + asset: TFT, + amount: USER_TFT_AMOUNT + })) + .setTimeout(30) + .build() + userFundTx.sign(issuerKp) + await server.submitTransaction(userFundTx) + log(`User funded with ${USER_TFT_AMOUNT} TFT.`) + + // 6. Write env file + const envContent = `# Auto-generated by bridge_accounts.js — do not edit manually +export ISSUER_ADDRESS="${issuerKp.publicKey()}" +export ISSUER_SECRET="${issuerKp.secret()}" +export BRIDGE_ADDRESS="${bridgeKp.publicKey()}" +export BRIDGE_SECRET="${bridgeKp.secret()}" +export USER_ADDRESS="${userKp.publicKey()}" +export USER_SECRET="${userKp.secret()}" +export TFT_ASSET_CODE="${TFT_ASSET_CODE}" +export STELLAR_HORIZON_URL="${HORIZON_URL}" +export STELLAR_NETWORK="testnet" +` + fs.writeFileSync(ENV_FILE, envContent) + log(`Environment written to ${ENV_FILE}`) + log('Done.') +} + +main().catch(e => err(e.message || String(e))) diff --git a/scripts/bridge_helpers.js b/scripts/bridge_helpers.js new file mode 100644 index 000000000..a55abe405 --- /dev/null +++ b/scripts/bridge_helpers.js @@ -0,0 +1,209 @@ +#!/usr/bin/env node +/** + * bridge_helpers.js + * + * Shared helpers for bridge E2E test scripts. Extracted from bridge_tests.js + * and bridge_mv_tests.js to eliminate duplication. + */ + +'use strict' + +const fs = require('fs') +const https = require('https') +const StellarSdk = require('@stellar/stellar-sdk') + +// ─── Logging & test result helpers ────────────────────────────────────────── + +function log (msg) { console.log(` ${msg}`) } +function pass (name, counter) { console.log(`\u2705 PASS: ${name}`); counter.passed++ } +function fail (name, reason, counter) { console.error(`\u274c FAIL: ${name} \u2014 ${reason}`); counter.failed++ } + +// ─── Env helpers ──────────────────────────────────────────────────────────── + +/** + * Load a shell-style env file (export KEY="value") into process.env. + * @param {string} envFile - Path to the env file + * @param {string} [label='tests'] - Label for error messages + */ +function loadEnv (envFile, label = 'tests') { + if (!fs.existsSync(envFile)) { + console.error(`[${label}] Env file not found: ${envFile}. Run the accounts target first.`) + process.exit(1) + } + const lines = fs.readFileSync(envFile, 'utf8').split('\n') + for (const line of lines) { + const m = line.match(/^export\s+(\w+)="([^"]*)"/) + if (m) process.env[m[1]] = m[2] + } +} + +/** + * Get a required env var or exit with an error. + */ +function getEnv (key) { + const val = process.env[key] + if (!val) { console.error(`Missing env var: ${key}`); process.exit(1) } + return val +} + +// ─── Stellar helpers ──────────────────────────────────────────────────────── + +/** + * Get TFT balance for a Stellar address. + * @param {string} address - Stellar public key + * @param {object} horizon - Horizon.Server instance + * @param {string} issuerAddress - TFT issuer public key + */ +async function stellarTFTBalance (address, horizon, issuerAddress) { + const acc = await horizon.loadAccount(address) + const tft = acc.balances.find(b => b.asset_code === 'TFT' && b.asset_issuer === issuerAddress) + return tft ? parseFloat(tft.balance) : 0 +} + +/** + * Fund a Stellar address via Friendbot (testnet). + * Treats HTTP 400 as success (already funded). + * @param {string} address - Stellar public key + * @param {string} [friendbotUrl='https://friendbot.stellar.org'] - Friendbot URL + */ +async function friendbot (address, friendbotUrl = 'https://friendbot.stellar.org') { + return new Promise((resolve, reject) => { + https.get(`${friendbotUrl}?addr=${address}`, (res) => { + let data = '' + res.on('data', c => { data += c }) + res.on('end', () => { + if (res.statusCode === 200 || res.statusCode === 400) resolve() + else reject(new Error(`Friendbot ${res.statusCode}: ${data.slice(0, 200)}`)) + }) + }).on('error', reject) + }) +} + +/** + * Wait for a Stellar account to appear on Horizon. + * @param {string} address - Stellar public key + * @param {object} server - Horizon.Server instance + * @param {number} [retries=12] - Max retry attempts (2s apart) + */ +async function waitForAccount (address, server, retries = 12) { + for (let i = 0; i < retries; i++) { + try { return await server.loadAccount(address) } catch {} + if (i < retries - 1) await new Promise(r => setTimeout(r, 2000)) + } + throw new Error(`Account ${address} not found after ${retries} attempts`) +} + +/** + * Send a Stellar payment (TFT asset) with optional memo. + * @param {object} horizon - Horizon.Server instance + * @param {string} issuerAddress - TFT issuer public key + * @param {string} networkPassphrase - Stellar network passphrase + * @param {string} fromSecret - Sender's Stellar secret key + * @param {string} toAddress - Destination Stellar public key + * @param {string|number} amount - Amount to send (string for Stellar precision) + * @param {string|null} [memo=null] - Optional text memo (e.g. "twin_") + * @returns {Promise} Horizon submit result (includes .hash) + */ +async function sendStellarPayment (horizon, issuerAddress, networkPassphrase, fromSecret, toAddress, amount, memo = null) { + const kp = StellarSdk.Keypair.fromSecret(fromSecret) + const TFTAsset = new StellarSdk.Asset('TFT', issuerAddress) + const acc = await horizon.loadAccount(kp.publicKey()) + + const builder = new StellarSdk.TransactionBuilder(acc, { + fee: '1000', + networkPassphrase + }).addOperation(StellarSdk.Operation.payment({ + destination: toAddress, + asset: TFTAsset, + amount: String(amount) + })).setTimeout(30) + + if (memo) builder.addMemo(StellarSdk.Memo.text(String(memo))) + + const tx = builder.build() + tx.sign(kp) + return horizon.submitTransaction(tx) +} + +// ─── Polling helper ───────────────────────────────────────────────────────── + +/** + * Poll until condition() returns truthy or timeout. + * @param {function} condition - Async function; return truthy to stop + * @param {object} opts + * @param {number} [opts.timeoutMs=180000] - Timeout in ms + * @param {number} [opts.intervalMs=3000] - Poll interval in ms + * @param {string} [opts.desc=''] - Description for timeout error + */ +async function waitUntil (condition, { timeoutMs = 180_000, intervalMs = 3000, desc = '' } = {}) { + const deadline = Date.now() + timeoutMs + while (Date.now() < deadline) { + const result = await condition() + if (result) return result + await new Promise(r => setTimeout(r, intervalMs)) + } + throw new Error(`Timeout waiting for: ${desc}`) +} + +// ─── TFChain helpers ──────────────────────────────────────────────────────── + +// TFT has 7 decimal places: 1 TFT = 10_000_000 base units +const TFT_DECIMALS = 1e7 +const TFT = (amount) => Math.round(amount * TFT_DECIMALS) + +/** + * Get free TFT balance for a TFChain address (returns float in TFT units). + * @param {object} api - ApiPromise instance + * @param {string} address - TFChain SS58 address + */ +async function tfchainBalance (api, address) { + const { data } = await api.query.system.account(address) + return Number(data.free) / TFT_DECIMALS +} + +/** + * Submit a swapToStellar extrinsic on TFChain. + * @param {object} api - ApiPromise instance + * @param {object} signer - Keyring pair (e.g. alice) + * @param {number} amountTFT - Amount in whole TFT + * @param {object} opts + * @param {string} opts.userAddress - Stellar destination address + * @param {number} [opts.nonce=-1] - Explicit nonce (-1 = auto) + */ +async function swapToStellar (api, signer, amountTFT, { userAddress, nonce = -1 } = {}) { + return new Promise((resolve, reject) => { + api.tx.tftBridgeModule.swapToStellar(userAddress, TFT(amountTFT)) + .signAndSend(signer, { nonce }, ({ status, dispatchError, events }) => { + if (dispatchError?.isModule) { + const d = api.registry.findMetaError(dispatchError.asModule) + reject(new Error(`${d.section}.${d.name}`)); return + } + if (status.isInBlock) { + let burnId = null + events.forEach(({ event }) => { + if (event.section === 'tftBridgeModule' && event.method === 'BurnTransactionCreated') { + burnId = event.data[0].toNumber() + } + }) + resolve(burnId) + } + }) + }) +} + +module.exports = { + log, + pass, + fail, + loadEnv, + getEnv, + stellarTFTBalance, + tfchainBalance, + friendbot, + waitForAccount, + waitUntil, + sendStellarPayment, + swapToStellar, + TFT, + TFT_DECIMALS +} diff --git a/scripts/bridge_instrumentation.js b/scripts/bridge_instrumentation.js new file mode 100644 index 000000000..eb0837676 --- /dev/null +++ b/scripts/bridge_instrumentation.js @@ -0,0 +1,1067 @@ +#!/usr/bin/env node +/** + * bridge_instrumentation.js + * + * Event collector and analysis engine for bridge E2E tests. + * Subscribes to every finalized block, captures all tftBridgeModule and utility + * events with block numbers and wall-clock timestamps, then generates a + * structured report mapping each transaction's full lifecycle. + * + * Usage (from bridge_mv_tests.js): + * const { startEventCollector, markTestPhase, generateReport } = require('./bridge_instrumentation') + * const collector = await startEventCollector(api) + * markTestPhase(collector, 'MV1', 'start') + * // ... test body ... + * markTestPhase(collector, 'MV1', 'end') + * await generateReport(collector, '/tmp/bridge_mv_analysis.json', api) + * collector.stop() + */ + +'use strict' + +const fs = require('fs') + +// ─── Event Collector ──────────────────────────────────────────────────────── + +/** + * Start a background event collector that captures all bridge-related events + * from every finalized block. + * + * @param {object} api - Connected ApiPromise instance + * @returns {object} collector with events, indexes, and stop() + */ +async function startEventCollector (api) { + const collector = { + events: [], + blockTimestamps: {}, + burnEvents: {}, + refundEvents: {}, + mintEvents: {}, + utilityEvents: [], + blockEventLog: {}, + testPhases: [], + _unsub: null, + _startTime: Date.now(), + _firstBlock: null, + _lastBlock: null, + + stop () { + if (this._unsub) { + this._unsub() + this._unsub = null + } + } + } + + collector._unsub = await api.rpc.chain.subscribeFinalizedHeads(async (header) => { + const blockNum = header.number.toNumber() + const blockHash = header.hash + collector.blockTimestamps[blockNum] = Date.now() + if (collector._firstBlock === null) collector._firstBlock = blockNum + collector._lastBlock = blockNum + + try { + const apiAt = await api.at(blockHash) + const records = await apiAt.query.system.events() + + records.forEach((record, idx) => { + const { event, phase } = record + const section = event.section + const method = event.method + + // Only capture bridge and utility events + if (section !== 'tftBridgeModule' && section !== 'utility') return + + const entry = { + block: blockNum, + timestamp: collector.blockTimestamps[blockNum], + index: idx, + section, + method, + data: event.data.map(d => d.toString()), + phase: phase.toString() + } + + collector.events.push(entry) + + // Index by block + if (!collector.blockEventLog[blockNum]) collector.blockEventLog[blockNum] = [] + collector.blockEventLog[blockNum].push(entry) + + // Index bridge events by transaction ID + if (section === 'tftBridgeModule') { + indexBridgeEvent(collector, entry) + } + if (section === 'utility') { + collector.utilityEvents.push(entry) + } + }) + } catch (err) { + // Silently ignore block fetch errors — don't disrupt the tests + } + }) + + return collector +} + +/** + * Index a bridge event into the collector's transaction maps. + */ +function indexBridgeEvent (collector, entry) { + const { method, data } = entry + + switch (method) { + // ── Burn lifecycle ── + case 'BurnTransactionCreated': { + const burnId = data[0] + if (!collector.burnEvents[burnId]) collector.burnEvents[burnId] = [] + collector.burnEvents[burnId].push({ ...entry, state: 'created' }) + break + } + case 'BurnTransactionProposed': { + const burnId = data[0] + if (!collector.burnEvents[burnId]) collector.burnEvents[burnId] = [] + collector.burnEvents[burnId].push({ ...entry, state: 'proposed' }) + break + } + case 'BurnTransactionSignatureAdded': { + const burnId = data[0] + if (!collector.burnEvents[burnId]) collector.burnEvents[burnId] = [] + collector.burnEvents[burnId].push({ ...entry, state: 'sig_added' }) + break + } + case 'BurnTransactionReady': { + const burnId = data[0] + if (!collector.burnEvents[burnId]) collector.burnEvents[burnId] = [] + collector.burnEvents[burnId].push({ ...entry, state: 'ready' }) + break + } + case 'BurnTransactionProcessed': { + // BurnTransactionProcessed carries the full BurnTransaction struct, not the burn_id. + // Store globally for post-collection reconciliation. + if (!collector.burnEvents._processed) collector.burnEvents._processed = [] + collector.burnEvents._processed.push({ ...entry, state: 'processed' }) + break + } + case 'BurnTransactionExpired': { + const burnId = data[0] + if (!collector.burnEvents[burnId]) collector.burnEvents[burnId] = [] + collector.burnEvents[burnId].push({ ...entry, state: 'expired' }) + break + } + + // ── Refund lifecycle ── + case 'RefundTransactionCreated': { + const txHash = data[0] + if (!collector.refundEvents[txHash]) collector.refundEvents[txHash] = [] + collector.refundEvents[txHash].push({ ...entry, state: 'created' }) + break + } + case 'RefundTransactionsignatureAdded': { + // Note: lowercase 's' — that's a typo in the pallet, not here. + const txHash = data[0] + if (!collector.refundEvents[txHash]) collector.refundEvents[txHash] = [] + collector.refundEvents[txHash].push({ ...entry, state: 'sig_added' }) + break + } + case 'RefundTransactionReady': { + const txHash = data[0] + if (!collector.refundEvents[txHash]) collector.refundEvents[txHash] = [] + collector.refundEvents[txHash].push({ ...entry, state: 'ready' }) + break + } + case 'RefundTransactionProcessed': { + if (!collector.refundEvents._processed) collector.refundEvents._processed = [] + collector.refundEvents._processed.push({ ...entry, state: 'processed' }) + break + } + case 'RefundTransactionExpired': { + const txHash = data[0] + if (!collector.refundEvents[txHash]) collector.refundEvents[txHash] = [] + collector.refundEvents[txHash].push({ ...entry, state: 'expired' }) + break + } + + // ── Mint lifecycle ── + case 'MintTransactionProposed': { + const txId = data[0] + if (!collector.mintEvents[txId]) collector.mintEvents[txId] = [] + collector.mintEvents[txId].push({ ...entry, state: 'proposed' }) + break + } + case 'MintTransactionVoted': { + const txId = data[0] + if (!collector.mintEvents[txId]) collector.mintEvents[txId] = [] + collector.mintEvents[txId].push({ ...entry, state: 'voted' }) + break + } + case 'MintCompleted': { + // MintCompleted data: (MintTransaction, tx_id) + const txId = data[1] + if (!collector.mintEvents[txId]) collector.mintEvents[txId] = [] + collector.mintEvents[txId].push({ ...entry, state: 'completed' }) + break + } + case 'MintTransactionExpired': { + const txId = data[0] + if (!collector.mintEvents[txId]) collector.mintEvents[txId] = [] + collector.mintEvents[txId].push({ ...entry, state: 'expired' }) + break + } + } +} + +// ─── Test Phase Markers ───────────────────────────────────────────────────── + +/** + * Mark the start or end of a test phase for event correlation. + * Uses block numbers (not timestamps) to avoid async lag between + * finalized head subscription and test code execution. + */ +function markTestPhase (collector, testName, phase) { + collector.testPhases.push({ + test: testName, + phase, + timestamp: Date.now(), + block: collector._lastBlock || 0 + }) +} + +// ─── Transaction Lifecycle Analysis ───────────────────────────────────────── + +function analyzeBurn (collector, burnId) { + const events = collector.burnEvents[burnId] || [] + if (events.length === 0) return null + + const record = { + burnId: Number(burnId), + type: 'burn', + createdBlock: null, + firstSigBlock: null, + readyBlock: null, + processedBlock: null, + expiryCount: 0, + expiryBlocks: [], + sigCount: 0, + blocksCreatedToReady: null, + blocksReadyToProcessed: null, + blocksCreatedToProcessed: null, + wallClockMs: null, + transitions: [] + } + + for (const evt of events) { + record.transitions.push({ + state: evt.state, + block: evt.block, + timestamp: evt.timestamp + }) + + switch (evt.state) { + case 'created': + record.createdBlock = evt.block + break + case 'sig_added': + record.sigCount++ + if (!record.firstSigBlock) record.firstSigBlock = evt.block + break + case 'ready': + record.readyBlock = evt.block + break + case 'processed': + record.processedBlock = evt.block + break + case 'expired': + record.expiryCount++ + record.expiryBlocks.push(evt.block) + break + } + } + + // Compute deltas + if (record.createdBlock != null && record.readyBlock != null) { + record.blocksCreatedToReady = record.readyBlock - record.createdBlock + } + if (record.readyBlock != null && record.processedBlock != null) { + record.blocksReadyToProcessed = record.processedBlock - record.readyBlock + } + if (record.createdBlock != null && record.processedBlock != null) { + record.blocksCreatedToProcessed = record.processedBlock - record.createdBlock + } + + // Wall-clock time + if (events.length >= 2) { + record.wallClockMs = events[events.length - 1].timestamp - events[0].timestamp + } + + return record +} + +function analyzeRefund (collector, txHash) { + const events = collector.refundEvents[txHash] || [] + if (events.length === 0) return null + + const record = { + txHash, + type: 'refund', + createdBlock: null, + readyBlock: null, + processedBlock: null, + expiryCount: 0, + sigCount: 0, + blocksCreatedToReady: null, + blocksCreatedToProcessed: null, + wallClockMs: null, + transitions: [] + } + + for (const evt of events) { + record.transitions.push({ state: evt.state, block: evt.block, timestamp: evt.timestamp }) + switch (evt.state) { + case 'created': record.createdBlock = evt.block; break + case 'sig_added': record.sigCount++; break + case 'ready': record.readyBlock = evt.block; break + case 'processed': record.processedBlock = evt.block; break + case 'expired': record.expiryCount++; break + } + } + + if (!record.processedBlock && collector.refundEvents._processed) { + for (const pe of collector.refundEvents._processed) { + if (record.readyBlock && pe.block >= record.readyBlock && !record.processedBlock) { + record.processedBlock = pe.block + } + } + } + + if (record.createdBlock != null && record.readyBlock != null) { + record.blocksCreatedToReady = record.readyBlock - record.createdBlock + } + if (record.createdBlock != null && record.processedBlock != null) { + record.blocksCreatedToProcessed = record.processedBlock - record.createdBlock + } + if (events.length >= 2) { + record.wallClockMs = events[events.length - 1].timestamp - events[0].timestamp + } + + return record +} + +function analyzeMint (collector, txId) { + const events = collector.mintEvents[txId] || [] + if (events.length === 0) return null + + const record = { + txId, + type: 'mint', + proposedBlock: null, + completedBlock: null, + voteCount: 0, + expiryCount: 0, + blocksProposedToCompleted: null, + wallClockMs: null, + transitions: [] + } + + for (const evt of events) { + record.transitions.push({ state: evt.state, block: evt.block, timestamp: evt.timestamp }) + switch (evt.state) { + case 'proposed': record.proposedBlock = evt.block; break + case 'voted': record.voteCount++; break + case 'completed': record.completedBlock = evt.block; break + case 'expired': record.expiryCount++; break + } + } + + if (record.proposedBlock != null && record.completedBlock != null) { + record.blocksProposedToCompleted = record.completedBlock - record.proposedBlock + } + if (events.length >= 2) { + record.wallClockMs = events[events.length - 1].timestamp - events[0].timestamp + } + + return record +} + +// ─── Test Phase Correlation ───────────────────────────────────────────────── + +/** + * Determine which test a block belongs to, using block-number ranges + * from the test phase markers. This avoids the async lag issue where + * finalized-head events arrive after the test code has already moved on. + * + * Phase markers record collector._lastBlock at the time markTestPhase is + * called. A block B belongs to test T if: + * T.start.block <= B <= T.end.block (or T has no end yet and B >= T.start.block) + */ +function getTestForBlock (collector, block) { + // Build sorted phase marker pairs + const tests = {} + for (const marker of collector.testPhases) { + if (!tests[marker.test]) tests[marker.test] = {} + tests[marker.test][marker.phase] = marker.block + } + + // Walk test order (use testPhases order of first appearance) + const testOrder = [] + const seen = new Set() + for (const marker of collector.testPhases) { + if (marker.phase === 'start' && !seen.has(marker.test)) { + seen.add(marker.test) + testOrder.push(marker.test) + } + } + + // Find the test whose block range contains this block + for (let i = testOrder.length - 1; i >= 0; i--) { + const test = testOrder[i] + const startBlock = tests[test].start + const endBlock = tests[test].end + + if (startBlock == null) continue + + if (endBlock != null) { + // Completed test: block must be in range [startBlock, endBlock] + if (block >= startBlock && block <= endBlock) return test + } else { + // Still running: block must be >= startBlock + if (block >= startBlock) return test + } + } + + // Blocks before any test started — assign to first test if close + if (testOrder.length > 0) { + const firstStart = tests[testOrder[0]].start + if (block < firstStart) return 'pre-test' + } + + return 'unknown' +} + +function groupEventsByTest (collector) { + const testGroups = {} + + // Group burn events by test phase (using block of first event) + for (const [burnId, events] of Object.entries(collector.burnEvents)) { + if (burnId === '_processed') continue + if (events.length === 0) continue + const test = getTestForBlock(collector, events[0].block) + if (!testGroups[test]) testGroups[test] = { burns: [], refunds: [], mints: [] } + testGroups[test].burns.push(burnId) + } + + // Group refund events by test phase + for (const [txHash, events] of Object.entries(collector.refundEvents)) { + if (txHash === '_processed') continue + if (events.length === 0) continue + const test = getTestForBlock(collector, events[0].block) + if (!testGroups[test]) testGroups[test] = { burns: [], refunds: [], mints: [] } + testGroups[test].refunds.push(txHash) + } + + // Group mint events by test phase + for (const [txId, events] of Object.entries(collector.mintEvents)) { + if (events.length === 0) continue + const test = getTestForBlock(collector, events[0].block) + if (!testGroups[test]) testGroups[test] = { burns: [], refunds: [], mints: [] } + testGroups[test].mints.push(txId) + } + + return testGroups +} + +// ─── Batch Analysis ───────────────────────────────────────────────────────── + +/** + * Analyze how set_burn_transaction_executed / set_refund_transaction_executed + * calls are batched. For each block, count how many Processed events arrived + * in a single batch (paired with ItemCompleted inside a BatchCompleted or + * BatchCompletedWithErrors). + * + * Returns an array of batch records: + * { block, type, processedCount, failedCount, batchType } + */ +function analyzeBatches (collector) { + const batches = [] + const blockNums = Object.keys(collector.blockEventLog).map(Number).sort((a, b) => a - b) + + for (const blockNum of blockNums) { + const events = collector.blockEventLog[blockNum] + + // Walk through events and group by batch boundaries + let currentBatch = null + + for (const evt of events) { + if (evt.section === 'tftBridgeModule' && + (evt.method === 'BurnTransactionProcessed' || evt.method === 'RefundTransactionProcessed')) { + if (!currentBatch) { + currentBatch = { + block: blockNum, + type: evt.method === 'BurnTransactionProcessed' ? 'burn_executed' : 'refund_executed', + processedCount: 0, + failedCount: 0, + batchType: 'standalone' + } + } + currentBatch.processedCount++ + } + if (evt.section === 'utility' && evt.method === 'ItemFailed' && currentBatch) { + currentBatch.failedCount++ + } + if (evt.section === 'utility' && evt.method === 'BatchCompleted' && currentBatch) { + currentBatch.batchType = 'BatchCompleted' + batches.push(currentBatch) + currentBatch = null + } + if (evt.section === 'utility' && evt.method === 'BatchCompletedWithErrors' && currentBatch) { + currentBatch.batchType = 'BatchCompletedWithErrors' + batches.push(currentBatch) + currentBatch = null + } + } + + // If we have a dangling batch (Processed events without a Batch* wrapper) + if (currentBatch && currentBatch.processedCount > 0) { + batches.push(currentBatch) + } + } + + return batches +} + +/** + * Analyze signature proposal batches (propose_stellar_burn_transaction_or_add_sig). + * For each block, count how many BurnTransactionSignatureAdded events arrived + * inside a single force_batch. + */ +function analyzeSignatureBatches (collector) { + const batches = [] + const blockNums = Object.keys(collector.blockEventLog).map(Number).sort((a, b) => a - b) + + for (const blockNum of blockNums) { + const events = collector.blockEventLog[blockNum] + let currentBatch = null + + for (const evt of events) { + if (evt.section === 'tftBridgeModule' && evt.method === 'BurnTransactionSignatureAdded') { + if (!currentBatch) { + currentBatch = { + block: blockNum, + type: 'sig_proposal', + sigCount: 0, + readyCount: 0, + failedCount: 0, + batchType: 'standalone' + } + } + currentBatch.sigCount++ + } + if (evt.section === 'tftBridgeModule' && evt.method === 'BurnTransactionReady' && currentBatch) { + currentBatch.readyCount++ + } + if (evt.section === 'utility' && evt.method === 'ItemFailed' && currentBatch) { + currentBatch.failedCount++ + } + if (evt.section === 'utility' && evt.method === 'BatchCompleted' && currentBatch) { + currentBatch.batchType = 'BatchCompleted' + batches.push(currentBatch) + currentBatch = null + } + if (evt.section === 'utility' && evt.method === 'BatchCompletedWithErrors' && currentBatch) { + currentBatch.batchType = 'BatchCompletedWithErrors' + batches.push(currentBatch) + currentBatch = null + } + } + + if (currentBatch && currentBatch.sigCount > 0) { + batches.push(currentBatch) + } + } + + return batches +} + +// ─── Statistics Helpers ───────────────────────────────────────────────────── + +function mean (arr) { return arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0 } +function median (arr) { + if (!arr.length) return 0 + const s = [...arr].sort((a, b) => a - b) + const mid = Math.floor(s.length / 2) + return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2 +} +function percentile (arr, p) { + if (!arr.length) return 0 + const s = [...arr].sort((a, b) => a - b) + const idx = Math.ceil(p / 100 * s.length) - 1 + return s[Math.max(0, idx)] +} + +// ─── Anomaly Detection ────────────────────────────────────────────────────── + +function detectAnomalies (collector, testGroups) { + const anomalies = [] + + // Tests that should have zero expiries + const noExpiryTests = ['MV1', 'MV2', 'MV3', 'MV4', 'MV6'] + for (const test of noExpiryTests) { + const group = testGroups[test] + if (!group) continue + + for (const burnId of group.burns) { + const rec = analyzeBurn(collector, burnId) + if (rec && rec.expiryCount > 0) { + anomalies.push({ + severity: 'warning', + test, + message: `Burn ${burnId} expired ${rec.expiryCount} time(s) in ${test} (expected 0)`, + burnId + }) + } + } + for (const txHash of group.refunds) { + const rec = analyzeRefund(collector, txHash) + if (rec && rec.expiryCount > 0) { + anomalies.push({ + severity: 'warning', + test, + message: `Refund ${txHash.slice(0, 16)}... expired ${rec.expiryCount} time(s) in ${test} (expected 0)`, + txHash + }) + } + } + } + + // MV4 should have exactly 2 sigs per refund (Val3 offline) + if (testGroups.MV4) { + for (const txHash of testGroups.MV4.refunds) { + const rec = analyzeRefund(collector, txHash) + if (rec && rec.sigCount > 2) { + anomalies.push({ + severity: 'warning', + test: 'MV4', + message: `Refund got ${rec.sigCount} sigs (expected 2, Val3 should be offline)`, + txHash + }) + } + } + } + + // MV9: check all 50 burns reached processed state + if (testGroups.MV9) { + const unprocessed = [] + for (const burnId of testGroups.MV9.burns) { + const rec = analyzeBurn(collector, burnId) + if (rec && rec.processedBlock == null) { + unprocessed.push(burnId) + } + } + if (unprocessed.length > 0) { + anomalies.push({ + severity: 'error', + test: 'MV9', + message: `${unprocessed.length} burns never reached Processed state (event missed by collector)`, + burnIds: unprocessed + }) + } + } + + // Utility ItemFailed events + const itemFailedCount = collector.utilityEvents.filter(e => e.method === 'ItemFailed').length + if (itemFailedCount > 0) { + anomalies.push({ + severity: 'info', + test: 'global', + message: `${itemFailedCount} Utility.ItemFailed events total (expected in multi-validator races)` + }) + } + + return anomalies +} + +// ─── Chain-State Reconciliation ───────────────────────────────────────────── + +/** + * Query the on-chain ExecutedBurnTransactions and ExecutedRefundTransactions + * maps to reconcile any burns/refunds whose Processed events were missed + * by the finalized-head subscription (e.g. late-arriving blocks). + * + * Also waits for all active transaction maps to drain (like MV7), so we + * know all transactions have been fully processed before generating the report. + */ +async function reconcileFromChain (collector, api) { + // Wait for active burn/refund maps to drain (up to 60s — same signal as MV7) + const started = Date.now() + while (Date.now() - started < 60_000) { + const burns = await api.query.tftBridgeModule.burnTransactions.entries() + const refunds = await api.query.tftBridgeModule.refundTransactions.entries() + if (burns.length === 0 && refunds.length === 0) break + await new Promise(r => setTimeout(r, 3000)) + } + + // Wait a bit more for any in-flight finalized head events + await new Promise(r => setTimeout(r, 5000)) + + let reconciled = 0 + + // Reconcile burns + for (const [burnId, events] of Object.entries(collector.burnEvents)) { + if (burnId === '_processed') continue + const hasProcessed = events.some(e => e.state === 'processed') + if (hasProcessed) continue + + try { + const executed = (await api.query.tftBridgeModule.executedBurnTransactions(Number(burnId))).toJSON() + if (executed && executed.target) { + events.push({ + block: collector._lastBlock, + timestamp: Date.now(), + index: -1, + section: 'tftBridgeModule', + method: 'BurnTransactionProcessed', + data: ['reconciled-from-chain-state'], + phase: 'reconciled', + state: 'processed', + _reconciled: true + }) + reconciled++ + } + } catch {} + } + + // Reconcile refunds + for (const [txHash, events] of Object.entries(collector.refundEvents)) { + if (txHash === '_processed') continue + const hasProcessed = events.some(e => e.state === 'processed') + if (hasProcessed) continue + + try { + const entries = await api.query.tftBridgeModule.executedRefundTransactions.entries() + const found = entries.some(([key]) => key.args[0].toString() === txHash) + if (found) { + events.push({ + block: collector._lastBlock, + timestamp: Date.now(), + index: -1, + section: 'tftBridgeModule', + method: 'RefundTransactionProcessed', + data: ['reconciled-from-chain-state'], + phase: 'reconciled', + state: 'processed', + _reconciled: true + }) + reconciled++ + } + } catch {} + } + + return reconciled +} + +// ─── Report Generator ─────────────────────────────────────────────────────── + +/** + * @param {object} collector - The event collector + * @param {string} outputPath - Path to write the JSON report + * @param {object} [api] - Optional ApiPromise for chain-state reconciliation + */ +async function generateReport (collector, outputPath, api) { + // Reconcile missing Processed events from chain state + let reconciledCount = 0 + if (api) { + reconciledCount = await reconcileFromChain(collector, api) + if (reconciledCount > 0) { + console.log(`[instrumentation] Reconciled ${reconciledCount} Processed events from chain state`) + } + } else { + // Fallback: just wait a bit for in-flight events + await new Promise(r => setTimeout(r, 3000)) + } + + const testGroups = groupEventsByTest(collector) + const anomalies = detectAnomalies(collector, testGroups) + + // Build burn analysis + const allBurnIds = Object.keys(collector.burnEvents).filter(k => k !== '_processed') + const burnRecords = allBurnIds.map(id => analyzeBurn(collector, id)).filter(Boolean) + + // Build refund analysis + const allRefundHashes = Object.keys(collector.refundEvents).filter(k => k !== '_processed') + const refundRecords = allRefundHashes.map(h => analyzeRefund(collector, h)).filter(Boolean) + + // Build mint analysis + const allMintIds = Object.keys(collector.mintEvents) + const mintRecords = allMintIds.map(id => analyzeMint(collector, id)).filter(Boolean) + + // Batch analysis + const executeBatches = analyzeBatches(collector) + const sigBatches = analyzeSignatureBatches(collector) + + // KPIs + const blocksToReady = burnRecords.map(b => b.blocksCreatedToReady).filter(v => v != null) + const blocksToProcessed = burnRecords.map(b => b.blocksCreatedToProcessed).filter(v => v != null) + const wallClockSecs = burnRecords.map(b => b.wallClockMs).filter(v => v != null).map(v => v / 1000) + + const kpis = { + burns: { + total: burnRecords.length, + avgBlocksToReady: Math.round(mean(blocksToReady) * 10) / 10, + medianBlocksToReady: median(blocksToReady), + avgBlocksToProcessed: Math.round(mean(blocksToProcessed) * 10) / 10, + medianBlocksToProcessed: median(blocksToProcessed), + p95BlocksToProcessed: percentile(blocksToProcessed, 95), + avgWallClockSecs: Math.round(mean(wallClockSecs) * 10) / 10, + totalExpiries: burnRecords.reduce((s, b) => s + b.expiryCount, 0), + burnsWithExpiry: burnRecords.filter(b => b.expiryCount > 0).length, + maxExpiryCycles: Math.max(...burnRecords.map(b => b.expiryCount), 0), + reconciledFromChain: reconciledCount + }, + refunds: { + total: refundRecords.length, + totalExpiries: refundRecords.reduce((s, r) => s + r.expiryCount, 0) + }, + mints: { + total: mintRecords.length + }, + utilityBatches: { + batchCompleted: collector.utilityEvents.filter(e => e.method === 'BatchCompleted').length, + batchCompletedWithErrors: collector.utilityEvents.filter(e => e.method === 'BatchCompletedWithErrors').length, + itemFailed: collector.utilityEvents.filter(e => e.method === 'ItemFailed').length, + itemCompleted: collector.utilityEvents.filter(e => e.method === 'ItemCompleted').length + } + } + + // Per-test summary + const perTest = {} + const testOrder = ['MV1', 'MV2', 'MV3', 'MV4', 'MV5', 'MV6', 'MV8', 'MV9', 'MV7'] + for (const test of testOrder) { + const group = testGroups[test] + if (!group) { perTest[test] = { burns: 0, refunds: 0, mints: 0, expiries: 0, blocks: 0, wallClockSecs: 0 }; continue } + + const burns = group.burns.map(id => analyzeBurn(collector, id)).filter(Boolean) + const refunds = group.refunds.map(h => analyzeRefund(collector, h)).filter(Boolean) + const mints = group.mints.map(id => analyzeMint(collector, id)).filter(Boolean) + + const allTransitions = [ + ...burns.flatMap(b => b.transitions), + ...refunds.flatMap(r => r.transitions), + ...mints.flatMap(m => m.transitions) + ] + const blocks = allTransitions.map(t => t.block).filter(Boolean) + const blockSpan = blocks.length ? Math.max(...blocks) - Math.min(...blocks) : 0 + + const timestamps = allTransitions.map(t => t.timestamp).filter(Boolean) + const timeSpan = timestamps.length ? (Math.max(...timestamps) - Math.min(...timestamps)) / 1000 : 0 + + perTest[test] = { + burns: group.burns.length, + refunds: group.refunds.length, + mints: group.mints.length, + expiries: burns.reduce((s, b) => s + b.expiryCount, 0) + refunds.reduce((s, r) => s + r.expiryCount, 0), + blocks: blockSpan, + wallClockSecs: Math.round(timeSpan) + } + } + + // Build timeline (block-by-block, only blocks with events) + const timeline = [] + const blockNums = Object.keys(collector.blockEventLog).map(Number).sort((a, b) => a - b) + for (const blockNum of blockNums) { + const events = collector.blockEventLog[blockNum] + timeline.push({ + block: blockNum, + timestamp: collector.blockTimestamps[blockNum], + events: events.map(e => ({ + section: e.section, + method: e.method, + data: e.data + })) + }) + } + + // Test phase markers (for debugging) + const phaseMarkers = collector.testPhases.map(m => ({ + test: m.test, + phase: m.phase, + block: m.block, + timestamp: m.timestamp + })) + + // Full report object + const report = { + metadata: { + generatedAt: new Date().toISOString(), + retryInterval: 20, + blockTimeSecs: 6, + validatorCount: 3, + threshold: 2, + totalBlocks: collector._lastBlock - collector._firstBlock + 1, + firstBlock: collector._firstBlock, + lastBlock: collector._lastBlock, + totalDurationSecs: Math.round((Date.now() - collector._startTime) / 1000), + reconciledFromChain: reconciledCount + }, + kpis, + perTest, + anomalies, + phaseMarkers, + batchAnalysis: { + executesBatches: executeBatches, + signatureBatches: sigBatches + }, + transactions: { + burns: burnRecords, + refunds: refundRecords, + mints: mintRecords + }, + timeline + } + + // Write JSON + fs.writeFileSync(outputPath, JSON.stringify(report, null, 2)) + + // Print human-readable summary + printSummary(report) + + return report +} + +// ─── Console Output ───────────────────────────────────────────────────────── + +function printSummary (report) { + const SEP = '─'.repeat(90) + const DSEP = '═'.repeat(90) + + console.log(`\n${DSEP}`) + console.log(' BRIDGE MV TEST ANALYSIS REPORT') + console.log(DSEP) + + // Metadata + const meta = report.metadata + console.log(` Duration: ${meta.totalDurationSecs}s | Blocks: ${meta.firstBlock}–${meta.lastBlock} (${meta.totalBlocks} blocks)`) + if (meta.reconciledFromChain > 0) { + console.log(` ⚠ ${meta.reconciledFromChain} Processed events reconciled from chain state (missed by event subscription)`) + } + console.log(SEP) + + // Per-test table + console.log(' Test │ Burns │ Refunds │ Mints │ Expiries │ Block Span │ Time') + console.log(' ' + '─'.repeat(78)) + const testOrder = ['MV1', 'MV2', 'MV3', 'MV4', 'MV5', 'MV6', 'MV8', 'MV9', 'MV7'] + for (const test of testOrder) { + const t = report.perTest[test] || {} + console.log( + ` ${test.padEnd(6)} │ ${String(t.burns || 0).padStart(5)} │ ${String(t.refunds || 0).padStart(7)} │ ${String(t.mints || 0).padStart(5)} │ ${String(t.expiries || 0).padStart(8)} │ ${String(t.blocks || 0).padStart(10)} │ ${t.wallClockSecs || 0}s` + ) + } + + console.log(SEP) + + // Burn KPIs + const bk = report.kpis.burns + console.log(' Burn KPIs:') + console.log(` Created→Ready: avg=${bk.avgBlocksToReady} blocks, median=${bk.medianBlocksToReady}`) + console.log(` Created→Processed: avg=${bk.avgBlocksToProcessed} blocks, median=${bk.medianBlocksToProcessed}, p95=${bk.p95BlocksToProcessed}`) + console.log(` Wall-clock: avg=${bk.avgWallClockSecs}s`) + console.log(` Expiries: total=${bk.totalExpiries}, burns_with_expiry=${bk.burnsWithExpiry}/${bk.total}, max_cycles=${bk.maxExpiryCycles}`) + + // Utility batch stats + const ub = report.kpis.utilityBatches + console.log(` Utility batches: completed=${ub.batchCompleted}, with_errors=${ub.batchCompletedWithErrors}, item_failed=${ub.itemFailed}, item_completed=${ub.itemCompleted}`) + + console.log(SEP) + + // Batch analysis + const ba = report.batchAnalysis + if (ba.executesBatches.length > 0) { + console.log(' SET_EXECUTED BATCH ANALYSIS:') + for (const b of ba.executesBatches) { + const status = b.failedCount > 0 + ? `✓ ${b.processedCount} processed, ${b.failedCount} failed (redundant validators)` + : `✓ ${b.processedCount} processed` + console.log(` Block ${b.block}: [${b.batchType}] ${b.type} — ${status}`) + } + console.log(SEP) + } + + if (ba.signatureBatches.length > 0) { + console.log(' SIGNATURE PROPOSAL BATCH ANALYSIS:') + for (const b of ba.signatureBatches) { + const readyTag = b.readyCount > 0 ? `, ${b.readyCount} → Ready` : '' + const failTag = b.failedCount > 0 ? `, ${b.failedCount} failed (validator race)` : '' + console.log(` Block ${b.block}: [${b.batchType}] ${b.sigCount} sigs${readyTag}${failTag}`) + } + console.log(SEP) + } + + // Anomalies + if (report.anomalies.length === 0) { + console.log(' ✅ Anomalies: none') + } else { + console.log(` Anomalies (${report.anomalies.length}):`) + for (const a of report.anomalies) { + const icon = a.severity === 'error' ? '\u274c' : a.severity === 'warning' ? '\u26a0\ufe0f' : '\u2139\ufe0f' + console.log(` ${icon} [${a.test}] ${a.message}`) + } + } + + // Phase markers (for debugging phasing issues) + console.log(`\n${DSEP}`) + console.log(' TEST PHASE MARKERS (block assignments)') + console.log(DSEP) + for (const m of report.phaseMarkers) { + console.log(` ${m.test}.${m.phase} → block ${m.block}`) + } + + // Print timeline for key tests + console.log(`\n${DSEP}`) + console.log(' BLOCK TIMELINE (bridge events only)') + console.log(DSEP) + + for (const entry of report.timeline) { + const events = entry.events + if (events.length === 0) continue + const ts = entry.timestamp ? new Date(entry.timestamp).toISOString().slice(11, 19) : '??:??:??' + console.log(` Block ${entry.block} [${ts}]:`) + for (const evt of events) { + const shortData = evt.data.map(d => d.length > 20 ? d.slice(0, 16) + '...' : d).join(', ') + console.log(` ${evt.section}.${evt.method}(${shortData})`) + } + } + + // Print per-burn lifecycle (compact) + console.log(`\n${DSEP}`) + console.log(' BURN LIFECYCLE DETAILS') + console.log(DSEP) + + for (const burn of report.transactions.burns) { + const states = burn.transitions.map(t => `${t.state}@${t.block}`).join(' → ') + const expTag = burn.expiryCount > 0 ? ` [${burn.expiryCount} expiry]` : '' + const timeTag = burn.wallClockMs != null ? ` (${Math.round(burn.wallClockMs / 1000)}s)` : '' + const reconTag = burn.transitions.some(t => t.state === 'processed') && burn.processedBlock === burn.transitions[burn.transitions.length - 1].block ? '' : '' + console.log(` Burn #${burn.burnId}: ${states}${expTag}${timeTag}${reconTag}`) + } + + for (const refund of report.transactions.refunds) { + const hash = typeof refund.txHash === 'string' ? refund.txHash.slice(0, 16) : String(refund.txHash).slice(0, 16) + const states = refund.transitions.map(t => `${t.state}@${t.block}`).join(' → ') + console.log(` Refund ${hash}...: ${states}`) + } + + for (const mint of report.transactions.mints) { + const id = typeof mint.txId === 'string' ? mint.txId.slice(0, 16) : String(mint.txId).slice(0, 16) + const states = mint.transitions.map(t => `${t.state}@${t.block}`).join(' → ') + console.log(` Mint ${id}...: ${states}`) + } + + console.log(DSEP) + console.log(` Full report: ${process.env.ANALYSIS_OUTPUT || '/tmp/bridge_mv_analysis.json'}`) + console.log(DSEP) +} + +module.exports = { + startEventCollector, + markTestPhase, + generateReport +} diff --git a/scripts/bridge_mv_accounts.js b/scripts/bridge_mv_accounts.js new file mode 100644 index 000000000..81f889bab --- /dev/null +++ b/scripts/bridge_mv_accounts.js @@ -0,0 +1,205 @@ +#!/usr/bin/env node +/** + * bridge_mv_accounts.js + * + * Sets up Stellar accounts for a 3-validator bridge dev environment. + * + * Multi-sig configuration (2-of-3): + * - Bridge account = Val1 Stellar keypair (master key, weight=1) + * - Val2 Stellar keypair added as signer (weight=1) + * - Val3 Stellar keypair added as signer (weight=1) + * - Thresholds: low=1, med=2 (any 2 of 3 can sign TFT payments), high=2 + * + * TFChain genesis bridge validators (from substrate-node/node/src/chain_spec.rs): + * Val1: "quarter between satisfy three sphere six soda boss cute decade old trend" (genesis) + * Val2: "employ split promote annual couple elder remain cricket company fitness senior fiscal" (added via council) + * Val3: "remind bird banner word spread volume card keep want faith insect mind" (added via council) + * + * Steps: + * 1. Generate keypairs: val1 (bridge master), val2, val3, user, issuer + * 2. Fund all via Stellar Friendbot + * 3. Create TFT trustline on bridge (val1) and user + * 4. Fund bridge via path_payment_strict_send (invisible to deposit monitor) + * 5. Fund user via regular payment + * 6. Configure bridge as 2-of-3 multi-sig (val1 master + val2 + val3 signers) + * 7. Write env file + * + * Usage: + * node scripts/bridge_mv_accounts.js + */ + +'use strict' + +const StellarSdk = require('@stellar/stellar-sdk') +const fs = require('fs') +const { friendbot, waitForAccount } = require('./bridge_helpers') + +const HORIZON_URL = process.env.STELLAR_HORIZON_URL || 'https://horizon-testnet.stellar.org' +const NETWORK_PASSPHRASE = StellarSdk.Networks.TESTNET +const ENV_FILE = process.env.BRIDGE_MV_ENV_FILE || '/tmp/bridge_mv_env.sh' + +const BRIDGE_TFT_FLOAT = process.env.BRIDGE_TFT_FLOAT || '20000' +const USER_TFT_AMOUNT = process.env.USER_TFT_AMOUNT || '1000' +const TFT_ASSET_CODE = 'TFT' + +const server = new StellarSdk.Horizon.Server(HORIZON_URL) + +function log (msg) { console.log(`[mv-accounts] ${msg}`) } +function die (msg) { console.error(`[mv-accounts] ERROR: ${msg}`); process.exit(1) } + +async function submitTx (kp, acc, ops) { + const builder = new StellarSdk.TransactionBuilder(acc, { + fee: '1000', + networkPassphrase: NETWORK_PASSPHRASE + }) + for (const op of ops) builder.addOperation(op) + const tx = builder.setTimeout(30).build() + tx.sign(kp) + return server.submitTransaction(tx) +} + +async function main () { + // 1. Generate keypairs + const val1Kp = process.env.VAL1_STELLAR_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.VAL1_STELLAR_SECRET) + : StellarSdk.Keypair.random() + + const val2Kp = process.env.VAL2_STELLAR_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.VAL2_STELLAR_SECRET) + : StellarSdk.Keypair.random() + + const val3Kp = process.env.VAL3_STELLAR_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.VAL3_STELLAR_SECRET) + : StellarSdk.Keypair.random() + + const userKp = process.env.USER_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.USER_SECRET) + : StellarSdk.Keypair.random() + + const issuerKp = process.env.ISSUER_SECRET + ? StellarSdk.Keypair.fromSecret(process.env.ISSUER_SECRET) + : StellarSdk.Keypair.random() + + const bridgeAddress = val1Kp.publicKey() + + log(`Issuer: ${issuerKp.publicKey()}`) + log(`Val1 / Bridge: ${val1Kp.publicKey()}`) + log(`Val2: ${val2Kp.publicKey()}`) + log(`Val3: ${val3Kp.publicKey()}`) + log(`User: ${userKp.publicKey()}`) + + const TFT = new StellarSdk.Asset(TFT_ASSET_CODE, issuerKp.publicKey()) + + // 2. Fund via Friendbot + log('Funding accounts via Friendbot...') + await Promise.all([ + friendbot(issuerKp.publicKey()), + friendbot(val1Kp.publicKey()), + friendbot(val2Kp.publicKey()), + friendbot(val3Kp.publicKey()), + friendbot(userKp.publicKey()) + ]) + log('Friendbot done. Waiting for accounts...') + + const [, bridgeAcc, , , userAcc] = await Promise.all([ + waitForAccount(issuerKp.publicKey(), server), + waitForAccount(val1Kp.publicKey(), server), + waitForAccount(val2Kp.publicKey(), server), + waitForAccount(val3Kp.publicKey(), server), + waitForAccount(userKp.publicKey(), server) + ]) + + // 3. TFT trustlines on bridge and user only (val2/val3 are signers, not holders) + log('Creating TFT trustlines on bridge and user...') + await Promise.all([ + submitTx(val1Kp, bridgeAcc, [StellarSdk.Operation.changeTrust({ asset: TFT })]), + submitTx(userKp, userAcc, [StellarSdk.Operation.changeTrust({ asset: TFT })]) + ]) + log('Trustlines created.') + + const [issuerAcc2] = await Promise.all([ + waitForAccount(issuerKp.publicKey(), server), + waitForAccount(val1Kp.publicKey(), server), + waitForAccount(val2Kp.publicKey(), server), + waitForAccount(val3Kp.publicKey(), server), + waitForAccount(userKp.publicKey(), server) + ]) + + // 4. Fund bridge via path_payment_strict_send (invisible to deposit monitor) + log(`Issuing ${BRIDGE_TFT_FLOAT} TFT to bridge via path_payment_strict_send...`) + await submitTx(issuerKp, issuerAcc2, [ + StellarSdk.Operation.pathPaymentStrictSend({ + sendAsset: TFT, + sendAmount: BRIDGE_TFT_FLOAT, + destination: bridgeAddress, + destAsset: TFT, + destMin: String(Number(BRIDGE_TFT_FLOAT) - 1), + path: [] + }) + ]) + log(`Bridge funded with ${BRIDGE_TFT_FLOAT} TFT.`) + + // 5. Fund user + const issuerAcc3 = await waitForAccount(issuerKp.publicKey(), server) + log(`Issuing ${USER_TFT_AMOUNT} TFT to user...`) + await submitTx(issuerKp, issuerAcc3, [ + StellarSdk.Operation.payment({ + destination: userKp.publicKey(), + asset: TFT, + amount: USER_TFT_AMOUNT + }) + ]) + log(`User funded with ${USER_TFT_AMOUNT} TFT.`) + + // 6. Configure bridge as 2-of-3 multi-sig + // Val1 is master (weight=1), val2 and val3 added as signers (weight=1 each) + // Any 2 of 3 signers needed for med ops (TFT payments) + const bridgeAcc3 = await waitForAccount(val1Kp.publicKey(), server) + log('Configuring bridge as 2-of-3 multi-sig (low=1, med=2, high=2)...') + await submitTx(val1Kp, bridgeAcc3, [ + StellarSdk.Operation.setOptions({ + signer: { ed25519PublicKey: val2Kp.publicKey(), weight: 1 } + }), + StellarSdk.Operation.setOptions({ + signer: { ed25519PublicKey: val3Kp.publicKey(), weight: 1 } + }), + StellarSdk.Operation.setOptions({ + lowThreshold: 1, + medThreshold: 2, + highThreshold: 2 + }) + ]) + + const finalAcc = await waitForAccount(bridgeAddress, server) + log(`Bridge thresholds: low=${finalAcc.thresholds.low_threshold} med=${finalAcc.thresholds.med_threshold} high=${finalAcc.thresholds.high_threshold}`) + log(`Bridge signers: ${finalAcc.signers.length} (expected 3: val1 master + val2 + val3)`) + + // 7. Write env file + const envContent = `# Auto-generated by bridge_mv_accounts.js — do not edit manually +# Multi-sig: 2-of-3 (val1 master + val2 + val3 all weight=1, med threshold=2) +# TFChain validator seeds (from substrate-node/node/src/chain_spec.rs): +# Val1: "quarter between satisfy three sphere six soda boss cute decade old trend" (genesis) +# Val2: "employ split promote annual couple elder remain cricket company fitness senior fiscal" (added via council) +# Val3: "remind bird banner word spread volume card keep want faith insect mind" (added via council) +export ISSUER_ADDRESS="${issuerKp.publicKey()}" +export ISSUER_SECRET="${issuerKp.secret()}" +export BRIDGE_ADDRESS="${bridgeAddress}" +export VAL1_STELLAR_SECRET="${val1Kp.secret()}" +export VAL1_STELLAR_ADDRESS="${val1Kp.publicKey()}" +export VAL2_STELLAR_SECRET="${val2Kp.secret()}" +export VAL2_STELLAR_ADDRESS="${val2Kp.publicKey()}" +export VAL3_STELLAR_SECRET="${val3Kp.secret()}" +export VAL3_STELLAR_ADDRESS="${val3Kp.publicKey()}" +export USER_ADDRESS="${userKp.publicKey()}" +export USER_SECRET="${userKp.secret()}" +export TFT_ASSET_CODE="${TFT_ASSET_CODE}" +export STELLAR_HORIZON_URL="${HORIZON_URL}" +export STELLAR_NETWORK="testnet" +export MV_MED_THRESHOLD="2" +` + fs.writeFileSync(ENV_FILE, envContent) + log(`Environment written to ${ENV_FILE}`) + log('Done.') +} + +main().catch(e => die(e.message || String(e))) diff --git a/scripts/bridge_mv_setup.js b/scripts/bridge_mv_setup.js new file mode 100644 index 000000000..e96155a49 --- /dev/null +++ b/scripts/bridge_mv_setup.js @@ -0,0 +1,185 @@ +#!/usr/bin/env node +/** + * bridge_mv_setup.js + * + * Configures TFChain for multi-validator bridge dev testing. + * + * Genesis pre-configures only validator 1 (dev key 1). This script uses + * council governance (Alice + Bob are genesis council members, 2-of-2) to + * add validators 2 and 3 to the bridge validator set. + * + * TFChain bridge validator dev seeds (from chain_spec.rs): + * Val1: "quarter between satisfy three sphere six soda boss cute decade old trend" (genesis) + * Val2: "employ split promote annual couple elder remain cricket company fitness senior fiscal" (added here) + * Val3: "remind bird banner word spread volume card keep want faith insect mind" (added here) + * + * Council flow per validator: Alice proposes → Bob votes yes → Alice votes yes → Alice closes. + * + * Usage: + * node scripts/bridge_mv_setup.js + */ + +'use strict' + +const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api') + +const TFCHAIN_URL = process.env.TFCHAIN_URL || 'ws://localhost:9944' + +// Bridge validator dev seeds — read from env (set by Makefile) with hardcoded defaults as fallback +const VAL2_SEED = process.env.VAL2_TFCHAIN_SEED || 'employ split promote annual couple elder remain cricket company fitness senior fiscal' +const VAL3_SEED = process.env.VAL3_TFCHAIN_SEED || 'remind bird banner word spread volume card keep want faith insect mind' + +function log (msg) { console.log(`[mv-setup] ${msg}`) } +function die (msg) { console.error(`[mv-setup] FATAL: ${msg}`); process.exit(1) } + +/** Sign a tx, wait for InBlock, and throw on dispatch error */ +function signAndWait (api, tx, signer) { + return new Promise((resolve, reject) => { + let unsub + tx.signAndSend(signer, ({ status, dispatchError, events }) => { + if (!status.isInBlock && !status.isFinalized) return + if (dispatchError) { + let msg = dispatchError.toString() + if (dispatchError.isModule) { + try { + const decoded = api.registry.findMetaError(dispatchError.asModule) + msg = `${decoded.section}.${decoded.name}: ${decoded.docs}` + } catch {} + } + if (unsub) unsub() + reject(new Error(msg)) + return + } + if (unsub) unsub() + resolve({ status, events }) + }).then(u => { unsub = u }).catch(reject) + }) +} + +/** Wait for N new blocks */ +async function waitBlocks (api, n = 1) { + return new Promise((resolve) => { + let count = 0 + const unsubPromise = api.rpc.chain.subscribeNewHeads(() => { + count++ + if (count >= n) { + unsubPromise.then(unsub => unsub()) + resolve() + } + }) + }) +} + +/** Add a bridge validator via council governance (Alice proposes, both vote, Alice closes) */ +async function addValidatorViaCouncil (api, alice, bob, validatorAddress, label) { + // Check if already registered + const validators = await api.query.tftBridgeModule.validators() + const valList = validators.toHuman() + log(`Current validators: ${JSON.stringify(valList)}`) + + if (valList.includes(validatorAddress)) { + log(`${label} (${validatorAddress}) already registered. Skipping.`) + return + } + + log(`Adding ${label} (${validatorAddress}) via council governance...`) + + // Build the addBridgeValidator call + const addValCall = api.tx.tftBridgeModule.addBridgeValidator(validatorAddress) + const encodedCall = addValCall.method.toHex() + const callLen = encodedCall.length / 2 - 1 // bytes + + // Alice proposes with threshold=2 (Alice + Bob must both vote) + log(`Alice proposing addBridgeValidator(${label})...`) + const { events: proposeEvents } = await signAndWait( + api, + api.tx.council.propose(2, addValCall, callLen), + alice + ) + + // Extract proposal hash and index from Proposed event + let proposalHash, proposalIndex + for (const { event } of proposeEvents) { + if (api.events.council.Proposed.is(event)) { + proposalHash = event.data[2].toHex() + proposalIndex = event.data[1].toNumber() + break + } + } + if (!proposalHash) die('Could not extract proposal hash from Proposed event') + log(`Proposal: hash=${proposalHash.slice(0, 12)}... index=${proposalIndex}`) + + // Bob votes yes + log('Bob voting yes...') + await signAndWait(api, api.tx.council.vote(proposalHash, proposalIndex, true), bob) + log('Bob voted yes.') + + // Alice votes yes + log('Alice voting yes...') + await signAndWait(api, api.tx.council.vote(proposalHash, proposalIndex, true), alice) + log('Alice voted yes.') + + // Close the proposal (executes the call) + log('Closing proposal...') + const maxWeight = { refTime: BigInt(1_000_000_000), proofSize: BigInt(1_000_000) } + await signAndWait(api, api.tx.council.close(proposalHash, proposalIndex, maxWeight, callLen), alice) + log('Proposal closed.') + + // Verify + await waitBlocks(api, 1) + const newValidators = await api.query.tftBridgeModule.validators() + const newValList = newValidators.toHuman() + log(`Updated validators: ${JSON.stringify(newValList)}`) + + if (!newValList.includes(validatorAddress)) { + die(`${label} was not added — council call may have failed`) + } + log(`${label} ✓ successfully added as bridge validator.`) +} + +async function main () { + log(`Connecting to TFChain at ${TFCHAIN_URL}...`) + const api = await ApiPromise.create({ provider: new WsProvider(TFCHAIN_URL) }) + + try { + const keyring = new Keyring({ type: 'sr25519' }) + + const alice = keyring.addFromUri('//Alice') + const bob = keyring.addFromUri('//Bob') + const val2 = keyring.addFromUri(VAL2_SEED) + const val3 = keyring.addFromUri(VAL3_SEED) + + log(`Val2 address: ${val2.address}`) + log(`Val3 address: ${val3.address}`) + + // Add val2 via council governance + await addValidatorViaCouncil(api, alice, bob, val2.address, 'Val2') + + // Add val3 via council governance + await addValidatorViaCouncil(api, alice, bob, val3.address, 'Val3') + + // Create Alice's twin (needed for MV2 deposit test) + // Alice must accept T&C before creating a twin + const aliceTwinOpt = await api.query.tfgridModule.twinIdByAccountID(alice.address) + const aliceTwinId = aliceTwinOpt.toJSON() + if (!aliceTwinId) { + log('Accepting T&C and creating Alice twin for deposit tests...') + await signAndWait(api, api.tx.tfgridModule.userAcceptTc('https://localhost/tc', 'deadbeef'), alice) + await signAndWait(api, api.tx.tfgridModule.createTwin(null, null), alice) + const newTwin = await api.query.tfgridModule.twinIdByAccountID(alice.address) + log(`Alice twin created (ID: ${newTwin.toJSON()})`) + } else { + log(`Alice twin already exists (ID: ${aliceTwinId})`) + } + + // Final state + const finalValidators = await api.query.tftBridgeModule.validators() + log(`Final validators: ${JSON.stringify(finalValidators.toHuman())}`) + + log('Setup complete.') + } finally { + await api.disconnect() + } +} + +main().catch(e => die(e.message || String(e))) diff --git a/scripts/bridge_mv_tests.js b/scripts/bridge_mv_tests.js new file mode 100644 index 000000000..98ff4be9c --- /dev/null +++ b/scripts/bridge_mv_tests.js @@ -0,0 +1,728 @@ +#!/usr/bin/env node +/** + * bridge_mv_tests.js + * + * Multi-validator E2E test suite for the TFChain bridge. + * Assumes 3 bridge daemons running (Val1=genesis-key-1, Val2=genesis-key-2, Val3=genesis-key-3), + * bridge Stellar account configured as 2-of-3 multi-sig (threshold=2). + * Val2 and Val3 are added via council governance in bridge-mv-setup. + * + * Tests (run sequentially): + * MV1 — Normal withdraw: 3 validators, 2-of-3 signatures, 1 TFT delivered + * MV2 — Deposit/mint: send TFT with valid memo, all 3 propose mint, threshold met + * MV3 — Bad deposit: no memo, all 3 detect and propose refund, full refund delivered + * MV4 — Validator offline: kill Val3 before deposit, Val1+Val2 alone complete refund (2-of-3) + * MV5 — Batch withdraws: 5 simultaneous swaps, all 5 eventually delivered (may use expiry) + * MV6 — Crash recovery: kill Val2 mid-withdraw, restart, verify delivery completes + * MV6a— Below-minimum: swap below fee, expect dispatch error (parity with SV test6) + * MV8 — Lost cursor: wipe all 3 persistency files, restart, verify no double-spend, + * then deposit to prove bridge is at Stellar tip (tx hash verified) + * MV9 — Expired batch: 50 swaps while all validators offline, wait for expiry, restart, all delivered + * MV7 — Clean state: verify no orphaned active transactions on-chain + * + * All tests assert exact TFT balances (Stellar + TFChain) and on-chain state. + * Non-zero exit on any failure. + * + * Usage: + * node scripts/bridge_mv_tests.js + */ + +'use strict' + +const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api') +const StellarSdk = require('@stellar/stellar-sdk') +const fs = require('fs') +const { spawn } = require('child_process') +const { + log, pass, fail, + loadEnv, getEnv, + stellarTFTBalance, + tfchainBalance, + waitUntil, + sendStellarPayment, + swapToStellar, + TFT_DECIMALS +} = require('./bridge_helpers') +const { startEventCollector, markTestPhase, generateReport } = require('./bridge_instrumentation') + +const TFCHAIN_URL = process.env.TFCHAIN_URL || 'ws://localhost:9944' +const HORIZON_URL = process.env.STELLAR_HORIZON_URL || 'https://horizon-testnet.stellar.org' +const NETWORK_PASSPHRASE = StellarSdk.Networks.TESTNET +const ENV_FILE = process.env.BRIDGE_MV_ENV_FILE || '/tmp/bridge_mv_env.sh' +const BRIDGE_BIN = process.env.BRIDGE_BIN || './bridge/tfchain_bridge/tfchain_bridge_local' +const BRIDGE_DIR = process.env.BRIDGE_DIR || './bridge/tfchain_bridge' + +const VAL_PID_FILES = [1, 2, 3].map(i => `/tmp/bridge_mv_${i}.pid`) +const VAL_LOG_FILES = [1, 2, 3].map(i => `/tmp/bridge_mv_${i}.log`) + +const WITHDRAW_FEE_TFT = 1 + +const counter = { passed: 0, failed: 0 } +let api, alice, horizon, issuerAddress, bridgeAddress, collector + +// ─── Validator lifecycle helpers ──────────────────────────────────────────── + +function getValPid (valIndex) { + const pidFile = VAL_PID_FILES[valIndex - 1] + if (!fs.existsSync(pidFile)) return null + return parseInt(fs.readFileSync(pidFile, 'utf8').trim()) +} + +function killValidator (valIndex, signal = 'SIGKILL') { + const pid = getValPid(valIndex) + if (pid) { + try { process.kill(pid, signal); log(`Val${valIndex} (PID ${pid}) killed`) } catch {} + } +} + +// Bridge validator dev seeds (from substrate-node/node/src/chain_spec.rs) +// Read from env vars (set by Makefile) with hardcoded defaults as fallback. +const VAL_TFCHAIN_SEEDS = [ + process.env.VAL1_TFCHAIN_SEED || 'quarter between satisfy three sphere six soda boss cute decade old trend', + process.env.VAL2_TFCHAIN_SEED || 'employ split promote annual couple elder remain cricket company fitness senior fiscal', + process.env.VAL3_TFCHAIN_SEED || 'remind bird banner word spread volume card keep want faith insect mind' +] + +function startValidator (valIndex) { + const secrets = ['VAL1_STELLAR_SECRET', 'VAL2_STELLAR_SECRET', 'VAL3_STELLAR_SECRET'] + const secret = getEnv(secrets[valIndex - 1]) + const seed = VAL_TFCHAIN_SEEDS[valIndex - 1] + const persistency = `${BRIDGE_DIR}/signer_mv_${valIndex}.json` + const logFile = VAL_LOG_FILES[valIndex - 1] + + // Use shell exec redirect — direct fd inheritance is unreliable on macOS after child.unref() + const cmd = [ + BRIDGE_BIN, + '--secret', secret, + '--tfchainurl', TFCHAIN_URL, + '--tfchainseed', `"${seed}"`, + '--bridgewallet', bridgeAddress, + '--persistency', persistency, + '--network', 'local' + ].join(' ') + + const child = spawn('/bin/sh', ['-c', `exec ${cmd} >> ${logFile} 2>&1`], { + detached: true, + stdio: 'ignore' + }) + child.unref() + fs.writeFileSync(VAL_PID_FILES[valIndex - 1], String(child.pid)) + log(`Val${valIndex} restarted (PID ${child.pid})`) +} + +// ─── On-chain assertion helpers ───────────────────────────────────────────── + +/** + * Poll until a burn tx moves to ExecutedBurnTransactions (not stuck in active map). + * Waits up to 30s for the on-chain state to settle — set_burn_transaction_executed + * may finalize a few blocks after the Stellar payment is visible. + */ +async function assertBurnExecuted (name, burnId) { + try { + await waitUntil(async () => { + const active = (await api.query.tftBridgeModule.burnTransactions(burnId)).toJSON() + if (active && active.target) return false + const executed = (await api.query.tftBridgeModule.executedBurnTransactions(burnId)).toJSON() + return executed && executed.target + }, { timeoutMs: 30_000, intervalMs: 3000, desc: `burn ${burnId} to reach ExecutedBurnTransactions` }) + return true + } catch { + const active = (await api.query.tftBridgeModule.burnTransactions(burnId)).toJSON() + if (active && active.target) { + fail(name, `burn ${burnId} still in active BurnTransactions after 30s`, counter) + } else { + fail(name, `burn ${burnId} not in ExecutedBurnTransactions after 30s`, counter) + } + return false + } +} + +/** + * Poll until at least one new refund reaches ExecutedRefundTransactions since `countBefore`. + * Waits up to 30s — set_refund_transaction_executed may finalize after the Stellar refund. + */ +async function assertRefundExecuted (name, countBefore) { + try { + await waitUntil(async () => { + const after = await api.query.tftBridgeModule.executedRefundTransactions.entries() + return after.length > countBefore + }, { timeoutMs: 30_000, intervalMs: 3000, desc: 'new refund in ExecutedRefundTransactions' }) + return true + } catch { + const after = await api.query.tftBridgeModule.executedRefundTransactions.entries() + fail(name, `no new refund in ExecutedRefundTransactions after 30s (before: ${countBefore}, after: ${after.length})`, counter) + return false + } +} + +// ─── Tests ──────────────────────────────────────────────────────────────────── + +async function testMV1_normalWithdraw () { + console.log('\n── MV1: Normal withdraw (3 validators, threshold=2) ──') + markTestPhase(collector, 'MV1', 'start') + const name = 'MV1_normalWithdraw' + const userAddress = getEnv('USER_ADDRESS') + const swapAmount = 2 + + try { + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const beforeTFChain = await tfchainBalance(api, alice.address) + log(`User Stellar TFT before: ${beforeStellar}`) + log(`Alice TFChain TFT before: ${beforeTFChain}`) + + const burnId = await swapToStellar(api, alice, swapAmount, { userAddress }) + log(`Burn ID: ${burnId}`) + + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal > beforeStellar) return bal + }, { timeoutMs: 300_000, intervalMs: 4000, desc: 'Stellar balance to increase' }) + + // Assert Stellar balance delta + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + const expected = swapAmount - WITHDRAW_FEE_TFT + log(`User Stellar TFT after: ${afterStellar} (+${delta} TFT)`) + if (Math.abs(delta - expected) > 1e-7) { + fail(name, `Expected Stellar +${expected} TFT, got +${delta}`, counter); return + } + + // Assert TFChain balance decreased by ~swapAmount (± 0.1 TFT for extrinsic fee). + const afterTFChain = await tfchainBalance(api, alice.address) + const tfDelta = Math.round((beforeTFChain - afterTFChain) * TFT_DECIMALS) / TFT_DECIMALS + log(`Alice TFChain TFT after: ${afterTFChain} (-${tfDelta} TFT)`) + if (Math.abs(tfDelta - swapAmount) > 0.1) { + fail(name, `TFChain balance should decrease by ~${swapAmount} (±0.1), decreased by ${tfDelta}`, counter); return + } + + // Assert on-chain: burn executed + if (!(await assertBurnExecuted(name, burnId))) return + + pass(name, counter) + } catch (e) { fail(name, e.message, counter) } + finally { markTestPhase(collector, 'MV1', 'end') } +} + +async function testMV2_deposit () { + console.log('\n── MV2: Deposit/mint (3 validators all propose) ──') + markTestPhase(collector, 'MV2', 'start') + const name = 'MV2_deposit' + const aliceAddress = alice.address + + try { + // Get Alice's twin ID — twinIdByAccountID returns Option + const twinOpt = await api.query.tfgridModule.twinIdByAccountID(aliceAddress) + const twinId = twinOpt.isSome ? twinOpt.unwrap().toNumber() : twinOpt.toJSON() + if (!twinId) throw new Error('Alice has no twin on TFChain — is bridge-setup complete?') + log(`Alice twin ID: ${twinId}`) + + const depositAmount = '2' + const depositFee = Number(await api.query.tftBridgeModule.depositFee()) / TFT_DECIMALS + const expectedMint = parseFloat(depositAmount) - depositFee + log(`Deposit fee: ${depositFee} TFT, expected mint: ${expectedMint} TFT`) + + const aliceBalBefore = await tfchainBalance(api, aliceAddress) + const mintsBefore = (await api.query.tftBridgeModule.executedMintTransactions.entries()).length + log(`Alice TFChain TFT before: ${aliceBalBefore}, executed mints: ${mintsBefore}`) + + // Memo format must be "twin_" (bridge parses "object_objectID") + const result = await sendStellarPayment( + horizon, issuerAddress, NETWORK_PASSPHRASE, + getEnv('USER_SECRET'), + bridgeAddress, + depositAmount, + `twin_${twinId}` + ) + log(`Deposit sent: ${result.hash.slice(0, 16)} (memo: twin_${twinId})`) + + // Wait for mint to be executed on TFChain + const mintsAfter = await waitUntil(async () => { + const mints = await api.query.tftBridgeModule.executedMintTransactions.entries() + if (mints.length > mintsBefore) return mints + }, { timeoutMs: 120_000, intervalMs: 4000, desc: 'executed mint count to increase' }) + + log(`Executed mints after: ${mintsAfter.length}`) + + // Assert Alice's TFChain balance increased by ~expectedMint (± 0.1 TFT for block author rewards). + const aliceBalAfter = await tfchainBalance(api, aliceAddress) + const balDelta = Math.round((aliceBalAfter - aliceBalBefore) * TFT_DECIMALS) / TFT_DECIMALS + log(`Alice TFChain TFT after: ${aliceBalAfter} (+${balDelta} TFT)`) + if (Math.abs(balDelta - expectedMint) > 0.1) { + fail(name, `Expected TFChain ~+${expectedMint} TFT (±0.1), got +${balDelta}`, counter); return + } + + pass(name, counter) + } catch (e) { fail(name, e.message, counter) } + finally { markTestPhase(collector, 'MV2', 'end') } +} + +async function testMV3_badDeposit () { + console.log('\n── MV3: Bad deposit (no memo -> full refund, 3 validators) ──') + markTestPhase(collector, 'MV3', 'start') + const name = 'MV3_badDeposit' + const userAddress = getEnv('USER_ADDRESS') + + try { + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const refundsBefore = (await api.query.tftBridgeModule.executedRefundTransactions.entries()).length + log(`User Stellar TFT before: ${beforeStellar}`) + + const result = await sendStellarPayment(horizon, issuerAddress, NETWORK_PASSPHRASE, getEnv('USER_SECRET'), bridgeAddress, '3') + log(`Bad deposit sent: ${result.hash.slice(0, 16)} (no memo)`) + + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal >= beforeStellar - 1e-7) return bal + }, { timeoutMs: 180_000, intervalMs: 4000, desc: 'balance restored after refund' }) + + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`User Stellar TFT after: ${afterStellar} (delta: ${delta >= 0 ? '+' : ''}${delta})`) + + if (Math.abs(delta) > 1e-7) { + fail(name, `Expected net 0 (full refund), got ${delta >= 0 ? '+' : ''}${delta}`, counter); return + } + + // Assert on-chain: refund executed + if (!(await assertRefundExecuted(name, refundsBefore))) return + + pass(name, counter) + } catch (e) { fail(name, e.message, counter) } + finally { markTestPhase(collector, 'MV3', 'end') } +} + +async function testMV4_validatorOffline () { + console.log('\n── MV4: Val3 offline — Val1+Val2 complete refund with 2-of-3 threshold ──') + markTestPhase(collector, 'MV4', 'start') + const name = 'MV4_validatorOffline' + const userAddress = getEnv('USER_ADDRESS') + + try { + // Kill Val3 before the deposit — threshold=2, so Val1+Val2 alone can complete + killValidator(3) + await new Promise(r => setTimeout(r, 2000)) + + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const refundsBefore = (await api.query.tftBridgeModule.executedRefundTransactions.entries()).length + log(`Val3 killed. User Stellar TFT before: ${beforeStellar}`) + + // Send bad deposit (no memo) — Val1+Val2 detect it, propose refund, threshold=2 met + const result = await sendStellarPayment(horizon, issuerAddress, NETWORK_PASSPHRASE, getEnv('USER_SECRET'), bridgeAddress, '4') + log(`Bad deposit sent: ${result.hash.slice(0, 16)} (no memo, Val3 offline)`) + + // Wait for refund to complete — Val1+Val2 have enough signatures (2-of-3) + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal >= beforeStellar - 1e-7) return bal + }, { timeoutMs: 180_000, intervalMs: 4000, desc: 'balance restored with Val3 offline' }) + + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`User Stellar TFT after: ${afterStellar} (delta: ${delta >= 0 ? '+' : ''}${delta})`) + + if (Math.abs(delta) > 1e-7) { + fail(name, `Expected net 0 (full refund), got ${delta >= 0 ? '+' : ''}${delta}`, counter) + } else if (!(await assertRefundExecuted(name, refundsBefore))) { + // assertRefundExecuted polls for 30s and logs failure itself + } else { + pass(name, counter) + } + } catch (e) { + fail(name, e.message, counter) + } finally { + // Restart Val3 for subsequent tests — best effort with fixed startup window. + try { + log('Restarting Val3 for subsequent tests...') + startValidator(3) + await new Promise(r => setTimeout(r, 8000)) + log('Val3 restarted.') + } catch (restartErr) { + log(`Warning: Val3 restart failed: ${restartErr.message}`) + } + markTestPhase(collector, 'MV4', 'end') + } +} + +async function testMV5_batchWithdraws () { + console.log('\n── MV5: Batch withdraws (5 simultaneous, all validators) ──') + markTestPhase(collector, 'MV5', 'start') + const name = 'MV5_batchWithdraws' + const userAddress = getEnv('USER_ADDRESS') + + try { + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before: ${beforeStellar}`) + + const nonce = await api.rpc.system.accountNextIndex(alice.address) + const burnIds = await Promise.all( + [0, 1, 2, 3, 4].map(i => swapToStellar(api, alice, 2, { userAddress, nonce: nonce.toNumber() + i })) + ) + log(`Burn IDs: ${burnIds.join(', ')}`) + + const expectedNet = 5 * (2 - WITHDRAW_FEE_TFT) + + // Use longer timeout — sequence collisions may require expiry cycle (~2 min each) + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal >= beforeStellar + expectedNet - 1e-7) return bal + }, { timeoutMs: 600_000, intervalMs: 4000, desc: `balance >= ${beforeStellar + expectedNet} (may need expiry cycles)` }) + + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`User Stellar TFT after: ${afterStellar} (+${delta}, expected +${expectedNet})`) + if (Math.abs(delta - expectedNet) > 1e-7) { + fail(name, `Expected +${expectedNet}, got +${delta}`, counter); return + } + + // Assert on-chain: all burns executed + for (const burnId of burnIds) { + if (!(await assertBurnExecuted(name, burnId))) return + } + + pass(name, counter) + } catch (e) { fail(name, e.message, counter) } + finally { markTestPhase(collector, 'MV5', 'end') } +} + +async function testMV6_crashRecovery () { + console.log('\n── MV6: Crash recovery (kill Val2 mid-withdraw, restart, verify delivery) ──') + markTestPhase(collector, 'MV6', 'start') + const name = 'MV6_crashRecovery' + const userAddress = getEnv('USER_ADDRESS') + + try { + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before: ${beforeStellar}`) + + const burnId = await swapToStellar(api, alice, 2, { userAddress }) + log(`Burn ID: ${burnId}`) + + // Wait for at least 1 signature (proposals submitted) + await waitUntil(async () => { + const burn = (await api.query.tftBridgeModule.burnTransactions(burnId)).toJSON() + return burn && burn.signatures && burn.signatures.length >= 1 + }, { timeoutMs: 60_000, desc: 'BurnTransactionReady (>=1 sig)' }) + + // Kill Val2 mid-flight + killValidator(2, 'SIGKILL') + log('Val2 killed. Waiting 3s...') + await new Promise(r => setTimeout(r, 3000)) + + // Restart Val2 + startValidator(2) + log('Val2 restarted. Waiting 10s for startup...') + await new Promise(r => setTimeout(r, 10_000)) + + // Val1+Val3 should complete it (2-of-3), or Val2 reconciles after restart + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal > beforeStellar) return bal + }, { timeoutMs: 300_000, intervalMs: 4000, desc: 'Stellar balance to increase after crash' }) + + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + const expected = 2 - WITHDRAW_FEE_TFT + log(`User Stellar TFT after: ${afterStellar} (+${delta} TFT)`) + + if (Math.abs(delta - expected) > 1e-7) { + fail(name, `Expected +${expected} TFT after recovery, got +${delta}`, counter); return + } + + // Assert on-chain: burn executed + if (!(await assertBurnExecuted(name, burnId))) return + + pass(name, counter) + } catch (e) { fail(name, e.message, counter) } + finally { markTestPhase(collector, 'MV6', 'end') } +} + +async function testMV6a_belowMinimum () { + console.log('\n── MV6a: Withdraw below minimum (should be rejected) ──') + markTestPhase(collector, 'MV6a', 'start') + const name = 'MV6a_belowMinimum' + try { + await swapToStellar(api, alice, 0.5, { userAddress: getEnv('USER_ADDRESS') }) + fail(name, 'swapToStellar should have thrown, but succeeded', counter) + } catch (e) { + if (e.message.includes('AmountIsLessThanWithdrawFee')) { + log(`Correctly rejected: ${e.message}`) + pass(name, counter) + } else { + fail(name, `Expected AmountIsLessThanWithdrawFee, got: ${e.message}`, counter) + } + } + markTestPhase(collector, 'MV6a', 'end') +} + +async function testMV8_lostCursor () { + console.log('\n── MV8: Lost cursor (wipe all 3 persistency files → no double-spend) ──') + markTestPhase(collector, 'MV8', 'start') + const name = 'MV8_lostCursor' + const userAddress = getEnv('USER_ADDRESS') + + try { + // Kill all 3 validators + for (let i = 1; i <= 3; i++) killValidator(i) + await new Promise(r => setTimeout(r, 2000)) + + // Wipe all 3 persistency files. + // Without the cursor, bridges re-scan the Stellar account from the beginning. + // Protection against duplicate burns: IsBurnedAlready (ExecutedBurnTransactions) + // Protection against duplicate mints: IsMintedAlready (ExecutedMintTransactions) + for (let i = 1; i <= 3; i++) { + const p = `${BRIDGE_DIR}/signer_mv_${i}.json` + if (fs.existsSync(p)) { + fs.unlinkSync(p) + log(`Wiped: ${p}`) + } + } + + // Snapshot Stellar balance — should NOT change after restart + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before restart: ${beforeStellar}`) + + // Restart all 3 validators — they rescan with no local state + for (let i = 1; i <= 3; i++) startValidator(i) + log('All 3 validators restarted with wiped cursors. Waiting 15s for rescan...') + await new Promise(r => setTimeout(r, 15_000)) + + // Verify no double-spend — balance must be unchanged + const afterStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`User Stellar TFT after rescan: ${afterStellar} (delta: ${delta >= 0 ? '+' : ''}${delta})`) + if (Math.abs(delta) > 1e-7) { + fail(name, `DOUBLE-SPEND: balance changed by ${delta} TFT after cursor wipe`, counter); return + } + log('No double-spend — IsMintedAlready + IsBurnedAlready held during rescan') + + // ─── Hardened: fresh DEPOSIT to prove bridge is at Stellar tip ─────── + // + // A withdraw (old approach) is event-driven from TFChain — it doesn't use + // the Stellar cursor at all, so it doesn't prove the bridge finished scanning. + // + // A deposit proves the bridge has caught up to the TIP of the Stellar account, + // because the bridge must reach our new transaction in the Horizon stream. + // + // Extra hardening: we verify the on-chain mint's tx_id matches our Stellar + // deposit hash, ruling out the case where an OLD deposit was re-processed + // and our new deposit is still un-seen. + log('Running fresh deposit to verify bridge scanned to Stellar tip...') + + const twinOpt = await api.query.tfgridModule.twinIdByAccountID(alice.address) + const twinId = twinOpt.isSome ? twinOpt.unwrap().toNumber() : twinOpt.toJSON() + if (!twinId) throw new Error('Alice has no twin on TFChain — is bridge-setup complete?') + + const depositAmount = '2' + const depositFee = Number(await api.query.tftBridgeModule.depositFee()) / TFT_DECIMALS + const expectedMint = parseFloat(depositAmount) - depositFee + log(`Deposit fee: ${depositFee} TFT, expected mint: ${expectedMint} TFT`) + + const aliceBalBefore = await tfchainBalance(api, alice.address) + const mintsBefore = (await api.query.tftBridgeModule.executedMintTransactions.entries()).length + log(`Alice TFChain TFT before: ${aliceBalBefore}, executed mints: ${mintsBefore}`) + + // Send deposit — capture the Stellar tx hash for verification + const result = await sendStellarPayment( + horizon, issuerAddress, NETWORK_PASSPHRASE, + getEnv('USER_SECRET'), + bridgeAddress, + depositAmount, + `twin_${twinId}` + ) + const depositTxHash = result.hash + log(`Fresh deposit sent: ${depositTxHash} (memo: twin_${twinId})`) + + // Wait for the mint to appear on-chain + await waitUntil(async () => { + const mints = await api.query.tftBridgeModule.executedMintTransactions.entries() + if (mints.length > mintsBefore) return mints + }, { timeoutMs: 300_000, intervalMs: 4000, desc: 'fresh deposit mint to complete' }) + + // ─── TX HASH VERIFICATION ────────────────────────────────────────── + // Query executedMintTransactions by our specific Stellar tx hash. + // On-chain key = Vec of the Stellar tx hash string (same as Go bridge passes). + // If found: our NEW deposit was processed (bridge is at tip). + // If not found: an OLD deposit was re-processed instead — FAIL. + const mintTx = (await api.query.tftBridgeModule.executedMintTransactions(depositTxHash)).toJSON() + if (!mintTx || !mintTx.amount || mintTx.amount === 0) { + fail(name, `Mint tx hash mismatch: executedMintTransactions["${depositTxHash.slice(0, 16)}..."] not found on-chain — an old deposit may have been re-processed instead`, counter) + return + } + log(`TX hash verified: executedMintTransactions["${depositTxHash.slice(0, 16)}..."] = {amount: ${mintTx.amount}, votes: ${mintTx.votes}}`) + + // Verify Alice's TFChain balance increased by expected amount (± 0.1 for block author rewards) + const aliceBalAfter = await tfchainBalance(api, alice.address) + const balDelta = Math.round((aliceBalAfter - aliceBalBefore) * TFT_DECIMALS) / TFT_DECIMALS + log(`Alice TFChain TFT after: ${aliceBalAfter} (+${balDelta} TFT)`) + if (Math.abs(balDelta - expectedMint) > 0.1) { + fail(name, `Fresh deposit: expected TFChain ~+${expectedMint} TFT (±0.1), got +${balDelta}`, counter); return + } + + // Verify exactly 1 new mint was processed (no old deposits re-minted) + const mintsAfter = (await api.query.tftBridgeModule.executedMintTransactions.entries()).length + const mintCountDelta = mintsAfter - mintsBefore + log(`Executed mints: ${mintsBefore} → ${mintsAfter} (+${mintCountDelta})`) + if (mintCountDelta !== 1) { + fail(name, `Expected exactly 1 new mint, got ${mintCountDelta} — old deposits may have been re-processed`, counter); return + } + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } finally { + markTestPhase(collector, 'MV8', 'end') + } +} + +async function testMV9_expiredBatchRecovery () { + console.log('\n── MV9: Expired batch recovery (50 swaps offline → expiry → restart) ──') + markTestPhase(collector, 'MV9', 'start') + const name = 'MV9_expiredBatchRecovery' + const userAddress = getEnv('USER_ADDRESS') + const N = 50 + + try { + // Kill all 3 validators + for (let i = 1; i <= 3; i++) killValidator(i) + await new Promise(r => setTimeout(r, 2000)) + log('All 3 validators killed') + + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before: ${beforeStellar}`) + + // Submit N swaps in one block using sequential nonces + log(`Submitting ${N} swaps (all validators offline)...`) + const nonce = await api.rpc.system.accountNextIndex(alice.address) + const burnIds = await Promise.all( + Array.from({ length: N }, (_, i) => + swapToStellar(api, alice, 2, { userAddress, nonce: nonce.toNumber() + i }) + ) + ) + log(`${N} burns created on-chain: IDs ${burnIds[0]}..${burnIds[burnIds.length - 1]}`) + + // Wait for all burns to expire (on_finalize clears signatures after RetryInterval=20 blocks ≈ 120s) + log('Waiting for burns to expire on-chain (RetryInterval=20 blocks)...') + await waitUntil(async () => { + const burn = (await api.query.tftBridgeModule.burnTransactions(burnIds[0])).toJSON() + return burn && burn.signatures && burn.signatures.length === 0 + }, { timeoutMs: 180_000, intervalMs: 6000, desc: 'first burn to expire (signatures cleared)' }) + log('All burns expired (signatures cleared, sequence_number reset to 0)') + + // Start all 3 validators — they catch the next BurnTransactionExpired events. + // handleProposalsBatch re-proposes all expired burns in a single force_batch tx with + // consecutive Stellar sequence numbers (SyncSequenceNumber + per-proposal increment). + // All validators sync the same base sequence (no Stellar tx submitted yet), so they + // produce matching signatures. Once threshold is met, all burns become Ready and + // handleWithdrawReady submits them sequentially — each using its stored sequence + // number, so all succeed in one pass. + // + // After all Stellar payments in a cycle complete, BatchSetWithdrawExecuted + // confirms all in one force_batch. Multiple expiry cycles may be needed. + for (let i = 1; i <= 3; i++) startValidator(i) + log('All 3 validators restarted. Recovering expired burns via batch re-proposal...') + log(`Expected: 1 force_batch re-proposes all ${N}, then all ${N} Stellar payments execute in sequence`) + + const expectedNet = N * (2 - WITHDRAW_FEE_TFT) + let lastReported = 0 + + const finalStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const delivered = Math.round((bal - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + const count = Math.round(delivered / (2 - WITHDRAW_FEE_TFT)) + if (count > lastReported) { + log(` Progress: ${count}/${N} burns delivered (+${delivered} TFT)`) + lastReported = count + } + if (bal >= beforeStellar + expectedNet - 1e-7) return bal + }, { timeoutMs: 900_000, intervalMs: 10_000, desc: `all ${N} burns delivered (+${expectedNet} TFT)` }) + + const delta = Math.round((finalStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`All ${N} burns delivered: +${delta} TFT (expected +${expectedNet})`) + if (Math.abs(delta - expectedNet) > 1e-7) { + fail(name, `Expected +${expectedNet}, got +${delta}`, counter); return + } + + // Assert on-chain: all burns executed + for (const burnId of burnIds) { + if (!(await assertBurnExecuted(name, burnId))) return + } + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } finally { + markTestPhase(collector, 'MV9', 'end') + } +} + +async function testMV7_cleanState () { + console.log('\n── MV7: Clean state (no orphaned active transactions) ──') + markTestPhase(collector, 'MV7', 'start') + const name = 'MV7_cleanState' + + try { + // Wait for all active transaction maps to drain (tolerates in-flight processing) + await waitUntil(async () => { + const burns = await api.query.tftBridgeModule.burnTransactions.entries() + const refunds = await api.query.tftBridgeModule.refundTransactions.entries() + const mints = await api.query.tftBridgeModule.mintTransactions.entries() + return burns.length === 0 && refunds.length === 0 && mints.length === 0 + }, { timeoutMs: 300_000, intervalMs: 5000, desc: 'all active tx maps to drain' }) + pass(name, counter) + } catch (e) { + // On timeout, report what's left + const burns = await api.query.tftBridgeModule.burnTransactions.entries() + const refunds = await api.query.tftBridgeModule.refundTransactions.entries() + const mints = await api.query.tftBridgeModule.mintTransactions.entries() + fail(name, `Orphaned: ${burns.length} burns, ${refunds.length} refunds, ${mints.length} mints`, counter) + } finally { + markTestPhase(collector, 'MV7', 'end') + } +} + +// ─── Main ──────────────────────────────────────────────────────────────────── + +async function main () { + loadEnv(ENV_FILE, 'mv-tests') + bridgeAddress = getEnv('BRIDGE_ADDRESS') + issuerAddress = getEnv('ISSUER_ADDRESS') + + console.log('[mv-tests] Connecting to TFChain and Stellar...') + api = await ApiPromise.create({ provider: new WsProvider(TFCHAIN_URL) }) + horizon = new StellarSdk.Horizon.Server(HORIZON_URL) + + try { + const keyring = new Keyring({ type: 'sr25519' }) + alice = keyring.addFromUri('//Alice') + + // Start event collector for deep analysis + collector = await startEventCollector(api) + console.log('[mv-tests] Event collector started — tracking all bridge events by block') + + console.log('[mv-tests] Starting multi-validator test suite...\n') + + await testMV1_normalWithdraw() + await testMV2_deposit() + await testMV3_badDeposit() + await testMV4_validatorOffline() // kills/restarts Val3 + await testMV5_batchWithdraws() + await testMV6_crashRecovery() // kills/restarts Val2 + await testMV6a_belowMinimum() // pure pallet test, no bridge needed + await testMV8_lostCursor() // kills/restarts all 3 with wiped cursors + await testMV9_expiredBatchRecovery() // kills all 3, 50 swaps, expiry, restart (long) + await testMV7_cleanState() + + console.log(`\n${'─'.repeat(50)}`) + console.log(`Results: ${counter.passed} passed, ${counter.failed} failed`) + console.log('─'.repeat(50)) + + // Generate analysis report (pass api for chain-state reconciliation) + const outputPath = process.env.ANALYSIS_OUTPUT || '/tmp/bridge_mv_analysis.json' + await generateReport(collector, outputPath, api) + } finally { + if (collector) collector.stop() + await api.disconnect() + } + + process.exit(counter.failed > 0 ? 1 : 0) +} + +main().catch(e => { + console.error(`[mv-tests] FATAL: ${e.message || e}`) + process.exit(1) +}) diff --git a/scripts/bridge_setup.js b/scripts/bridge_setup.js new file mode 100644 index 000000000..b569f5c17 --- /dev/null +++ b/scripts/bridge_setup.js @@ -0,0 +1,123 @@ +#!/usr/bin/env node +/** + * bridge_setup.js + * + * Verifies that the TFChain dev chain genesis has the bridge pallet configured + * correctly for local development. The dev chain genesis pre-configures: + * - Bridge validators: Bob (//Bob) and Charlie (//Charlie) + * - Fee account: Ferdie (//Ferdie) + * - Deposit fee: 10,000,000 base units (1 TFT) + * - Withdraw fee: 10,000,000 base units (1 TFT) + * + * The dev chain genesis is sufficient for bridge configuration (no sudo needed). + * This script additionally creates Alice's twin (needed for deposit tests). + * + * Usage: + * node scripts/bridge_setup.js + */ + +'use strict' + +const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api') + +const TFCHAIN_URL = process.env.TFCHAIN_URL || 'ws://localhost:9944' + +function log (msg) { console.log(`[setup] ${msg}`) } +function warn (msg) { console.warn(`[setup] WARN: ${msg}`) } +function die (msg) { console.error(`[setup] ERROR: ${msg}`); process.exit(1) } + +/** Sign a tx, wait for InBlock, and throw on dispatch error */ +function signAndWait (api, tx, signer) { + return new Promise((resolve, reject) => { + let unsub + tx.signAndSend(signer, ({ status, dispatchError, events }) => { + if (!status.isInBlock && !status.isFinalized) return + if (dispatchError) { + let msg = dispatchError.toString() + if (dispatchError.isModule) { + try { + const decoded = api.registry.findMetaError(dispatchError.asModule) + msg = `${decoded.section}.${decoded.name}: ${decoded.docs}` + } catch {} + } + if (unsub) unsub() + reject(new Error(msg)) + return + } + if (unsub) unsub() + resolve({ status, events }) + }).then(u => { unsub = u }).catch(reject) + }) +} + +async function main () { + log(`Connecting to TFChain at ${TFCHAIN_URL}...`) + const api = await ApiPromise.create({ provider: new WsProvider(TFCHAIN_URL) }) + + try { + const keyring = new Keyring({ type: 'sr25519' }) + + const alice = keyring.addFromUri('//Alice') + const bob = keyring.addFromUri('//Bob') + const charlie = keyring.addFromUri('//Charlie') + const ferdie = keyring.addFromUri('//Ferdie') + + const validators = await api.query.tftBridgeModule.validators() + const valList = validators.toHuman() + const feeAccount = await api.query.tftBridgeModule.feeAccount() + const depositFee = await api.query.tftBridgeModule.depositFee() + const withdrawFee = await api.query.tftBridgeModule.withdrawFee() + + log('=== TFChain Bridge Genesis Configuration ===') + log(` Validators: ${JSON.stringify(valList)}`) + log(` Fee account: ${feeAccount.toHuman()}`) + log(` Deposit fee: ${Number(depositFee.toString()) / 1e7} TFT`) + log(` Withdraw fee: ${Number(withdrawFee.toString()) / 1e7} TFT`) + + // Verify expected validators are present + if (!valList.includes(bob.address)) { + warn(`Bob (${bob.address}) is not a genesis validator — bridge daemon using //Bob will be rejected`) + } else { + log(` Bob (//Bob) ✓ is a registered validator`) + } + + if (!valList.includes(charlie.address)) { + warn(`Charlie (${charlie.address}) is not a genesis validator`) + } else { + log(` Charlie (//Charlie) ✓ is a registered validator`) + } + + if (feeAccount.toHuman() !== ferdie.address) { + warn(`Fee account is ${feeAccount.toHuman()}, expected Ferdie (${ferdie.address})`) + } else { + log(` Fee account ✓ is Ferdie`) + } + + if (Number(depositFee.toString()) === 0) { + warn('Deposit fee is 0 — bridge may not charge fees') + } + + // Create Alice's twin (needed for test5_deposit) + // Alice must accept T&C before creating a twin + const aliceTwinOpt = await api.query.tfgridModule.twinIdByAccountID(alice.address) + const aliceTwinId = aliceTwinOpt.toJSON() + if (!aliceTwinId) { + log('Accepting T&C and creating Alice twin for deposit tests...') + await signAndWait(api, api.tx.tfgridModule.userAcceptTc('https://localhost/tc', 'deadbeef'), alice) + await signAndWait(api, api.tx.tfgridModule.createTwin(null, null), alice) + const newTwin = await api.query.tfgridModule.twinIdByAccountID(alice.address) + log(`Alice twin created (ID: ${newTwin.toJSON()})`) + } else { + log(`Alice twin already exists (ID: ${aliceTwinId})`) + } + + log('Setup verification complete.') + } finally { + await api.disconnect() + } +} + +main().catch(e => { + console.error(`[setup] FATAL: ${e.message || e}`) + process.exit(1) +}) diff --git a/scripts/bridge_tests.js b/scripts/bridge_tests.js new file mode 100644 index 000000000..5a0b859ef --- /dev/null +++ b/scripts/bridge_tests.js @@ -0,0 +1,654 @@ +#!/usr/bin/env node +/** + * bridge_tests.js + * + * E2E test suite for the TFChain bridge local dev environment. + * + * Tests (run sequentially): + * 1. Normal withdraw — swap 2 TFT on TFChain, receive 1 TFT on Stellar (1 TFT fee) + * 2. Batch withdraws — 5 simultaneous swaps in one block, all 5 delivered + * 3. Bad deposit — send TFT to bridge without memo, expect full refund + * 5. Deposit/mint — send TFT to bridge with twin memo, verify TFChain balance + * 6. Below-minimum — swap below fee, expect dispatch error + * 4. Crash recovery — SIGKILL bridge mid-withdraw, restart, verify delivery completes + * 8. Lost cursor — wipe persistency (BoltDB), restart, verify no double-spend, + * then deposit to prove bridge is at Stellar tip (tx hash verified) + * 9. Expired batch — 50 swaps while bridge offline, wait for expiry, restart, all delivered + * 7. Clean state — verify no orphaned active transactions on-chain + * + * All tests assert exact TFT balances (Stellar + TFChain) and on-chain state. + * Non-zero exit on any failure. + * + * Usage: + * node scripts/bridge_tests.js + * TFCHAIN_URL=ws://localhost:9944 BRIDGE_PID_FILE=/tmp/bridge_local.pid node scripts/bridge_tests.js + */ + +'use strict' + +const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api') +const StellarSdk = require('@stellar/stellar-sdk') +const fs = require('fs') +const { spawn } = require('child_process') +const { + log, pass, fail, + loadEnv, getEnv, + stellarTFTBalance, + tfchainBalance, + waitUntil, + sendStellarPayment, + swapToStellar, + TFT_DECIMALS +} = require('./bridge_helpers') + +const TFCHAIN_URL = process.env.TFCHAIN_URL || 'ws://localhost:9944' +const HORIZON_URL = process.env.STELLAR_HORIZON_URL || 'https://horizon-testnet.stellar.org' +const NETWORK_PASSPHRASE = StellarSdk.Networks.TESTNET +const ENV_FILE = process.env.BRIDGE_ENV_FILE || '/tmp/bridge_local_env.sh' +const BRIDGE_PID_FILE = process.env.BRIDGE_PID_FILE || '/tmp/bridge_local.pid' +const BRIDGE_LOG_FILE = process.env.BRIDGE_LOG_FILE || '/tmp/bridge_local.log' +const BRIDGE_BIN = process.env.BRIDGE_BIN || './bridge/tfchain_bridge/tfchain_bridge_local' +const BRIDGE_PERSISTENCY = process.env.BRIDGE_PERSISTENCY || './bridge/tfchain_bridge/signer_local.json' + +const WITHDRAW_FEE_TFT = 1 // 1 TFT fee + +const counter = { passed: 0, failed: 0 } +let api, alice, horizon, bridgeAddress, issuerAddress + +// ─── Bridge lifecycle helpers ─────────────────────────────────────────────── + +async function bridgeIsRunning () { + if (!fs.existsSync(BRIDGE_PID_FILE)) return false + const pid = parseInt(fs.readFileSync(BRIDGE_PID_FILE, 'utf8').trim()) + try { process.kill(pid, 0); return true } catch { return false } +} + +function getBridgePid () { + if (!fs.existsSync(BRIDGE_PID_FILE)) return null + return parseInt(fs.readFileSync(BRIDGE_PID_FILE, 'utf8').trim()) +} + +function killBridge (signal = 'SIGKILL') { + const pid = getBridgePid() + if (pid) { + try { process.kill(pid, signal); log(`Bridge (PID ${pid}) killed with ${signal}`) } catch {} + } +} + +function startBridge () { + const bridgeSecret = getEnv('BRIDGE_SECRET') + const tfchainSeed = process.env.VAL1_TFCHAIN_SEED || + 'quarter between satisfy three sphere six soda boss cute decade old trend' + + // Use shell exec + append redirect instead of fd inheritance. + // On macOS, passing a numeric fd to a detached child's stdio is unreliable: + // the fd silently becomes invalid after child.unref(), so the bridge writes nothing + // to the log. Shell exec replaces sh with the bridge binary (same PID), and + // >> redirect is handled by the shell before exec, so it works cross-platform. + const shellCmd = [ + 'exec', + `"${BRIDGE_BIN}"`, + '--secret', `"${bridgeSecret}"`, + '--tfchainurl', TFCHAIN_URL, + '--tfchainseed', `"${tfchainSeed}"`, + '--bridgewallet', bridgeAddress, + '--persistency', BRIDGE_PERSISTENCY, + '--network', 'local', + `>>"${BRIDGE_LOG_FILE}"`, '2>&1' + ].join(' ') + + const child = spawn('/bin/sh', ['-c', shellCmd], { + detached: true, + stdio: 'ignore' + }) + child.unref() + fs.writeFileSync(BRIDGE_PID_FILE, String(child.pid)) + log(`Bridge restarted (PID ${child.pid})`) + return child.pid +} + +// ─── On-chain assertion helpers ───────────────────────────────────────────── + +/** + * Poll until a burn tx moves to ExecutedBurnTransactions (not stuck in active map). + * Waits up to 30s for the on-chain state to settle — set_burn_transaction_executed + * may finalize a few blocks after the Stellar payment is visible. + */ +async function assertBurnExecuted (name, burnId) { + try { + await waitUntil(async () => { + const active = (await api.query.tftBridgeModule.burnTransactions(burnId)).toJSON() + if (active && active.target) return false + const executed = (await api.query.tftBridgeModule.executedBurnTransactions(burnId)).toJSON() + return executed && executed.target + }, { timeoutMs: 30_000, intervalMs: 3000, desc: `burn ${burnId} to reach ExecutedBurnTransactions` }) + return true + } catch { + const active = (await api.query.tftBridgeModule.burnTransactions(burnId)).toJSON() + if (active && active.target) { + fail(name, `burn ${burnId} still in active BurnTransactions after 30s`, counter) + } else { + fail(name, `burn ${burnId} not in ExecutedBurnTransactions after 30s`, counter) + } + return false + } +} + +/** + * Poll until at least one new refund reaches ExecutedRefundTransactions since `countBefore`. + * Waits up to 30s — set_refund_transaction_executed may finalize after the Stellar refund. + */ +async function assertRefundExecuted (name, countBefore) { + try { + await waitUntil(async () => { + const after = await api.query.tftBridgeModule.executedRefundTransactions.entries() + return after.length > countBefore + }, { timeoutMs: 30_000, intervalMs: 3000, desc: 'new refund in ExecutedRefundTransactions' }) + return true + } catch { + const after = await api.query.tftBridgeModule.executedRefundTransactions.entries() + fail(name, `no new refund in ExecutedRefundTransactions after 30s (before: ${countBefore}, after: ${after.length})`, counter) + return false + } +} + +// ─── Tests ──────────────────────────────────────────────────────────────────── + +async function test1_normalWithdraw () { + console.log('\n── TEST 1: Normal withdraw (2 TFT swap → 1 TFT net on Stellar) ──') + const name = 'test1_normalWithdraw' + const userAddress = getEnv('USER_ADDRESS') + const swapAmount = 2 + + try { + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const beforeTFChain = await tfchainBalance(api, alice.address) + log(`User Stellar TFT before: ${beforeStellar}`) + log(`Alice TFChain TFT before: ${beforeTFChain}`) + + const burnId = await swapToStellar(api, alice, swapAmount, { userAddress }) + log(`Burn ID: ${burnId}`) + + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal > beforeStellar) return bal + }, { timeoutMs: 180_000, desc: `Stellar balance to increase above ${beforeStellar}` }) + + // Assert Stellar balance delta + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + const expected = swapAmount - WITHDRAW_FEE_TFT + if (Math.abs(delta - expected) > 1e-7) { + fail(name, `Expected Stellar +${expected} TFT, got +${delta}`, counter); return + } + log(`User Stellar TFT after: ${afterStellar} (+${delta} TFT)`) + + // Assert TFChain balance decreased by ~swapAmount (± 0.1 TFT for extrinsic fee). + const afterTFChain = await tfchainBalance(api, alice.address) + const tfDelta = Math.round((beforeTFChain - afterTFChain) * TFT_DECIMALS) / TFT_DECIMALS + log(`Alice TFChain TFT after: ${afterTFChain} (-${tfDelta} TFT)`) + if (Math.abs(tfDelta - swapAmount) > 0.1) { + fail(name, `TFChain balance should decrease by ~${swapAmount} (±0.1), decreased by ${tfDelta}`, counter); return + } + + // Assert on-chain: burn executed + if (!(await assertBurnExecuted(name, burnId))) return + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } +} + +async function test2_batchWithdraw () { + console.log('\n── TEST 2: Batch withdraw (5 simultaneous swaps in one block) ──') + const name = 'test2_batchWithdraw' + const userAddress = getEnv('USER_ADDRESS') + + try { + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before: ${beforeStellar}`) + + const nonce = await api.rpc.system.accountNextIndex(alice.address) + const burnIds = await Promise.all( + [0, 1, 2, 3, 4].map(i => swapToStellar(api, alice, 2, { userAddress, nonce: nonce.toNumber() + i })) + ) + log(`Burn IDs: ${burnIds.join(', ')}`) + + const expectedNet = 5 * (2 - WITHDRAW_FEE_TFT) + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal >= beforeStellar + expectedNet - 1e-7) return bal + }, { timeoutMs: 300_000, desc: `Stellar balance >= ${beforeStellar + expectedNet}` }) + + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`User Stellar TFT after: ${afterStellar} (+${delta} TFT, expected +${expectedNet})`) + if (Math.abs(delta - expectedNet) > 1e-7) { + fail(name, `Expected +${expectedNet} TFT, got +${delta}`, counter); return + } + + // Assert on-chain: all burns executed + for (const burnId of burnIds) { + if (!(await assertBurnExecuted(name, burnId))) return + } + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } +} + +async function test3_badDeposit () { + console.log('\n── TEST 3: Bad deposit (no memo → full refund) ──') + const name = 'test3_badDeposit' + const userAddress = getEnv('USER_ADDRESS') + const userSecret = getEnv('USER_SECRET') + + try { + const depositAmount = '3' + + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const refundsBefore = (await api.query.tftBridgeModule.executedRefundTransactions.entries()).length + log(`User Stellar TFT before: ${beforeStellar}`) + + // Send TFT to bridge without a memo + const result = await sendStellarPayment(horizon, issuerAddress, NETWORK_PASSPHRASE, userSecret, bridgeAddress, depositAmount) + log(`Bad deposit sent: ${result.hash.slice(0, 16)}`) + + // Wait for refund — balance should return to (roughly) beforeStellar + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal >= beforeStellar - 1e-7) return bal + }, { timeoutMs: 180_000, desc: 'refund to restore balance' }) + + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`User Stellar TFT after: ${afterStellar} (delta: ${delta >= 0 ? '+' : ''}${delta})`) + // Balance should be within 0 (full refund, no deposit fee on refunds) + if (Math.abs(delta) > 1e-7) { + fail(name, `Expected net 0 change (full refund), got ${delta >= 0 ? '+' : ''}${delta}`, counter); return + } + + // Assert on-chain: refund executed + if (!(await assertRefundExecuted(name, refundsBefore))) return + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } +} + +async function test5_deposit () { + console.log('\n── TEST 5: Deposit/mint (send TFT to bridge with twin memo) ──') + const name = 'test5_deposit' + + try { + // Get Alice's twin ID + const twinOpt = await api.query.tfgridModule.twinIdByAccountID(alice.address) + const twinId = twinOpt.isSome ? twinOpt.unwrap().toNumber() : twinOpt.toJSON() + if (!twinId) { fail(name, 'Alice has no twin on TFChain', counter); return } + log(`Alice twin ID: ${twinId}`) + + const depositAmount = '2' + const depositFee = Number(await api.query.tftBridgeModule.depositFee()) / TFT_DECIMALS + const expectedMint = parseFloat(depositAmount) - depositFee + log(`Deposit fee: ${depositFee} TFT, expected mint: ${expectedMint} TFT`) + + const aliceBalBefore = await tfchainBalance(api, alice.address) + const mintsBefore = (await api.query.tftBridgeModule.executedMintTransactions.entries()).length + log(`Alice TFChain TFT before: ${aliceBalBefore}, executed mints: ${mintsBefore}`) + + // Send TFT to bridge with twin_ memo + const userSecret = getEnv('USER_SECRET') + const result = await sendStellarPayment(horizon, issuerAddress, NETWORK_PASSPHRASE, userSecret, bridgeAddress, depositAmount, `twin_${twinId}`) + log(`Deposit sent: ${result.hash.slice(0, 16)} (memo: twin_${twinId})`) + + // Wait for mint to be executed on TFChain + await waitUntil(async () => { + const mints = await api.query.tftBridgeModule.executedMintTransactions.entries() + if (mints.length > mintsBefore) return true + }, { timeoutMs: 120_000, desc: 'executed mint count to increase' }) + + // Assert Alice's TFChain balance increased by ~expectedMint (± 0.1 TFT for block author rewards). + const aliceBalAfter = await tfchainBalance(api, alice.address) + const balDelta = Math.round((aliceBalAfter - aliceBalBefore) * TFT_DECIMALS) / TFT_DECIMALS + log(`Alice TFChain TFT after: ${aliceBalAfter} (+${balDelta} TFT)`) + if (Math.abs(balDelta - expectedMint) > 0.1) { + fail(name, `Expected TFChain ~+${expectedMint} TFT (±0.1), got +${balDelta}`, counter); return + } + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } +} + +async function test6_belowMinimum () { + console.log('\n── TEST 6: Withdraw below minimum (should be rejected) ──') + const name = 'test6_belowMinimum' + const userAddress = getEnv('USER_ADDRESS') + + try { + // Attempt swap with 0.5 TFT (below 1 TFT withdraw fee) + await swapToStellar(api, alice, 0.5, { userAddress }) + fail(name, 'swapToStellar should have thrown, but succeeded', counter) + } catch (e) { + if (e.message.includes('AmountIsLessThanWithdrawFee')) { + log(`Correctly rejected: ${e.message}`) + pass(name, counter) + } else { + fail(name, `Expected AmountIsLessThanWithdrawFee, got: ${e.message}`, counter) + } + } +} + +async function test4_crashRecovery () { + console.log('\n── TEST 4: Crash recovery (SIGKILL mid-withdraw, restart, verify delivery) ──') + const name = 'test4_crashRecovery' + const userAddress = getEnv('USER_ADDRESS') + + try { + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before: ${beforeStellar}`) + + // Trigger a withdraw + const burnId = await swapToStellar(api, alice, 2, { userAddress }) + log(`Burn ID: ${burnId}`) + + // Wait for BurnTransactionReady on TFChain (signatures collected), then kill bridge + log('Waiting for BurnTransactionReady...') + await waitUntil(async () => { + const ready = await api.query.tftBridgeModule.burnTransactions(burnId) + const json = ready.toJSON() + return json && json.signatures && json.signatures.length >= 1 + }, { timeoutMs: 60_000, desc: 'BurnTransactionReady (>=1 sig)' }) + + // Kill bridge mid-flight + killBridge('SIGKILL') + log('Bridge killed. Waiting 3s...') + await new Promise(r => setTimeout(r, 3000)) + + // Restart bridge. + // Note: detecting bridge readiness via log file is unreliable on macOS because + // detached process stdout fd inheritance breaks after child.unref(). Instead, we + // give the bridge a fixed startup window and then verify the actual outcome. + startBridge() + log('Bridge restarted. Waiting 10s for startup...') + await new Promise(r => setTimeout(r, 10_000)) + + // Verify the withdrawal completed — either: + // (a) bridge completed before kill and balance is already updated, or + // (b) bridge restarted and completed via reconciliation / expiry recovery + const afterStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + if (bal > beforeStellar) return bal + }, { timeoutMs: 300_000, desc: 'Stellar balance to increase after crash recovery' }) + + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + const expected = 2 - WITHDRAW_FEE_TFT + log(`User Stellar TFT after: ${afterStellar} (+${delta} TFT)`) + + // Explicit double-spend guard: verify exactly +expected, not 2x expected + if (Math.abs(delta - expected) > 1e-7) { + fail(name, `Expected +${expected} TFT after recovery, got +${delta} (double-spend if 2x)`, counter); return + } + + // Assert on-chain: burn executed + if (!(await assertBurnExecuted(name, burnId))) return + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } +} + +async function test8_lostCursor () { + console.log('\n── TEST 8: Lost cursor (wipe persistency → no double-spend) ──') + const name = 'test8_lostCursor' + const userAddress = getEnv('USER_ADDRESS') + + try { + // Bridge is running (restarted by T4). Kill it. + killBridge('SIGKILL') + await new Promise(r => setTimeout(r, 2000)) + + // Wipe the persistency file (BoltDB/JSON cursor). + // Without the cursor, bridge re-scans the Stellar account from the beginning. + // Protection against duplicate burns: IsBurnedAlready (ExecutedBurnTransactions) + // Protection against duplicate mints: IsMintedAlready (ExecutedMintTransactions) + if (fs.existsSync(BRIDGE_PERSISTENCY)) { + fs.unlinkSync(BRIDGE_PERSISTENCY) + log(`Persistency wiped: ${BRIDGE_PERSISTENCY}`) + } else { + log('Persistency file not found (nothing to wipe)') + } + + // Snapshot Stellar balance — should NOT change after restart + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before restart: ${beforeStellar}`) + + // Restart bridge — it rescans with no local state + startBridge() + log('Bridge restarted with wiped cursor. Waiting 15s for rescan...') + await new Promise(r => setTimeout(r, 15_000)) + + // Verify no double-spend — balance must be unchanged + const afterStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const delta = Math.round((afterStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`User Stellar TFT after rescan: ${afterStellar} (delta: ${delta >= 0 ? '+' : ''}${delta})`) + if (Math.abs(delta) > 1e-7) { + fail(name, `DOUBLE-SPEND: balance changed by ${delta} TFT after cursor wipe`, counter); return + } + log('No double-spend — IsMintedAlready + IsBurnedAlready held during rescan') + + // ─── Hardened: fresh DEPOSIT to prove bridge is at Stellar tip ─────── + // + // A withdraw (old approach) is event-driven from TFChain — it doesn't use + // the Stellar cursor at all, so it doesn't prove the bridge finished scanning. + // + // A deposit proves the bridge has caught up to the TIP of the Stellar account, + // because the bridge must reach our new transaction in the Horizon stream. + // + // Extra hardening: we verify the on-chain mint's tx_id matches our Stellar + // deposit hash, ruling out the case where an OLD deposit was re-processed + // and our new deposit is still un-seen. + log('Running fresh deposit to verify bridge scanned to Stellar tip...') + + const twinOpt = await api.query.tfgridModule.twinIdByAccountID(alice.address) + const twinId = twinOpt.isSome ? twinOpt.unwrap().toNumber() : twinOpt.toJSON() + if (!twinId) throw new Error('Alice has no twin on TFChain — is bridge-setup complete?') + + const depositAmount = '2' + const depositFee = Number(await api.query.tftBridgeModule.depositFee()) / TFT_DECIMALS + const expectedMint = parseFloat(depositAmount) - depositFee + log(`Deposit fee: ${depositFee} TFT, expected mint: ${expectedMint} TFT`) + + const aliceBalBefore = await tfchainBalance(api, alice.address) + const mintsBefore = (await api.query.tftBridgeModule.executedMintTransactions.entries()).length + log(`Alice TFChain TFT before: ${aliceBalBefore}, executed mints: ${mintsBefore}`) + + // Send deposit — capture the Stellar tx hash for verification + const userSecret = getEnv('USER_SECRET') + const result = await sendStellarPayment(horizon, issuerAddress, NETWORK_PASSPHRASE, userSecret, bridgeAddress, depositAmount, `twin_${twinId}`) + const depositTxHash = result.hash + log(`Fresh deposit sent: ${depositTxHash} (memo: twin_${twinId})`) + + // Wait for the mint to appear on-chain + await waitUntil(async () => { + const mints = await api.query.tftBridgeModule.executedMintTransactions.entries() + if (mints.length > mintsBefore) return mints + }, { timeoutMs: 300_000, desc: 'fresh deposit mint to complete' }) + + // ─── TX HASH VERIFICATION ────────────────────────────────────────── + // Query executedMintTransactions by our specific Stellar tx hash. + // On-chain key = Vec of the Stellar tx hash string (same as Go bridge passes). + // If found: our NEW deposit was processed (bridge is at tip). + // If not found: an OLD deposit was re-processed instead — FAIL. + const mintTx = (await api.query.tftBridgeModule.executedMintTransactions(depositTxHash)).toJSON() + if (!mintTx || !mintTx.amount || mintTx.amount === 0) { + fail(name, `Mint tx hash mismatch: executedMintTransactions["${depositTxHash.slice(0, 16)}..."] not found on-chain — an old deposit may have been re-processed instead`, counter) + return + } + log(`TX hash verified: executedMintTransactions["${depositTxHash.slice(0, 16)}..."] = {amount: ${mintTx.amount}, votes: ${mintTx.votes}}`) + + // Verify Alice's TFChain balance increased by expected amount (± 0.1 for block author rewards) + const aliceBalAfter = await tfchainBalance(api, alice.address) + const balDelta = Math.round((aliceBalAfter - aliceBalBefore) * TFT_DECIMALS) / TFT_DECIMALS + log(`Alice TFChain TFT after: ${aliceBalAfter} (+${balDelta} TFT)`) + if (Math.abs(balDelta - expectedMint) > 0.1) { + fail(name, `Fresh deposit: expected TFChain ~+${expectedMint} TFT (±0.1), got +${balDelta}`, counter); return + } + + // Verify exactly 1 new mint was processed (no old deposits re-minted) + const mintsAfter = (await api.query.tftBridgeModule.executedMintTransactions.entries()).length + const mintCountDelta = mintsAfter - mintsBefore + log(`Executed mints: ${mintsBefore} → ${mintsAfter} (+${mintCountDelta})`) + if (mintCountDelta !== 1) { + fail(name, `Expected exactly 1 new mint, got ${mintCountDelta} — old deposits may have been re-processed`, counter); return + } + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } +} + +async function test9_expiredBatchRecovery () { + console.log('\n── TEST 9: Expired batch recovery (50 swaps offline → expiry → restart) ──') + const name = 'test9_expiredBatchRecovery' + const userAddress = getEnv('USER_ADDRESS') + const N = 50 + + try { + // Kill bridge before submitting swaps + killBridge('SIGKILL') + await new Promise(r => setTimeout(r, 2000)) + log('Bridge killed') + + const beforeStellar = await stellarTFTBalance(userAddress, horizon, issuerAddress) + log(`User Stellar TFT before: ${beforeStellar}`) + + // Submit N swaps in one block using sequential nonces + log(`Submitting ${N} swaps (bridge offline)...`) + const nonce = await api.rpc.system.accountNextIndex(alice.address) + const burnIds = await Promise.all( + Array.from({ length: N }, (_, i) => + swapToStellar(api, alice, 2, { userAddress, nonce: nonce.toNumber() + i }) + ) + ) + log(`${N} burns created on-chain: IDs ${burnIds[0]}..${burnIds[burnIds.length - 1]}`) + + // Wait for all burns to expire (on_finalize clears signatures after RetryInterval=20 blocks ≈ 120s) + log('Waiting for burns to expire on-chain (RetryInterval=20 blocks)...') + await waitUntil(async () => { + const burn = (await api.query.tftBridgeModule.burnTransactions(burnIds[0])).toJSON() + return burn && burn.signatures && burn.signatures.length === 0 + }, { timeoutMs: 180_000, intervalMs: 6000, desc: 'first burn to expire (signatures cleared)' }) + log('All burns expired (signatures cleared, sequence_number reset to 0)') + + // Start bridge — it subscribes to new blocks and catches the next BurnTransactionExpired events. + // handleProposalsBatch re-proposes all expired burns in a single force_batch tx with + // consecutive Stellar sequence numbers (SyncSequenceNumber + per-proposal increment). + // All burns become Ready in the same block, and handleWithdrawReady processes them + // sequentially — each Stellar submission uses its stored sequence number, so all + // succeed in one pass without sequence collisions. + // + // After all Stellar payments complete, each handleWithdrawReady calls + // SetWithdrawExecuted individually (could be batched in a future optimization). + startBridge() + log('Bridge restarted. Recovering expired burns via batch re-proposal...') + log(`Expected: 1 force_batch re-proposes all ${N}, then all ${N} Stellar payments execute in sequence`) + + const expectedNet = N * (2 - WITHDRAW_FEE_TFT) + let lastReported = 0 + + const finalStellar = await waitUntil(async () => { + const bal = await stellarTFTBalance(userAddress, horizon, issuerAddress) + const delivered = Math.round((bal - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + const count = Math.round(delivered / (2 - WITHDRAW_FEE_TFT)) + if (count > lastReported) { + log(` Progress: ${count}/${N} burns delivered (+${delivered} TFT)`) + lastReported = count + } + if (bal >= beforeStellar + expectedNet - 1e-7) return bal + }, { timeoutMs: 900_000, intervalMs: 10_000, desc: `all ${N} burns delivered (+${expectedNet} TFT)` }) + + const delta = Math.round((finalStellar - beforeStellar) * TFT_DECIMALS) / TFT_DECIMALS + log(`All ${N} burns delivered: +${delta} TFT (expected +${expectedNet})`) + if (Math.abs(delta - expectedNet) > 1e-7) { + fail(name, `Expected +${expectedNet}, got +${delta}`, counter); return + } + + // Assert on-chain: all burns executed + for (const burnId of burnIds) { + if (!(await assertBurnExecuted(name, burnId))) return + } + + pass(name, counter) + } catch (e) { + fail(name, e.message, counter) + } +} + +async function test7_cleanState () { + console.log('\n── TEST 7: Clean state (no orphaned active transactions) ──') + const name = 'test7_cleanState' + + try { + // Wait for all active transaction maps to drain (tolerates in-flight processing) + await waitUntil(async () => { + const burns = await api.query.tftBridgeModule.burnTransactions.entries() + const refunds = await api.query.tftBridgeModule.refundTransactions.entries() + const mints = await api.query.tftBridgeModule.mintTransactions.entries() + return burns.length === 0 && refunds.length === 0 && mints.length === 0 + }, { timeoutMs: 300_000, intervalMs: 5000, desc: 'all active tx maps to drain' }) + pass(name, counter) + } catch (e) { + // On timeout, report what's left + const burns = await api.query.tftBridgeModule.burnTransactions.entries() + const refunds = await api.query.tftBridgeModule.refundTransactions.entries() + const mints = await api.query.tftBridgeModule.mintTransactions.entries() + fail(name, `Orphaned: ${burns.length} burns, ${refunds.length} refunds, ${mints.length} mints`, counter) + } +} + +// ─── Main ──────────────────────────────────────────────────────────────────── + +async function main () { + loadEnv(ENV_FILE, 'tests') + + bridgeAddress = getEnv('BRIDGE_ADDRESS') + issuerAddress = getEnv('ISSUER_ADDRESS') + + console.log('[tests] Connecting to TFChain and Stellar...') + api = await ApiPromise.create({ provider: new WsProvider(TFCHAIN_URL) }) + horizon = new StellarSdk.Horizon.Server(HORIZON_URL) + + try { + const keyring = new Keyring({ type: 'sr25519' }) + alice = keyring.addFromUri('//Alice') + + console.log('[tests] Starting test suite...\n') + + await test1_normalWithdraw() + await test2_batchWithdraw() + await test3_badDeposit() + await test5_deposit() + await test6_belowMinimum() + await test4_crashRecovery() // kills/restarts bridge + await test8_lostCursor() // kills/restarts bridge with wiped cursor + await test9_expiredBatchRecovery() // kills bridge, 50 swaps, expiry, restart (long) + await test7_cleanState() + + console.log(`\n${'─'.repeat(50)}`) + console.log(`Results: ${counter.passed} passed, ${counter.failed} failed`) + console.log('─'.repeat(50)) + } finally { + await api.disconnect() + } + + process.exit(counter.failed > 0 ? 1 : 0) +} + +main().catch(e => { + console.error(`[tests] FATAL: ${e.message || e}`) + process.exit(1) +}) diff --git a/scripts/package-lock.json b/scripts/package-lock.json new file mode 100644 index 000000000..642bedc17 --- /dev/null +++ b/scripts/package-lock.json @@ -0,0 +1,4690 @@ +{ + "name": "tfchain-js-scripts", + "version": "2.12.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tfchain-js-scripts", + "version": "2.12.0", + "license": "ISC", + "dependencies": { + "@polkadot/api": "^10.7.2", + "@stellar/stellar-sdk": "^12.3.0", + "axios": "^0.25.0", + "bip39": "^3.0.3", + "blake": "^1.0.1", + "bn.js": "^5.1.3", + "ip-regex": "^4.3.0", + "moment": "^2.29.1" + }, + "devDependencies": { + "standard": "^16.0.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/api": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.8.1.tgz", + "integrity": "sha512-Txx1bXmB4FHghzPZ+OVQk6oYgPE03bhwMNiXzmC8Ia/tw5aoFnko2FFl+Y1pEhhMKDmqfyVe4L+HxPjfEQbsfA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-augment": "10.8.1", + "@polkadot/api-base": "10.8.1", + "@polkadot/api-derive": "10.8.1", + "@polkadot/keyring": "^12.2.2", + "@polkadot/rpc-augment": "10.8.1", + "@polkadot/rpc-core": "10.8.1", + "@polkadot/rpc-provider": "10.8.1", + "@polkadot/types": "10.8.1", + "@polkadot/types-augment": "10.8.1", + "@polkadot/types-codec": "10.8.1", + "@polkadot/types-create": "10.8.1", + "@polkadot/types-known": "10.8.1", + "@polkadot/util": "^12.2.2", + "@polkadot/util-crypto": "^12.2.2", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.8.1.tgz", + "integrity": "sha512-KFfF0OESmFI8hFmuKGuU204+S4SORIxniZr88xUnEPyJQr4R6XYnbGSKcLJM5Y2MK8a7JEoKgg+hfnUTK6Se0w==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-base": "10.8.1", + "@polkadot/rpc-augment": "10.8.1", + "@polkadot/types": "10.8.1", + "@polkadot/types-augment": "10.8.1", + "@polkadot/types-codec": "10.8.1", + "@polkadot/util": "^12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-base": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.8.1.tgz", + "integrity": "sha512-13BZ04UtiCECQshstL9RBLDJ6nq9HSwWXwMuWZcXUEPSsPhfR3iT0o212dtGrGliakYWgGEU1LGJuGhZ5iK7TA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "10.8.1", + "@polkadot/types": "10.8.1", + "@polkadot/util": "^12.2.2", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.8.1.tgz", + "integrity": "sha512-r1SBY9vu6OZMGp8/KZFwOqh7yS8yl0YbNDWuju2BEMWQ4Xx6WOlQjQV8Np9UFtKcnBFQzQjMLWH3vwrfTDgVEQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "10.8.1", + "@polkadot/api-augment": "10.8.1", + "@polkadot/api-base": "10.8.1", + "@polkadot/rpc-core": "10.8.1", + "@polkadot/types": "10.8.1", + "@polkadot/types-codec": "10.8.1", + "@polkadot/util": "^12.2.2", + "@polkadot/util-crypto": "^12.2.2", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/keyring": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.2.2.tgz", + "integrity": "sha512-z8MVdgrhzg/bFiR2i5/W06Ma+IPeisH7EtGuIQ+ZwXiCJlXMAGUy5spfk3fUbXYubCCqNycqFgKTYDM/rDhXSg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "12.2.2", + "@polkadot/util-crypto": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.2.2", + "@polkadot/util-crypto": "12.2.2" + } + }, + "node_modules/@polkadot/networks": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.2.2.tgz", + "integrity": "sha512-SsZognHwXyD2saJkB35G+28noAZBcNpJAXsTI7QTTDHGiQSDp0mPmrk3Rt7BRAeFn4qdXQuRqQYKYUwBM2i9mQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "12.2.2", + "@substrate/ss58-registry": "^1.40.0", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.8.1.tgz", + "integrity": "sha512-FmXAQLyG8cwBI+MwMxxx4qttolR2gFnYXC7PjYrrjYq4AZrrGWd9SvwXx8aA/NLRJ/PJqvri4dsoKPe7NiE+1A==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "10.8.1", + "@polkadot/types": "10.8.1", + "@polkadot/types-codec": "10.8.1", + "@polkadot/util": "^12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.8.1.tgz", + "integrity": "sha512-GTMYBBssiP6wyYvc8hB0glQc4VUneGxiSYjWGijh9NEl/JVBpU01jcK3dfx534AWptctJN1Vk2fWzhaDgnj8zA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-augment": "10.8.1", + "@polkadot/rpc-provider": "10.8.1", + "@polkadot/types": "10.8.1", + "@polkadot/util": "^12.2.2", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.8.1.tgz", + "integrity": "sha512-yQdUmaWRMSa/qVGBRP1vGjdv4DnlaYOctJfRpz2MWPbEckH5DmPRxV4BAZ9FVa5lATX0Qkmr3uvBt3qApH7xhQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^12.2.2", + "@polkadot/types": "10.8.1", + "@polkadot/types-support": "10.8.1", + "@polkadot/util": "^12.2.2", + "@polkadot/util-crypto": "^12.2.2", + "@polkadot/x-fetch": "^12.2.2", + "@polkadot/x-global": "^12.2.2", + "@polkadot/x-ws": "^12.2.2", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.2.1", + "nock": "^13.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@substrate/connect": "0.7.26" + } + }, + "node_modules/@polkadot/types": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.8.1.tgz", + "integrity": "sha512-m6UvsvQOZ7sRGbonb6QLs4mZ6TmYKdAXAcHakiJl2xArqsgOghJsKhgaTqcigPkSq4947MXtIkEzdrwFEnkYkQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^12.2.2", + "@polkadot/types-augment": "10.8.1", + "@polkadot/types-codec": "10.8.1", + "@polkadot/types-create": "10.8.1", + "@polkadot/util": "^12.2.2", + "@polkadot/util-crypto": "^12.2.2", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.8.1.tgz", + "integrity": "sha512-rVn8aA4u6YPcxGEnBq2rXVmgXM5kSuiTHIjsusb6Sm3PzO//NcC/TW9sbZjlAJApgSoj9iagM7Y85OPGOZlxwg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types": "10.8.1", + "@polkadot/types-codec": "10.8.1", + "@polkadot/util": "^12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.8.1.tgz", + "integrity": "sha512-8dj4T6GA6JxuwUNShO70omZ4qkChwsJeGAJg5x09UeLEAwBS02BkFSllRUJjGEwnAUb/Iq4s3NBVmYiiZ/wmKg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^12.2.2", + "@polkadot/x-bigint": "^12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-create": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.8.1.tgz", + "integrity": "sha512-v2WZHQAjf8TiLipRkR1iPTyWSjGHJJP2SQ5uVO5UJlHilpE8lODqY1rr/9hGN+sbRhU0vEy6ZceDEKuNbtJB3Q==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types-codec": "10.8.1", + "@polkadot/util": "^12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-known": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.8.1.tgz", + "integrity": "sha512-AIeuF7eTIEnUgxa1pU0UMmF/tIXgucAECwU8vzoKeJLrYWA16VYUm0Pst9e3jK3PyLaCneMRyR00Lc7oxVANbw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/networks": "^12.2.2", + "@polkadot/types": "10.8.1", + "@polkadot/types-codec": "10.8.1", + "@polkadot/types-create": "10.8.1", + "@polkadot/util": "^12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-support": { + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.8.1.tgz", + "integrity": "sha512-arDVaL70vzVL5JBGWW1qcOASn1cJ/UxNMR3fHchoVkAqS20VIrehE8MF4zXMdjcP0Ak3+6E0FaSmHMTKlmEJsg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.2.2.tgz", + "integrity": "sha512-u/v5Z2+iUwX/CXEMVZgJmwqqx1kT5Zfxsio3vpuYaPCg49xhTKqAcrakgB+1BUHhhyF3Zkb9uG73JWFR0Lkk9w==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "12.2.2", + "@polkadot/x-global": "12.2.2", + "@polkadot/x-textdecoder": "12.2.2", + "@polkadot/x-textencoder": "12.2.2", + "@types/bn.js": "^5.1.1", + "bn.js": "^5.2.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.2.2.tgz", + "integrity": "sha512-4JfEd/TJaDArp5Jpr3N/aYHp+QR71XzZRKqU4u7WkGKmnGt28Qfh2IWGB/E2MvIFxa6CjIiQMxN2hnkNr49JAQ==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@polkadot/networks": "12.2.2", + "@polkadot/util": "12.2.2", + "@polkadot/wasm-crypto": "^7.2.1", + "@polkadot/wasm-util": "^7.2.1", + "@polkadot/x-bigint": "12.2.2", + "@polkadot/x-randomvalues": "12.2.2", + "@scure/base": "1.1.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.2.2" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz", + "integrity": "sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz", + "integrity": "sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.2.1", + "@polkadot/wasm-crypto-asmjs": "7.2.1", + "@polkadot/wasm-crypto-init": "7.2.1", + "@polkadot/wasm-crypto-wasm": "7.2.1", + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz", + "integrity": "sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz", + "integrity": "sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.2.1", + "@polkadot/wasm-crypto-asmjs": "7.2.1", + "@polkadot/wasm-crypto-wasm": "7.2.1", + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz", + "integrity": "sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz", + "integrity": "sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.2.2.tgz", + "integrity": "sha512-KSe7WAqwI1tubi0m5CP4oqf8EIjABZXLGkTHXKwjtAAMa9Q7hqFmVG2sXfvC+XSnhto1UKMe52TjuPrYSJI+jg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.2.2.tgz", + "integrity": "sha512-A3ttQp9oE6QH9VsggdQsBsgc9zyalxHoVXhZsn6yqcjzc9AoaY5QevezxVy88ZQpRp3bsYVn0RqyBV7eFq8WPw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "node-fetch": "^3.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-global": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.2.2.tgz", + "integrity": "sha512-hLVoKR9fGhZdy/eK/LHTyh4jJ3V+3VfcxbCey0k2t1Byrwbmsi6wL3NUQk6i3NviswR9OSCic9mhgDQPRBXZEg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.2.2.tgz", + "integrity": "sha512-eExiOT/up5ZzwHJkFpGhQ6sCdPSJnn6PJsQnyJMEdgPaUES70u/wWMLGFNiy3U8rRRVSsZi6rc9Unsr02LczzA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.2.2", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.2.2.tgz", + "integrity": "sha512-Rsvsc7ZLBKT1rls8gdbvzLLEs2sGUA8cDiTaQUkCHJN3ja/37Bppz1wNPcEIMsJ2pyL6bwq86HB0xmC28QVdqA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.2.2.tgz", + "integrity": "sha512-g6bX4DTBmkr3QLNeihlrHYvaZCKu1kFiK+BDQXVzBg+oHpzxz5wSVhzsG3GEVoVszXMiugWpSn03wCIvaRFMoQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "12.2.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.2.2.tgz", + "integrity": "sha512-kZtdfRHsgpJ+HV/jY8mQG4BFpCIz6NxZlrRKzWdaIacPVeXHkV3nfk7i9ghK+MP/nWC0AKuq06yysp9ZwWMCug==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "12.2.2", + "tslib": "^2.5.3", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@stellar/js-xdr": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", + "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", + "license": "Apache-2.0" + }, + "node_modules/@stellar/stellar-base": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-12.1.1.tgz", + "integrity": "sha512-gOBSOFDepihslcInlqnxKZdIW9dMUO1tpOm3AtJR33K2OvpXG6SaVHCzAmCFArcCqI9zXTEiSoh70T48TmiHJA==", + "license": "Apache-2.0", + "dependencies": { + "@stellar/js-xdr": "^3.1.2", + "base32.js": "^0.1.0", + "bignumber.js": "^9.1.2", + "buffer": "^6.0.3", + "sha.js": "^2.3.6", + "tweetnacl": "^1.0.3" + }, + "optionalDependencies": { + "sodium-native": "^4.1.1" + } + }, + "node_modules/@stellar/stellar-sdk": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-12.3.0.tgz", + "integrity": "sha512-F2DYFop/M5ffXF0lvV5Ezjk+VWNKg0QDX8gNhwehVU3y5LYA3WAY6VcCarMGPaG9Wdgoeh1IXXzOautpqpsltw==", + "license": "Apache-2.0", + "dependencies": { + "@stellar/stellar-base": "^12.1.1", + "axios": "^1.7.7", + "bignumber.js": "^9.1.2", + "eventsource": "^2.0.2", + "randombytes": "^2.1.0", + "toml": "^3.0.0", + "urijs": "^1.19.1" + } + }, + "node_modules/@stellar/stellar-sdk/node_modules/axios": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@substrate/connect": { + "version": "0.7.26", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.26.tgz", + "integrity": "sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw==", + "deprecated": "versions below 1.x are no longer maintained", + "license": "GPL-3.0-only", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^1.0.1", + "eventemitter3": "^4.0.7", + "smoldot": "1.0.4" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", + "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/connect/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT", + "optional": true + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz", + "integrity": "sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA==", + "license": "Apache-2.0" + }, + "node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.2.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", + "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.7" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/bare-addon-resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.0.tgz", + "integrity": "sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-module-resolve": "^1.10.0", + "bare-semver": "^1.0.0" + }, + "peerDependencies": { + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-module-resolve": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.1.tgz", + "integrity": "sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-semver": "^1.0.0" + }, + "peerDependencies": { + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-semver": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.2.tgz", + "integrity": "sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/base32.js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "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/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "license": "ISC", + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, + "node_modules/blake": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blake/-/blake-1.0.1.tgz", + "integrity": "sha512-vX8JQdac+BNxW4clj/rJk0ooLBXTdt462V2QSz5GMNJ8RhYK48n7cW/UyUvuXGMiqctsd7i1xIio4vI8O/j+Fw==", + "license": "MIT", + "dependencies": { + "cop": "^0.3.6", + "event-stream": "^3.3.1", + "fstream": "^1.0.7", + "lru-cache": "^2.6.5", + "mkdirp": "^0.5.1", + "popfun": "^1.0.0", + "prettydate": "0.0.1" + }, + "bin": { + "blake": "bin/cli.js" + }, + "engines": { + "node": ">0.10" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "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": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "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/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/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" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/cop": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cop/-/cop-0.3.6.tgz", + "integrity": "sha512-JB+js3riedeJxEmea7HZbV0xI4+j0/WHVYststvqtOgcEWg+bHNuYY9o1DJCeQEUY6XusaQe9CWHZP3z/zhJOA==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "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": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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, + "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/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "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-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.3.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-standard": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "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" + } + ], + "license": "MIT", + "peerDependencies": { + "eslint": "^7.12.1", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1 || ^5.0.0" + } + }, + "node_modules/eslint-config-standard-jsx": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", + "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", + "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" + } + ], + "license": "MIT", + "peerDependencies": { + "eslint": "^7.12.1", + "eslint-plugin-react": "^7.21.5" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", + "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.6.2", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.6.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.4", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.11.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz", + "integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz", + "integrity": "sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.3", + "array.prototype.flatmap": "^1.2.4", + "doctrine": "^2.1.0", + "estraverse": "^5.2.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.4", + "object.fromentries": "^2.0.4", + "object.hasown": "^1.0.0", + "object.values": "^1.1.4", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "string.prototype.matchall": "^4.0.5" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "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" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "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" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "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/event-stream": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", + "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, + "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/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "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==", + "dev": true, + "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, + "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, + "license": "MIT" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "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.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "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/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "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": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "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/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.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-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.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.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "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, + "license": "ISC" + }, + "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": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "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, + "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, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + }, + "engines": { + "node": ">=4.0" + } + }, + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==", + "license": "ISC" + }, + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "license": "MIT" + }, + "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": ">= 0.4" + } + }, + "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": { + "mime-db": "1.52.0" + }, + "engines": { + "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==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mock-socket": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", + "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nock": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", + "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)", + "optional": true + }, + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "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" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "license": [ + "MIT", + "Apache2" + ], + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/popfun": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/popfun/-/popfun-1.0.0.tgz", + "integrity": "sha512-8TOxvFAIyxvxNn8ca7RXtPJi7bPrBH77H8+yRWKgvV/5SJUAu5XXCDGtTf7UrezQq+WkZJgI9fXv/irfoPSQgg==", + "license": "MIT", + "engines": { + "node": ">0.10" + } + }, + "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/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/prettydate": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/prettydate/-/prettydate-0.0.1.tgz", + "integrity": "sha512-kkTd6fDCxmiTyod7+rW9LXnvkw3G6CrSYcVokhlJ9mr+Zb4DcbcgIlVIhj9I3Mg9AhZDIlkk1MdAJTBP6jwUhw==", + "engines": { + "node": "*" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-addon": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", + "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-addon-resolve": "^1.3.0" + }, + "engines": { + "bare": ">=1.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "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/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smoldot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.4.tgz", + "integrity": "sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "optional": true, + "dependencies": { + "pako": "^2.0.4", + "ws": "^8.8.1" + } + }, + "node_modules/sodium-native": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz", + "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", + "license": "MIT", + "optional": true, + "dependencies": { + "require-addon": "^1.1.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/standard": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz", + "integrity": "sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "eslint": "~7.18.0", + "eslint-config-standard": "16.0.3", + "eslint-config-standard-jsx": "10.0.0", + "eslint-plugin-import": "~2.24.2", + "eslint-plugin-node": "~11.1.0", + "eslint-plugin-promise": "~5.1.0", + "eslint-plugin-react": "~7.25.1", + "standard-engine": "^14.0.1" + }, + "bin": { + "standard": "bin/cmd.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/standard-engine": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", + "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", + "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" + } + ], + "license": "MIT", + "dependencies": { + "get-stdin": "^8.0.0", + "minimist": "^1.2.5", + "pkg-conf": "^3.1.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8.10" + } + }, + "node_modules/stream-combiner": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, + "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==", + "dev": true, + "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/string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "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/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "license": "MIT" + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "license": "0BSD" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, + "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/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "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/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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": { + "punycode": "^2.1.0" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "license": "MIT" + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "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" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "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.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "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/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/scripts/package.json b/scripts/package.json index 92b88f48d..0d79b89f9 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@polkadot/api": "^10.7.2", + "@stellar/stellar-sdk": "^12.3.0", "axios": "^0.25.0", "bip39": "^3.0.3", "blake": "^1.0.1", diff --git a/scripts/wait_for_node.js b/scripts/wait_for_node.js new file mode 100644 index 000000000..1bce165ed --- /dev/null +++ b/scripts/wait_for_node.js @@ -0,0 +1,55 @@ +#!/usr/bin/env node +/** + * wait_for_node.js + * + * Polls a WebSocket endpoint until it accepts connections, then exits 0. + * Used by Make targets to block until TFChain is ready before running setup. + * + * Usage: + * node scripts/wait_for_node.js + * TFCHAIN_URL=ws://localhost:9944 WAIT_TIMEOUT_MS=30000 node scripts/wait_for_node.js + */ + +'use strict' + +const { ApiPromise, WsProvider } = require('@polkadot/api') + +const url = process.env.TFCHAIN_URL || 'ws://localhost:9944' +const timeoutMs = parseInt(process.env.WAIT_TIMEOUT_MS || '60000') +const intervalMs = 1500 + +async function tryConnect () { + return new Promise((resolve) => { + const provider = new WsProvider(url, false) + const timer = setTimeout(() => { provider.disconnect(); resolve(false) }, 4000) + provider.on('connected', async () => { + clearTimeout(timer) + try { + const api = await ApiPromise.create({ provider, noInitWarn: true }) + await api.disconnect() + resolve(true) + } catch { + resolve(false) + } + }) + provider.on('error', () => { clearTimeout(timer); resolve(false) }) + provider.connect() + }) +} + +async function main () { + const deadline = Date.now() + timeoutMs + process.stdout.write(`[wait] Waiting for TFChain at ${url}`) + while (Date.now() < deadline) { + if (await tryConnect()) { + console.log(' ready.') + process.exit(0) + } + process.stdout.write('.') + await new Promise(r => setTimeout(r, intervalMs)) + } + console.log('\n[wait] Timed out waiting for TFChain.') + process.exit(1) +} + +main() diff --git a/scripts/yarn.lock b/scripts/yarn.lock index 8b74be261..255fbaabd 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -4,19 +4,19 @@ "@babel/code-frame@^7.0.0": version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz" integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== dependencies: "@babel/highlight" "^7.22.5" "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== "@babel/highlight@^7.22.5": version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz" integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== dependencies: "@babel/helper-validator-identifier" "^7.22.5" @@ -25,7 +25,7 @@ "@eslint/eslintrc@^0.3.0": version "0.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz" integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== dependencies: ajv "^6.12.4" @@ -41,19 +41,19 @@ "@noble/curves@1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" + resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== dependencies: "@noble/hashes" "1.3.1" -"@noble/hashes@1.3.1", "@noble/hashes@^1.2.0": +"@noble/hashes@^1.2.0", "@noble/hashes@1.3.1": version "1.3.1" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz" integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== "@polkadot/api-augment@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-10.8.1.tgz#585b93ef9d09c114b57a8794574a429386c94660" + resolved "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.8.1.tgz" integrity sha512-KFfF0OESmFI8hFmuKGuU204+S4SORIxniZr88xUnEPyJQr4R6XYnbGSKcLJM5Y2MK8a7JEoKgg+hfnUTK6Se0w== dependencies: "@polkadot/api-base" "10.8.1" @@ -66,7 +66,7 @@ "@polkadot/api-base@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-10.8.1.tgz#c6df0ff420c1af48ec58c823681d6c342d7b56f5" + resolved "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.8.1.tgz" integrity sha512-13BZ04UtiCECQshstL9RBLDJ6nq9HSwWXwMuWZcXUEPSsPhfR3iT0o212dtGrGliakYWgGEU1LGJuGhZ5iK7TA== dependencies: "@polkadot/rpc-core" "10.8.1" @@ -77,7 +77,7 @@ "@polkadot/api-derive@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-10.8.1.tgz#eab3fa9ef975bccad5ab0d5275699f42b51725c7" + resolved "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.8.1.tgz" integrity sha512-r1SBY9vu6OZMGp8/KZFwOqh7yS8yl0YbNDWuju2BEMWQ4Xx6WOlQjQV8Np9UFtKcnBFQzQjMLWH3vwrfTDgVEQ== dependencies: "@polkadot/api" "10.8.1" @@ -91,9 +91,9 @@ rxjs "^7.8.1" tslib "^2.5.3" -"@polkadot/api@10.8.1", "@polkadot/api@^10.7.2": +"@polkadot/api@^10.7.2", "@polkadot/api@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-10.8.1.tgz#ecf4e8a7167d67ba1392ba0b93133c701e088280" + resolved "https://registry.npmjs.org/@polkadot/api/-/api-10.8.1.tgz" integrity sha512-Txx1bXmB4FHghzPZ+OVQk6oYgPE03bhwMNiXzmC8Ia/tw5aoFnko2FFl+Y1pEhhMKDmqfyVe4L+HxPjfEQbsfA== dependencies: "@polkadot/api-augment" "10.8.1" @@ -116,16 +116,16 @@ "@polkadot/keyring@^12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-12.2.2.tgz#4efb5333c78222a91949b699d4a65b338c79eede" + resolved "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.2.2.tgz" integrity sha512-z8MVdgrhzg/bFiR2i5/W06Ma+IPeisH7EtGuIQ+ZwXiCJlXMAGUy5spfk3fUbXYubCCqNycqFgKTYDM/rDhXSg== dependencies: "@polkadot/util" "12.2.2" "@polkadot/util-crypto" "12.2.2" tslib "^2.5.3" -"@polkadot/networks@12.2.2", "@polkadot/networks@^12.2.2": +"@polkadot/networks@^12.2.2", "@polkadot/networks@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-12.2.2.tgz#14b34210ea2dfc3b27897b579eb93c5f0a8f2a1c" + resolved "https://registry.npmjs.org/@polkadot/networks/-/networks-12.2.2.tgz" integrity sha512-SsZognHwXyD2saJkB35G+28noAZBcNpJAXsTI7QTTDHGiQSDp0mPmrk3Rt7BRAeFn4qdXQuRqQYKYUwBM2i9mQ== dependencies: "@polkadot/util" "12.2.2" @@ -134,7 +134,7 @@ "@polkadot/rpc-augment@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-10.8.1.tgz#19bbfdf78ca5b6d493aee7b954bb4a526be6ebe7" + resolved "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.8.1.tgz" integrity sha512-FmXAQLyG8cwBI+MwMxxx4qttolR2gFnYXC7PjYrrjYq4AZrrGWd9SvwXx8aA/NLRJ/PJqvri4dsoKPe7NiE+1A== dependencies: "@polkadot/rpc-core" "10.8.1" @@ -145,7 +145,7 @@ "@polkadot/rpc-core@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-10.8.1.tgz#1bc8f7f840164bf3f03fe71851071c7f19f4f166" + resolved "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.8.1.tgz" integrity sha512-GTMYBBssiP6wyYvc8hB0glQc4VUneGxiSYjWGijh9NEl/JVBpU01jcK3dfx534AWptctJN1Vk2fWzhaDgnj8zA== dependencies: "@polkadot/rpc-augment" "10.8.1" @@ -157,7 +157,7 @@ "@polkadot/rpc-provider@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-10.8.1.tgz#7455b284934151bcc20e89d9605cb09186cea74a" + resolved "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.8.1.tgz" integrity sha512-yQdUmaWRMSa/qVGBRP1vGjdv4DnlaYOctJfRpz2MWPbEckH5DmPRxV4BAZ9FVa5lATX0Qkmr3uvBt3qApH7xhQ== dependencies: "@polkadot/keyring" "^12.2.2" @@ -177,7 +177,7 @@ "@polkadot/types-augment@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-10.8.1.tgz#e774f3ba399f9f8961a5f557fb5a9c7c5901625a" + resolved "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.8.1.tgz" integrity sha512-rVn8aA4u6YPcxGEnBq2rXVmgXM5kSuiTHIjsusb6Sm3PzO//NcC/TW9sbZjlAJApgSoj9iagM7Y85OPGOZlxwg== dependencies: "@polkadot/types" "10.8.1" @@ -187,7 +187,7 @@ "@polkadot/types-codec@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-10.8.1.tgz#65f886fd2b717e2e12b319a395f9887edd1f9094" + resolved "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.8.1.tgz" integrity sha512-8dj4T6GA6JxuwUNShO70omZ4qkChwsJeGAJg5x09UeLEAwBS02BkFSllRUJjGEwnAUb/Iq4s3NBVmYiiZ/wmKg== dependencies: "@polkadot/util" "^12.2.2" @@ -196,7 +196,7 @@ "@polkadot/types-create@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-10.8.1.tgz#f5974a00918e2c4b7fca29c18abd3410536393ad" + resolved "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.8.1.tgz" integrity sha512-v2WZHQAjf8TiLipRkR1iPTyWSjGHJJP2SQ5uVO5UJlHilpE8lODqY1rr/9hGN+sbRhU0vEy6ZceDEKuNbtJB3Q== dependencies: "@polkadot/types-codec" "10.8.1" @@ -205,7 +205,7 @@ "@polkadot/types-known@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-10.8.1.tgz#f630d3354cbe80149360edb37c569c5042eced12" + resolved "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.8.1.tgz" integrity sha512-AIeuF7eTIEnUgxa1pU0UMmF/tIXgucAECwU8vzoKeJLrYWA16VYUm0Pst9e3jK3PyLaCneMRyR00Lc7oxVANbw== dependencies: "@polkadot/networks" "^12.2.2" @@ -217,7 +217,7 @@ "@polkadot/types-support@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-10.8.1.tgz#b299f829374ce77fdfbe1d1b8faa14ba02969783" + resolved "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.8.1.tgz" integrity sha512-arDVaL70vzVL5JBGWW1qcOASn1cJ/UxNMR3fHchoVkAqS20VIrehE8MF4zXMdjcP0Ak3+6E0FaSmHMTKlmEJsg== dependencies: "@polkadot/util" "^12.2.2" @@ -225,7 +225,7 @@ "@polkadot/types@10.8.1": version "10.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-10.8.1.tgz#83c01c347189ff97b98b34a5a4aba27c715539eb" + resolved "https://registry.npmjs.org/@polkadot/types/-/types-10.8.1.tgz" integrity sha512-m6UvsvQOZ7sRGbonb6QLs4mZ6TmYKdAXAcHakiJl2xArqsgOghJsKhgaTqcigPkSq4947MXtIkEzdrwFEnkYkQ== dependencies: "@polkadot/keyring" "^12.2.2" @@ -237,9 +237,9 @@ rxjs "^7.8.1" tslib "^2.5.3" -"@polkadot/util-crypto@12.2.2", "@polkadot/util-crypto@^12.2.2": +"@polkadot/util-crypto@^12.2.2", "@polkadot/util-crypto@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-12.2.2.tgz#7e6ab56482d3dfb8704a724d695028677799c685" + resolved "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.2.2.tgz" integrity sha512-4JfEd/TJaDArp5Jpr3N/aYHp+QR71XzZRKqU4u7WkGKmnGt28Qfh2IWGB/E2MvIFxa6CjIiQMxN2hnkNr49JAQ== dependencies: "@noble/curves" "1.1.0" @@ -253,9 +253,9 @@ "@scure/base" "1.1.1" tslib "^2.5.3" -"@polkadot/util@12.2.2", "@polkadot/util@^12.2.2": +"@polkadot/util@*", "@polkadot/util@^12.2.2", "@polkadot/util@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-12.2.2.tgz#f586fd62c330a09bb026b1584be1bb07c8b27b6b" + resolved "https://registry.npmjs.org/@polkadot/util/-/util-12.2.2.tgz" integrity sha512-u/v5Z2+iUwX/CXEMVZgJmwqqx1kT5Zfxsio3vpuYaPCg49xhTKqAcrakgB+1BUHhhyF3Zkb9uG73JWFR0Lkk9w== dependencies: "@polkadot/x-bigint" "12.2.2" @@ -268,7 +268,7 @@ "@polkadot/wasm-bridge@7.2.1": version "7.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz#8464a96552207d2b49c6f32137b24132534b91ee" + resolved "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz" integrity sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A== dependencies: "@polkadot/wasm-util" "7.2.1" @@ -276,14 +276,14 @@ "@polkadot/wasm-crypto-asmjs@7.2.1": version "7.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz#3e7a91e2905ab7354bc37b82f3e151a62bb024db" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz" integrity sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg== dependencies: tslib "^2.5.0" "@polkadot/wasm-crypto-init@7.2.1": version "7.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz#9dbba41ed7d382575240f1483cf5a139ff2787bd" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz" integrity sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw== dependencies: "@polkadot/wasm-bridge" "7.2.1" @@ -294,7 +294,7 @@ "@polkadot/wasm-crypto-wasm@7.2.1": version "7.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz#d2486322c725f6e5d2cc2d6abcb77ecbbaedc738" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz" integrity sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA== dependencies: "@polkadot/wasm-util" "7.2.1" @@ -302,7 +302,7 @@ "@polkadot/wasm-crypto@^7.2.1": version "7.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz#db671dcb73f1646dc13478b5ffc3be18c64babe1" + resolved "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz" integrity sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q== dependencies: "@polkadot/wasm-bridge" "7.2.1" @@ -312,16 +312,16 @@ "@polkadot/wasm-util" "7.2.1" tslib "^2.5.0" -"@polkadot/wasm-util@7.2.1", "@polkadot/wasm-util@^7.2.1": +"@polkadot/wasm-util@*", "@polkadot/wasm-util@^7.2.1", "@polkadot/wasm-util@7.2.1": version "7.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz#fda233120ec02f77f0d14e4d3c7ad9ce06535fb8" + resolved "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz" integrity sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og== dependencies: tslib "^2.5.0" -"@polkadot/x-bigint@12.2.2", "@polkadot/x-bigint@^12.2.2": +"@polkadot/x-bigint@^12.2.2", "@polkadot/x-bigint@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-12.2.2.tgz#18ff80c306b486fb926702ba9bb56291fb69d4f1" + resolved "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.2.2.tgz" integrity sha512-KSe7WAqwI1tubi0m5CP4oqf8EIjABZXLGkTHXKwjtAAMa9Q7hqFmVG2sXfvC+XSnhto1UKMe52TjuPrYSJI+jg== dependencies: "@polkadot/x-global" "12.2.2" @@ -329,23 +329,23 @@ "@polkadot/x-fetch@^12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-12.2.2.tgz#452b096a3233308a1cbbeae867c26a374b62b9e8" + resolved "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.2.2.tgz" integrity sha512-A3ttQp9oE6QH9VsggdQsBsgc9zyalxHoVXhZsn6yqcjzc9AoaY5QevezxVy88ZQpRp3bsYVn0RqyBV7eFq8WPw== dependencies: "@polkadot/x-global" "12.2.2" node-fetch "^3.3.1" tslib "^2.5.3" -"@polkadot/x-global@12.2.2", "@polkadot/x-global@^12.2.2": +"@polkadot/x-global@^12.2.2", "@polkadot/x-global@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-12.2.2.tgz#dda816c00738b72209637e623b50ecad5ce234bf" + resolved "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.2.2.tgz" integrity sha512-hLVoKR9fGhZdy/eK/LHTyh4jJ3V+3VfcxbCey0k2t1Byrwbmsi6wL3NUQk6i3NviswR9OSCic9mhgDQPRBXZEg== dependencies: tslib "^2.5.3" -"@polkadot/x-randomvalues@12.2.2": +"@polkadot/x-randomvalues@*", "@polkadot/x-randomvalues@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-12.2.2.tgz#c249d990f3033b0e9ea4a7964419f04d47b0d228" + resolved "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.2.2.tgz" integrity sha512-eExiOT/up5ZzwHJkFpGhQ6sCdPSJnn6PJsQnyJMEdgPaUES70u/wWMLGFNiy3U8rRRVSsZi6rc9Unsr02LczzA== dependencies: "@polkadot/x-global" "12.2.2" @@ -353,7 +353,7 @@ "@polkadot/x-textdecoder@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-12.2.2.tgz#9e3c7b17f6a8e032aa3ab906fcff3037aeecaa4c" + resolved "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.2.2.tgz" integrity sha512-Rsvsc7ZLBKT1rls8gdbvzLLEs2sGUA8cDiTaQUkCHJN3ja/37Bppz1wNPcEIMsJ2pyL6bwq86HB0xmC28QVdqA== dependencies: "@polkadot/x-global" "12.2.2" @@ -361,7 +361,7 @@ "@polkadot/x-textencoder@12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-12.2.2.tgz#6a40a953774093a070f2819959f054f258c001af" + resolved "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.2.2.tgz" integrity sha512-g6bX4DTBmkr3QLNeihlrHYvaZCKu1kFiK+BDQXVzBg+oHpzxz5wSVhzsG3GEVoVszXMiugWpSn03wCIvaRFMoQ== dependencies: "@polkadot/x-global" "12.2.2" @@ -369,7 +369,7 @@ "@polkadot/x-ws@^12.2.2": version "12.2.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-12.2.2.tgz#41d7645507137e5f13abb9536c18c840f7e86324" + resolved "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.2.2.tgz" integrity sha512-kZtdfRHsgpJ+HV/jY8mQG4BFpCIz6NxZlrRKzWdaIacPVeXHkV3nfk7i9ghK+MP/nWC0AKuq06yysp9ZwWMCug== dependencies: "@polkadot/x-global" "12.2.2" @@ -378,17 +378,49 @@ "@scure/base@1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@stellar/js-xdr@^3.1.2": + version "3.1.2" + resolved "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz" + integrity sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ== + +"@stellar/stellar-base@^12.1.1": + version "12.1.1" + resolved "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-12.1.1.tgz" + integrity sha512-gOBSOFDepihslcInlqnxKZdIW9dMUO1tpOm3AtJR33K2OvpXG6SaVHCzAmCFArcCqI9zXTEiSoh70T48TmiHJA== + dependencies: + "@stellar/js-xdr" "^3.1.2" + base32.js "^0.1.0" + bignumber.js "^9.1.2" + buffer "^6.0.3" + sha.js "^2.3.6" + tweetnacl "^1.0.3" + optionalDependencies: + sodium-native "^4.1.1" + +"@stellar/stellar-sdk@^12.3.0": + version "12.3.0" + resolved "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-12.3.0.tgz" + integrity sha512-F2DYFop/M5ffXF0lvV5Ezjk+VWNKg0QDX8gNhwehVU3y5LYA3WAY6VcCarMGPaG9Wdgoeh1IXXzOautpqpsltw== + dependencies: + "@stellar/stellar-base" "^12.1.1" + axios "^1.7.7" + bignumber.js "^9.1.2" + eventsource "^2.0.2" + randombytes "^2.1.0" + toml "^3.0.0" + urijs "^1.19.1" + "@substrate/connect-extension-protocol@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz#fa5738039586c648013caa6a0c95c43265dbe77d" + resolved "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz" integrity sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg== "@substrate/connect@0.7.26": version "0.7.26" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.26.tgz#a0ee5180c9cb2f29250d1219a32f7b7e7dea1196" + resolved "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.26.tgz" integrity sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw== dependencies: "@substrate/connect-extension-protocol" "^1.0.1" @@ -397,39 +429,39 @@ "@substrate/ss58-registry@^1.40.0": version "1.40.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz#2223409c496271df786c1ca8496898896595441e" + resolved "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz" integrity sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA== "@types/bn.js@^5.1.1": version "5.1.1" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz" integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*": version "20.2.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" + resolved "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz" integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== acorn-jsx@^5.3.1: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^7.4.0: +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^7.4.0: version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -439,7 +471,7 @@ ajv@^6.10.0, ajv@^6.12.4: ajv@^8.0.1: version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" @@ -449,38 +481,38 @@ ajv@^8.0.1: ansi-colors@^4.1.1: version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" argparse@^1.0.7: version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" array-buffer-byte-length@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz" integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== dependencies: call-bind "^1.0.2" @@ -488,7 +520,7 @@ array-buffer-byte-length@^1.0.0: array-includes@^3.1.3, array-includes@^3.1.5: version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz" integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" @@ -499,7 +531,7 @@ array-includes@^3.1.3, array-includes@^3.1.5: array.prototype.flat@^1.2.4: version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz" integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" @@ -509,7 +541,7 @@ array.prototype.flat@^1.2.4: array.prototype.flatmap@^1.2.4: version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz" integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== dependencies: call-bind "^1.0.2" @@ -519,36 +551,87 @@ array.prototype.flatmap@^1.2.4: astral-regex@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +available-typed-arrays@^1.0.5, available-typed-arrays@^1.0.7: + 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== + dependencies: + possible-typed-array-names "^1.0.0" axios@^0.25.0: version "0.25.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + resolved "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz" integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== dependencies: follow-redirects "^1.14.7" +axios@^1.7.7: + version "1.13.6" + resolved "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz" + integrity sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ== + dependencies: + follow-redirects "^1.15.11" + form-data "^4.0.5" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bare-addon-resolve@^1.3.0: + version "1.10.0" + resolved "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.0.tgz" + integrity sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA== + dependencies: + bare-module-resolve "^1.10.0" + bare-semver "^1.0.0" + +bare-module-resolve@^1.10.0: + version "1.12.1" + resolved "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.1.tgz" + integrity sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg== + dependencies: + bare-semver "^1.0.0" + +bare-semver@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.2.tgz" + integrity sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA== + +base32.js@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz" + integrity sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bignumber.js@^9.1.2: + version "9.3.1" + resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz" + integrity sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ== + bip39@^3.0.3: version "3.1.0" - resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.1.0.tgz#c55a418deaf48826a6ceb34ac55b3ee1577e18a3" + resolved "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz" integrity sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A== dependencies: "@noble/hashes" "^1.2.0" blake@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/blake/-/blake-1.0.1.tgz#6c8cff58e5217c86329a49c61e72cda3eb83862b" + resolved "https://registry.npmjs.org/blake/-/blake-1.0.1.tgz" integrity sha512-vX8JQdac+BNxW4clj/rJk0ooLBXTdt462V2QSz5GMNJ8RhYK48n7cW/UyUvuXGMiqctsd7i1xIio4vI8O/j+Fw== dependencies: cop "^0.3.6" @@ -561,33 +644,59 @@ blake@^1.0.1: bn.js@^5.1.3, bn.js@^5.2.1: version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" -call-bind@^1.0.0, call-bind@^1.0.2: +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== chalk@^2.0.0: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -596,7 +705,7 @@ chalk@^2.0.0: chalk@^4.0.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -604,41 +713,48 @@ chalk@^4.0.0: color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== cop@^0.3.6: version "0.3.6" - resolved "https://registry.yarnpkg.com/cop/-/cop-0.3.6.tgz#8f1485eb26b5481a45ef0f058e69699e16cbbeac" + resolved "https://registry.npmjs.org/cop/-/cop-0.3.6.tgz" integrity sha512-JB+js3riedeJxEmea7HZbV0xI4+j0/WHVYststvqtOgcEWg+bHNuYY9o1DJCeQEUY6XusaQe9CWHZP3z/zhJOA== cross-spawn@^7.0.2: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -647,84 +763,107 @@ cross-spawn@^7.0.2: data-uri-to-buffer@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz" integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== debug@^2.6.9: version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@^3.2.7: version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" deep-is@^0.1.3: version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +define-data-property@^1.1.4: + 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== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== enquirer@^2.3.5: version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz" integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== dependencies: array-buffer-byte-length "^1.0.0" @@ -762,25 +901,43 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.9" -es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" - has-tostringtag "^1.0.0" + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.1, es-set-tostringtag@^2.1.0: + 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== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" es-shim-unscopables@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== dependencies: has "^1.0.3" es-to-primitive@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" @@ -789,22 +946,22 @@ es-to-primitive@^1.2.1: escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== eslint-config-standard-jsx@10.0.0: version "10.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz#dc24992661325a2e480e2c3091d669f19034e18d" + resolved "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz" integrity sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA== eslint-config-standard@16.0.3: version "16.0.3" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz#6c8761e544e96c531ff92642eeb87842b8488516" + resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz" integrity sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg== eslint-import-resolver-node@^0.3.6: version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz" integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" @@ -813,22 +970,22 @@ eslint-import-resolver-node@^0.3.6: eslint-module-utils@^2.6.2: version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz" integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" eslint-plugin-es@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + resolved "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz" integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== dependencies: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@~2.24.2: +eslint-plugin-import@^2.22.1, eslint-plugin-import@~2.24.2: version "2.24.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz#2c8cd2e341f3885918ee27d18479910ade7bb4da" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz" integrity sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q== dependencies: array-includes "^3.1.3" @@ -847,9 +1004,9 @@ eslint-plugin-import@~2.24.2: resolve "^1.20.0" tsconfig-paths "^3.11.0" -eslint-plugin-node@~11.1.0: +eslint-plugin-node@^11.1.0, eslint-plugin-node@~11.1.0: version "11.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + resolved "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz" integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== dependencies: eslint-plugin-es "^3.0.0" @@ -859,14 +1016,14 @@ eslint-plugin-node@~11.1.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-promise@~5.1.0: +"eslint-plugin-promise@^4.2.1 || ^5.0.0", eslint-plugin-promise@~5.1.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz#9674d11c056d1bafac38e4a3a9060be740988d90" + resolved "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz" integrity sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA== -eslint-plugin-react@~7.25.1: +eslint-plugin-react@^7.21.5, eslint-plugin-react@~7.25.1: version "7.25.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz#3333a974772745ddb3aecea84621019b635766bc" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz" integrity sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w== dependencies: array-includes "^3.1.3" @@ -885,7 +1042,7 @@ eslint-plugin-react@~7.25.1: eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" @@ -893,24 +1050,29 @@ eslint-scope@^5.1.1: eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: +eslint-visitor-keys@^1.1.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint-visitor-keys@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@~7.18.0: +"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0", "eslint@^3 || ^4 || ^5 || ^6 || ^7", eslint@^7.0.0, eslint@^7.12.1, eslint@>=4.19.1, eslint@>=5.16.0, eslint@~7.18.0: version "7.18.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.18.0.tgz#7fdcd2f3715a41fe6295a16234bd69aed2c75e67" + resolved "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz" integrity sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ== dependencies: "@babel/code-frame" "^7.0.0" @@ -953,7 +1115,7 @@ eslint@~7.18.0: espree@^7.3.0, espree@^7.3.1: version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz" integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" @@ -962,41 +1124,41 @@ espree@^7.3.0, espree@^7.3.1: esprima@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.2.0: version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== event-stream@^3.3.1: version "3.3.5" - resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.5.tgz#e5dd8989543630d94c6cf4d657120341fa31636b" + resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz" integrity sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g== dependencies: duplexer "^0.1.1" @@ -1009,32 +1171,37 @@ event-stream@^3.3.1: eventemitter3@^4.0.7: version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== eventemitter3@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== +eventsource@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz" + integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== + fast-deep-equal@^3.1.1: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz" integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== dependencies: node-domexception "^1.0.0" @@ -1042,28 +1209,28 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: file-entry-cache@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== dependencies: locate-path "^2.0.0" find-up@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" flat-cache@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: flatted "^3.1.0" @@ -1071,41 +1238,52 @@ flat-cache@^3.0.4: flatted@^3.1.0: version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.14.7: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.14.7, follow-redirects@^1.15.11: + version "1.15.11" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +for-each@^0.3.3, for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== +form-data@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== dependencies: - is-callable "^1.1.3" + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" formdata-polyfill@^4.0.10: version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== dependencies: fetch-blob "^3.1.2" from@^0.1.7: version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + resolved "https://registry.npmjs.org/from/-/from-0.1.7.tgz" integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fstream@^1.0.7: version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + resolved "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz" integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== dependencies: graceful-fs "^4.1.2" @@ -1113,14 +1291,14 @@ fstream@^1.0.7: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== function.prototype.name@^1.1.5: version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== dependencies: call-bind "^1.0.2" @@ -1130,32 +1308,46 @@ function.prototype.name@^1.1.5: functional-red-black-tree@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.4, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + 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" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-proto "^1.0.1" - has-symbols "^1.0.3" + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" get-stdin@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz" integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== get-symbol-description@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== dependencies: call-bind "^1.0.2" @@ -1163,14 +1355,14 @@ get-symbol-description@^1.0.0: glob-parent@^5.0.0: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob@^7.1.3: version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" @@ -1182,94 +1374,104 @@ glob@^7.1.3: globals@^12.1.0: version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + resolved "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz" integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== dependencies: type-fest "^0.8.1" globalthis@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== dependencies: define-properties "^1.1.3" -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== graceful-fs@^4.1.15, graceful-fs@^4.1.2: version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: - get-intrinsic "^1.1.1" + es-define-property "^1.0.0" has-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.0.2, has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - has-symbols "^1.0.2" + has-symbols "^1.0.3" has@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== dependencies: function-bind "^1.1.1" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ignore@^4.0.6: version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.1.1: version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -1277,25 +1479,25 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@~2.0.0: +inherits@^2.0.4, inherits@~2.0.0, inherits@2: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== internal-slot@^1.0.3, internal-slot@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: get-intrinsic "^1.2.0" @@ -1304,12 +1506,12 @@ internal-slot@^1.0.3, internal-slot@^1.0.5: ip-regex@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz" integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== dependencies: call-bind "^1.0.2" @@ -1318,75 +1520,75 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== dependencies: has-bigints "^1.0.1" is-boolean-object@^1.1.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: +is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.11.0, is-core-module@^2.6.0, is-core-module@^2.9.0: version "2.12.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz" integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== dependencies: has "^1.0.3" is-date-object@^1.0.1: version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-negative-zero@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" is-regex@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== dependencies: call-bind "^1.0.2" @@ -1394,56 +1596,57 @@ is-regex@^1.1.4: is-shared-array-buffer@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== dependencies: call-bind "^1.0.2" is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== dependencies: has-tostringtag "^1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== +is-typed-array@^1.1.10, is-typed-array@^1.1.14, is-typed-array@^1.1.9: + version "1.1.15" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.16" is-weakref@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -1451,39 +1654,39 @@ js-yaml@^3.13.1: json-parse-better-errors@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stringify-safe@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== json5@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" "jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.3" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== dependencies: array-includes "^3.1.5" @@ -1491,7 +1694,7 @@ json5@^1.0.2: levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -1499,7 +1702,7 @@ levn@^0.4.1: load-json-file@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== dependencies: graceful-fs "^4.1.2" @@ -1509,7 +1712,7 @@ load-json-file@^4.0.0: load-json-file@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz" integrity sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw== dependencies: graceful-fs "^4.1.15" @@ -1520,7 +1723,7 @@ load-json-file@^5.2.0: locate-path@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== dependencies: p-locate "^2.0.0" @@ -1528,7 +1731,7 @@ locate-path@^2.0.0: locate-path@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" @@ -1536,90 +1739,102 @@ locate-path@^3.0.0: lodash.truncate@^4.4.2: version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lru-cache@^2.6.5: version "2.7.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" integrity sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ== lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" map-stream@0.0.7: version "0.0.7" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" + resolved "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz" integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -"mkdirp@>=0.5 0", mkdirp@^0.5.1: +mkdirp@^0.5.1, "mkdirp@>=0.5 0": version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" mock-socket@^9.2.1: version "9.2.1" - resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.2.1.tgz#cc9c0810aa4d0afe02d721dcb2b7e657c00e2282" + resolved "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz" integrity sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag== moment@^2.29.1: version "2.29.4" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: +ms@^2.1.1, ms@2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== nock@^13.3.1: version "13.3.1" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.1.tgz#f22d4d661f7a05ebd9368edae1b5dc0a62d758fc" + resolved "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz" integrity sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw== dependencies: debug "^4.1.0" @@ -1629,12 +1844,12 @@ nock@^13.3.1: node-domexception@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== node-fetch@^3.3.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz" integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow== dependencies: data-uri-to-buffer "^4.0.0" @@ -1643,7 +1858,7 @@ node-fetch@^3.3.1: normalize-package-data@^2.3.2: version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -1653,22 +1868,22 @@ normalize-package-data@^2.3.2: object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.3, object.assign@^4.1.4: version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: call-bind "^1.0.2" @@ -1678,7 +1893,7 @@ object.assign@^4.1.3, object.assign@^4.1.4: object.entries@^1.1.4: version "1.1.6" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz" integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== dependencies: call-bind "^1.0.2" @@ -1687,7 +1902,7 @@ object.entries@^1.1.4: object.fromentries@^2.0.4: version "2.0.6" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz" integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== dependencies: call-bind "^1.0.2" @@ -1696,7 +1911,7 @@ object.fromentries@^2.0.4: object.hasown@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz" integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== dependencies: define-properties "^1.1.4" @@ -1704,7 +1919,7 @@ object.hasown@^1.0.0: object.values@^1.1.4: version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz" integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" @@ -1713,14 +1928,14 @@ object.values@^1.1.4: once@^1.3.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" optionator@^0.9.1: version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: deep-is "^0.1.3" @@ -1732,57 +1947,57 @@ optionator@^0.9.1: p-limit@^1.1.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^2.0.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== dependencies: p-limit "^1.1.0" p-locate@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-try@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pako@^2.0.4: version "2.1.0" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-json@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== dependencies: error-ex "^1.3.1" @@ -1790,51 +2005,51 @@ parse-json@^4.0.0: path-exists@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-type@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz" integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" pause-stream@^0.0.11: version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz" integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== dependencies: through "~2.3" pify@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== pify@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pkg-conf@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-3.1.0.tgz#d9f9c75ea1bae0e77938cde045b276dac7cc69ae" + resolved "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz" integrity sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ== dependencies: find-up "^3.0.0" @@ -1842,34 +2057,39 @@ pkg-conf@^3.1.0: pkg-up@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz" integrity sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg== dependencies: find-up "^2.1.0" popfun@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/popfun/-/popfun-1.0.0.tgz#49528e6c1b49a5efb7f41cf2131bc95cea4bccb4" + resolved "https://registry.npmjs.org/popfun/-/popfun-1.0.0.tgz" integrity sha512-8TOxvFAIyxvxNn8ca7RXtPJi7bPrBH77H8+yRWKgvV/5SJUAu5XXCDGtTf7UrezQq+WkZJgI9fXv/irfoPSQgg== +possible-typed-array-names@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== + prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettydate@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/prettydate/-/prettydate-0.0.1.tgz#cbe3624713cff4801f4aa0531ac3f9f224d33aa5" + resolved "https://registry.npmjs.org/prettydate/-/prettydate-0.0.1.tgz" integrity sha512-kkTd6fDCxmiTyod7+rW9LXnvkw3G6CrSYcVokhlJ9mr+Zb4DcbcgIlVIhj9I3Mg9AhZDIlkk1MdAJTBP6jwUhw== progress@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== prop-types@^15.7.2: version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -1878,22 +2098,34 @@ prop-types@^15.7.2: propagate@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + react-is@^16.13.1: version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== read-pkg-up@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz" integrity sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw== dependencies: find-up "^2.0.0" @@ -1901,7 +2133,7 @@ read-pkg-up@^3.0.0: read-pkg@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz" integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA== dependencies: load-json-file "^4.0.0" @@ -1910,7 +2142,7 @@ read-pkg@^3.0.0: regexp.prototype.flags@^1.4.3: version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz" integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" @@ -1919,22 +2151,29 @@ regexp.prototype.flags@^1.4.3: regexpp@^3.0.0, regexpp@^3.1.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +require-addon@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz" + integrity sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA== + dependencies: + bare-addon-resolve "^1.3.0" + require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve@^1.10.0, resolve@^1.10.1, resolve@^1.20.0, resolve@^1.22.1: version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz" integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: is-core-module "^2.11.0" @@ -1943,75 +2182,101 @@ resolve@^1.10.0, resolve@^1.10.1, resolve@^1.20.0, resolve@^1.22.1: resolve@^2.0.0-next.3: version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz" integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== dependencies: is-core-module "^2.9.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -rimraf@2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" +rimraf@2: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rxjs@^7.8.1: version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" +safe-buffer@^5.1.0, safe-buffer@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex-test@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== dependencies: call-bind "^1.0.2" get-intrinsic "^1.1.3" is-regex "^1.1.4" -"semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@^6.1.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.2.1: version "7.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz" integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw== dependencies: lru-cache "^6.0.0" +"semver@2 || 3 || 4 || 5": + version "5.7.1" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +set-function-length@^1.2.2: + 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== + 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" + +sha.js@^2.3.6: + version "2.4.12" + resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz" + integrity sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w== + dependencies: + inherits "^2.0.4" + safe-buffer "^5.2.1" + to-buffer "^1.2.0" + shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== side-channel@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: call-bind "^1.0.0" @@ -2020,7 +2285,7 @@ side-channel@^1.0.4: slice-ansi@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: ansi-styles "^4.0.0" @@ -2029,15 +2294,22 @@ slice-ansi@^4.0.0: smoldot@1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/smoldot/-/smoldot-1.0.4.tgz#e4c38cedad68d699a11b5b9ce72bb75c891bfd98" + resolved "https://registry.npmjs.org/smoldot/-/smoldot-1.0.4.tgz" integrity sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A== dependencies: pako "^2.0.4" ws "^8.8.1" +sodium-native@^4.1.1: + version "4.3.3" + resolved "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz" + integrity sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw== + dependencies: + require-addon "^1.1.0" + spdx-correct@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" @@ -2045,12 +2317,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -2058,24 +2330,24 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.13" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz" integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== split@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== dependencies: through "2" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== standard-engine@^14.0.1: version "14.0.1" - resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-14.0.1.tgz#fe568e138c3d9768fc59ff81001f7049908a8156" + resolved "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz" integrity sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q== dependencies: get-stdin "^8.0.0" @@ -2085,7 +2357,7 @@ standard-engine@^14.0.1: standard@^16.0.3: version "16.0.4" - resolved "https://registry.yarnpkg.com/standard/-/standard-16.0.4.tgz#779113ba41dd218ab545e7b4eb2405561f6eb370" + resolved "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz" integrity sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ== dependencies: eslint "~7.18.0" @@ -2099,7 +2371,7 @@ standard@^16.0.3: stream-combiner@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" + resolved "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz" integrity sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ== dependencies: duplexer "~0.1.1" @@ -2107,7 +2379,7 @@ stream-combiner@^0.2.2: string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -2116,7 +2388,7 @@ string-width@^4.2.3: string.prototype.matchall@^4.0.5: version "4.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz" integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== dependencies: call-bind "^1.0.2" @@ -2130,7 +2402,7 @@ string.prototype.matchall@^4.0.5: string.prototype.trim@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz" integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== dependencies: call-bind "^1.0.2" @@ -2139,7 +2411,7 @@ string.prototype.trim@^1.2.7: string.prototype.trimend@^1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz" integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" @@ -2148,7 +2420,7 @@ string.prototype.trimend@^1.0.6: string.prototype.trimstart@^1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz" integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" @@ -2157,43 +2429,43 @@ string.prototype.trimstart@^1.0.6: strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== table@^6.0.4: version "6.8.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + resolved "https://registry.npmjs.org/table/-/table-6.8.1.tgz" integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== dependencies: ajv "^8.0.1" @@ -2204,17 +2476,31 @@ table@^6.0.4: text-table@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -through@2, through@^2.3.8, through@~2.3, through@~2.3.4: +through@^2.3.8, through@~2.3, through@~2.3.4, through@2: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +to-buffer@^1.2.0: + version "1.2.2" + resolved "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz" + integrity sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw== + dependencies: + isarray "^2.0.5" + safe-buffer "^5.2.1" + typed-array-buffer "^1.0.3" + +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + tsconfig-paths@^3.11.0: version "3.14.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz" integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" @@ -2224,29 +2510,43 @@ tsconfig-paths@^3.11.0: tslib@^2.1.0, tslib@^2.5.0, tslib@^2.5.3: version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz" integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-fest@^0.3.0: version "0.3.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz" integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== type-fest@^0.8.1: version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + typed-array-length@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== dependencies: call-bind "^1.0.2" @@ -2255,7 +2555,7 @@ typed-array-length@^1.0.4: unbox-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: call-bind "^1.0.2" @@ -2265,19 +2565,24 @@ unbox-primitive@^1.0.2: uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" +urijs@^1.19.1: + version "1.19.11" + resolved "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz" + integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ== + v8-compile-cache@^2.0.3: version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== validate-npm-package-license@^3.0.1: version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -2285,12 +2590,12 @@ validate-npm-package-license@^3.0.1: web-streams-polyfill@^3.0.3: version "3.2.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== which-boxed-primitive@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== dependencies: is-bigint "^1.0.1" @@ -2299,46 +2604,47 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.16, which-typed-array@^1.1.9: + version "1.1.20" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" word-wrap@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== ws@^8.13.0, ws@^8.8.1: version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + resolved "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== xdg-basedir@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/substrate-node/pallets/pallet-tft-bridge/src/tft_bridge.rs b/substrate-node/pallets/pallet-tft-bridge/src/tft_bridge.rs index 83e1f49a3..8a7abeaa8 100644 --- a/substrate-node/pallets/pallet-tft-bridge/src/tft_bridge.rs +++ b/substrate-node/pallets/pallet-tft-bridge/src/tft_bridge.rs @@ -345,16 +345,9 @@ impl Pallet { !ExecutedBurnTransactions::::contains_key(tx_id), Error::::BurnTransactionAlreadyExecuted ); - ensure!( - BurnTransactions::::contains_key(tx_id), - Error::::BurnTransactionNotExists - ); - - let Some(tx) = BurnTransactions::::get(tx_id) else { - return Err(DispatchErrorWithPostInfo::from( - Error::::BurnTransactionNotExists, - )); - }; + let tx = BurnTransactions::::get(tx_id).ok_or( + DispatchErrorWithPostInfo::from(Error::::BurnTransactionNotExists), + )?; BurnTransactions::::remove(tx_id); ExecutedBurnTransactions::::insert(tx_id, &tx);