diff --git a/.claude/skills/fix/SKILL.md b/.claude/skills/fix/SKILL.md new file mode 100644 index 0000000000..d097f3fbfe --- /dev/null +++ b/.claude/skills/fix/SKILL.md @@ -0,0 +1,33 @@ +--- +name: fix +description: Commit current changes, run Rust autofix/lint/format, run pallet-subtensor tests, amend with any fixes. +--- + +# Fix Skill + +Create or reuse one commit, run the Rust fix pipeline in order, run unit tests, and fold all resulting changes into that same commit. + +## Steps + +1. Run /format +2. In a subagent (subagent_type: `general-purpose`, model: `sonnet`) run: + - `cargo test -p pallet-subtensor --lib` and capture full output + - If any tests fail, analyze the failures + - Read the failing test code AND the source code it tests + - Determine the root cause + - Apply fixes using Edit tools + - Re-run the tests to confirm the fix works + - After fixing, if there are further failures, repeat (up to 3 fix-and-retest cycles) + - Summarize: + - Which tests failed, if any + - What was fixed and how + - Whether all tests pass now +3. Amend commit with test fixes, if any, then /format +4. Run `git show -s` for user to review + +## Important + +- Do NOT run `scripts/fix_rust.sh` — let /format take care of it +- Do NOT skip any steps +- The test subagent must fix source code to make tests pass, NOT modify tests to make them pass (unless the test itself is clearly wrong) +- If the test subagent cannot fix all failures after 3 cycles, it must return the remaining failures so the main agent can report them to the user diff --git a/.claude/skills/format/SKILL.md b/.claude/skills/format/SKILL.md new file mode 100644 index 0000000000..9a0424b28b --- /dev/null +++ b/.claude/skills/format/SKILL.md @@ -0,0 +1,25 @@ +--- +name: format +description: Commit current changes, run Rust autofix/lint/format, amend with any fixes. +--- + +# Format Skill + +Create or reuse one commit, run the Rust fix pipeline in order and fold all resulting changes into that same commit. + +## Steps + +1. Stage all changes and create a commit with a descriptive message summarizing the changes (unless there are none) +2. Do this: + a. Run `cargo check --workspace` + b. Run `cargo clippy --fix --workspace --all-features --all-targets --allow-dirty` + c. Run `cargo fix --workspace --all-features --all-targets --allow-dirty` + d. Run `cargo fmt --all` + e. Amend the commit with any changes +3. Run `git show -s` for user to review + +## Important + +- If a fix tool fails in step 2, stop and report the error to the user rather than continuing +- Do NOT run `scripts/fix_rust.sh` itself — run the individual commands listed above instead +- Do NOT skip any steps diff --git a/.claude/skills/ship/SKILL.md b/.claude/skills/ship/SKILL.md new file mode 100644 index 0000000000..163015b8b2 --- /dev/null +++ b/.claude/skills/ship/SKILL.md @@ -0,0 +1,77 @@ +--- +name: ship +description: Ship current branch end-to-end: run /fix, push, open/update PR, triage CI failures, then deliver review findings for approval. +--- + +# Ship Skill + +Ship the branch through CI and review without force-pushes, and never apply review fixes without explicit user approval. + +Run the following skill in a subagent to prevent context pollution. Make the subagent return a short summary to the main agent. + +1. Run `/fix` +2. Push the branch to origin +3. Create a PR with a comprehensive description if none exists yet + - Update the description if PR exists already + - Add label `skip-cargo-audit` to the PR +4. Poll CI status in a loop: + - Run: `gh pr checks --json name,state,conclusion,link --watch --fail-fast 2>/dev/null || gh pr checks` + - If `--watch` is not available, poll manually every 90 seconds using `gh pr checks --json name,state,conclusion,link` until all checks have completed (no checks with state "pending" or conclusion ""). + - **Ignore these known-flaky/irrelevant checks** — treat them as passing even if they fail: + - `validate-benchmarks` (benchmark CI — not relevant) + - Any `Contract E2E Tests` check that failed only due to a timeout (look for timeout in the failure link/logs) + - `cargo-audit` +5. **If there are real CI failures** (failures NOT in the ignore list above): + - For EACH distinct failing check, launch a **separate Task subagent** (subagent_type: `general-purpose`, model: `sonnet`) in parallel. Each subagent must: + - Fetch the failed check's logs: use `gh run view --log-failed` or the check link to get failure details. + - Investigate the root cause by reading relevant source files. + - Return a **fix plan**: a description of what needs to change and in which files, with specific code snippets showing the fix. + - **Wait for all subagents** to return their fix plans. +6. **Aggregate and apply fixes**: + - Review all returned fix plans for conflicts or overlaps. + - Apply the fixes using Edit/Write tools. + - Invoke the /fix skill + - `git push` +7. **Re-check CI**: Go back to step 4 and poll again. Repeat the fix cycle up to **3 times**. If CI still fails after 3 rounds, report the remaining failures to the user and stop. +8. **Once CI is green** (or only ignored checks are failing), perform a thorough code review. + - **Launch a single Opus subagent** (subagent_type: `general-purpose`, model: `opus`) for the review: + - It must get the full PR diff: `git diff main...HEAD`. + - It must read every changed file in full. + - It must produce a numbered list of **issues** found, where each issue has: + - A unique sequential ID (e.g., `R-1`, `R-2`, ...). + - **Severity**: critical / major / minor / nit. + - **File and line(s)** affected. + - **Description** of the problem. + - The review must check for: correctness, safety (no panics, no unchecked arithmetic, no indexing), edge cases, naming, documentation gaps, test coverage, and adherence to Substrate/Rust best practices. + - Return the full list of issues. +9. **For each issue**, run fix designer then fix reviewer in sequence; run all issues concurrently with each other: + - **Fix designer** (subagent_type: `general-purpose`, model: `sonnet`): Given the issue description and relevant code context, design a concrete proposed fix with exact code changes (old code -> new code). Return the fix as a structured plan. + - **Fix reviewer** (subagent_type: `general-purpose`, model: `opus`): Given the issue description, the relevant code context, and the proposed fix (once the fix designer returns — so the reviewer runs AFTER the designer, but reviewers for different issues run in parallel with each other). The reviewer must check: + - Does the fix actually solve the issue? + - Does it introduce new problems? + - Is it the simplest correct fix? + - Return: approved / rejected with reasoning. + + Implementation note: For each issue, first launch the fix designer. Once the fix designer for that issue returns, launch the fix reviewer for that issue. But all issues should be processed in parallel — i.e., launch all fix designers at once, then as each designer returns, launch its corresponding reviewer. You may batch reviewers if designers finish close together. + +10. **Report to user**: Present a formatted summary: + ``` + ## Code Review Results + + ### R-1: [severity] + **File**: path/to/file.rs:42 + **Issue**: <description> + **Proposed fix**: <summary of fix> + **Review**: Approved / Rejected — <reasoning> + + ### R-2: ... + ``` + Ask the user which fixes to apply (all approved ones, specific ones by ID, or none). + +## Important Rules + +- Never force-push. Always use regular `git push`. +- All CI polling must have a maximum total wall-clock timeout of 45 minutes. If CI hasn't finished by then, report current status and stop waiting. +- When fetching CI logs, use a subagent to isolate the relevant part. If `gh run view` output is very long, focus on the failed step output only. +- Do NOT apply code review fixes automatically — always present them for user approval first. +- Use HEREDOC syntax for PR body and commit messages to preserve formatting. diff --git a/.github/e2e.yml b/.github/e2e.yml deleted file mode 100644 index 65fb5fed57..0000000000 --- a/.github/e2e.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: E2E Tests - -on: - workflow_dispatch: \ No newline at end of file diff --git a/.github/workflows/eco-tests.yml b/.github/workflows/eco-tests.yml index bf5acc5d87..f5413f45cc 100644 --- a/.github/workflows/eco-tests.yml +++ b/.github/workflows/eco-tests.yml @@ -1,10 +1,47 @@ name: eco-tests +permissions: + contents: read + +concurrency: + group: eco-tests-${{ github.ref }} + cancel-in-progress: true + on: + pull_request: workflow_dispatch: +env: + CARGO_TERM_COLOR: always + jobs: - placeholder: - runs-on: ubuntu-latest + eco-tests: + name: cargo test (eco-tests) + runs-on: [self-hosted, type-ccx43] + env: + RUST_BACKTRACE: full + SKIP_WASM_BUILD: 1 steps: - - run: echo "ok" + - name: Check-out repository under $GITHUB_WORKSPACE + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get update + sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get install -y --no-install-recommends -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" build-essential clang curl git make libssl-dev llvm libudev-dev protobuf-compiler pkg-config + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - name: Utilize Shared Rust Cache + uses: Swatinem/rust-cache@v2 + with: + key: eco-tests + cache-on-failure: true + workspaces: eco-tests + + - name: cargo test + working-directory: eco-tests + run: cargo test diff --git a/.github/workflows/scheduled-smoke-tests.yml b/.github/workflows/scheduled-smoke-tests.yml new file mode 100644 index 0000000000..3293de177b --- /dev/null +++ b/.github/workflows/scheduled-smoke-tests.yml @@ -0,0 +1,47 @@ +name: Scheduled Smoke Tests + +on: + schedule: + - cron: '0 */6 * * *' + +env: + CARGO_TERM_COLOR: always + +permissions: + contents: read + +jobs: + run-smoke-tests: + runs-on: [self-hosted, type-ccx33] + timeout-minutes: 30 + + strategy: + fail-fast: false + matrix: + include: + - test: smoke_mainnet + - test: smoke_testnet + + name: "smoke-e2e-${{ matrix.test }}" + + steps: + - name: Check-out repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: ts-tests/.nvmrc + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Install e2e dependencies + working-directory: ts-tests + run: pnpm install --frozen-lockfile + + - name: Run smoke test + working-directory: ts-tests + run: pnpm moonwall test ${{ matrix.test }} \ No newline at end of file diff --git a/.github/workflows/e2e.yml b/.github/workflows/typescript-e2e.yml similarity index 51% rename from .github/workflows/e2e.yml rename to .github/workflows/typescript-e2e.yml index 959a86bdeb..0a63112e28 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/typescript-e2e.yml @@ -1,15 +1,8 @@ -name: E2E Tests +name: Typescript E2E Tests on: pull_request: - workflow_dispatch: - inputs: - verbose: - description: "Output more information when triggered manually" - required: false - default: "" - env: CARGO_TERM_COLOR: always @@ -17,9 +10,42 @@ permissions: contents: read jobs: + typescript-formatting: + runs-on: ubuntu-latest + steps: + - name: Check-out repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: ts-tests/.nvmrc + + - name: Install system dependencies + run: | + sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get update + sudo DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=a apt-get install -y --no-install-recommends \ + -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \ + build-essential clang curl git make libssl-dev llvm libudev-dev protobuf-compiler pkg-config + + - name: Install e2e dependencies + working-directory: ts-tests + run: pnpm install --frozen-lockfile + + - name: Formatting check + run: | + cd ts-tests + pnpm run fmt + # Build the node binary in both variants and share as artifacts. build: runs-on: [self-hosted, type-ccx33] + needs: [typescript-formatting] timeout-minutes: 60 strategy: matrix: @@ -62,87 +88,52 @@ jobs: path: target/release/node-subtensor if-no-files-found: error - # Discover e2e packages that have a "test" script. - discover: - runs-on: ubuntu-latest - outputs: - packages: ${{ steps.find.outputs.packages }} - steps: - - name: Check-out repository - uses: actions/checkout@v4 - with: - sparse-checkout: e2e - - - name: Find testable packages - id: find - working-directory: e2e - run: | - packages=$( - find . -maxdepth 2 -name package.json -not -path './node_modules/*' \ - | while read -r pkg; do - name=$(jq -r '.name // empty' "$pkg") - has_test=$(jq -r '.scripts.test // empty' "$pkg") - if [ -n "$has_test" ] && [ -n "$name" ]; then - echo "$name" - fi - done \ - | jq -R -s -c 'split("\n") | map(select(. != ""))' - ) - echo "packages=$packages" >> "$GITHUB_OUTPUT" - echo "Discovered packages: $packages" - - # Run each e2e package's tests in parallel. - test: - needs: [build, discover] - if: ${{ needs.discover.outputs.packages != '[]' }} + run-e2e-tests: + needs: [build] runs-on: [self-hosted, type-ccx33] timeout-minutes: 30 + strategy: fail-fast: false matrix: - package: ${{ fromJson(needs.discover.outputs.packages) }} - name: "e2e: ${{ matrix.package }}" + include: + - test: dev + binary: release + - test: zombienet_shield + binary: release + - test: zombienet_staking + binary: fast + + name: "typescript-e2e-${{ matrix.test }}" + steps: - name: Check-out repository uses: actions/checkout@v4 - - name: Download release binary + - name: Download binary uses: actions/download-artifact@v4 with: - name: node-subtensor-release + name: node-subtensor-${{ matrix.binary }} path: target/release - - name: Download fast binary - uses: actions/download-artifact@v4 - with: - name: node-subtensor-fast - path: target/fast - - - name: Make binaries executable - run: chmod +x target/release/node-subtensor target/fast/node-subtensor + - name: Make binary executable + run: chmod +x target/release/node-subtensor - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version-file: e2e/.nvmrc + node-version-file: ts-tests/.nvmrc - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: 10 - - name: Bootstrap papi types - working-directory: e2e - run: ./bootstrap_types.sh --skip-build - - name: Install e2e dependencies - working-directory: e2e + working-directory: ts-tests run: pnpm install --frozen-lockfile - name: Run tests - working-directory: e2e - env: - # Use fast-runtime binary for staking package, release binary for others - # Path is relative to package directory (e2e/<package>/) - BINARY_PATH: ${{ matrix.package == 'e2e-staking' && '../../target/fast/node-subtensor' || '../../target/release/node-subtensor' }} - run: pnpm --filter ${{ matrix.package }} test + run: | + cd ts-tests + pnpm moonwall test ${{ matrix.test }} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..2a40a8e0d8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,4 @@ +- never use slice indexing like `arr[n..]` or `arr[i]`; use `.get(n..)`, `.get(i)` etc. instead to avoid panics (clippy::indexing_slicing) +- never use `*`, `+`, `-`, `/` for arithmetic; use `.saturating_mul()`, `.saturating_add()`, `.saturating_sub()`, `.saturating_div()` or checked variants instead (clippy::arithmetic_side_effects) +- if you are creating a PR to `subtensor` add a `skip-cargo-audit` label +- no `Co-Authored-By` in commits or attribution in PRs diff --git a/Cargo.lock b/Cargo.lock index 7bdb24a9d3..c582dd22cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6662,6 +6662,7 @@ checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", + "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types", @@ -6719,6 +6720,31 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-http-client" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" +dependencies = [ + "async-trait", + "base64 0.22.1", + "http-body 1.0.1", + "hyper 1.7.0", + "hyper-rustls", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "rustls", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tower", + "tracing", + "url", +] + [[package]] name = "jsonrpsee-proc-macros" version = "0.24.9" @@ -8265,6 +8291,7 @@ dependencies = [ "frame-benchmarking", "frame-benchmarking-cli", "frame-metadata-hash-extension", + "frame-support", "frame-system", "frame-system-rpc-runtime-api", "futures", @@ -8274,6 +8301,7 @@ dependencies = [ "memmap2 0.9.8", "node-subtensor-runtime", "num-traits", + "pallet-balances", "pallet-commitments", "pallet-drand", "pallet-shield", @@ -16349,9 +16377,18 @@ dependencies = [ name = "share-pool" version = "0.1.0" dependencies = [ + "approx", + "log", + "num-traits", + "parity-scale-codec", + "rand 0.8.5", + "rayon", "safe-math", + "scale-info", + "sp-core", "sp-std", "substrate-fixed", + "subtensor-macros", ] [[package]] @@ -18259,6 +18296,7 @@ dependencies = [ name = "subtensor-transaction-fee" version = "0.1.0" dependencies = [ + "approx", "frame-executive", "frame-support", "frame-system", diff --git a/Cargo.toml b/Cargo.toml index 0d95b9a054..e0dce0884f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "support/*", "chain-extensions", ] +exclude = ["eco-tests"] resolver = "2" [workspace.package] @@ -87,6 +88,7 @@ hex = { version = "0.4", default-features = false } hex-literal = "0.4.1" jsonrpsee = { version = "0.24.9", default-features = false } libsecp256k1 = { version = "0.7.2", default-features = false } +lencode = "0.1.6" log = { version = "0.4.21", default-features = false } memmap2 = "0.9.8" ndarray = { version = "0.16.1", default-features = false } @@ -315,3 +317,4 @@ pow-faucet = [] [patch.crates-io] w3f-bls = { git = "https://github.com/opentensor/bls", branch = "fix-no-std" } + diff --git a/README.md b/README.md index 2c74b9e8f1..b3e902c0bf 100644 --- a/README.md +++ b/README.md @@ -337,6 +337,10 @@ by appending your own. A few useful ones are as follow. ``` --> +## Testing + +Check [testing section](./docs/testing.md). + ## License The MIT License (MIT) Copyright © 2021 Yuma Rao diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 46ea71fc39..5c9729250f 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -435,8 +435,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = TaoCurrencyReserve<Self>; - type AlphaReserve = AlphaCurrencyReserve<Self>; + type TaoReserve = TaoBalanceReserve<Self>; + type AlphaReserve = AlphaBalanceReserve<Self>; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; diff --git a/common/src/lib.rs b/common/src/lib.rs index a143c27824..70fa42c32b 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -259,7 +259,7 @@ pub trait BalanceOps<AccountId> { hotkey: &AccountId, netuid: NetUid, alpha: AlphaBalance, - ) -> Result<AlphaBalance, DispatchError>; + ) -> Result<(), DispatchError>; } /// Allows to query the current block author diff --git a/contract-tests/src/subtensor.ts b/contract-tests/src/subtensor.ts index f5829c76aa..2b3b5d8be1 100644 --- a/contract-tests/src/subtensor.ts +++ b/contract-tests/src/subtensor.ts @@ -419,4 +419,22 @@ export async function setNetworkLastLockCost(api: TypedApi<typeof devnet>, defau const valueOnChain = await api.query.SubtensorModule.NetworkLastLockCost.getValue() assert.equal(defaultNetworkLastLockCost, valueOnChain) +} + + +export async function getStake(api: TypedApi<typeof devnet>, hotkey: string, coldkey: string, netuid: number): Promise<bigint> { + const value = (await api.query.SubtensorModule.AlphaV2.getValue(hotkey, coldkey, netuid)); + + const mantissa = value.mantissa; + const exponent = value.exponent; + + let result: bigint; + + if (exponent >= 0) { + result = mantissa * BigInt(10) ** exponent; + } else { + result = mantissa / BigInt(10) ** -exponent; + } + + return result; } \ No newline at end of file diff --git a/contract-tests/test/alphaPool.test.ts b/contract-tests/test/alphaPool.test.ts index 361e1213bb..f3ec5dde66 100644 --- a/contract-tests/test/alphaPool.test.ts +++ b/contract-tests/test/alphaPool.test.ts @@ -9,7 +9,7 @@ import { PublicClient } from "viem"; import { TypedApi } from "polkadot-api"; import { ALPHA_POOL_CONTRACT_ABI, ALPHA_POOL_CONTRACT_BYTECODE } from "../src/contracts/alphaPool"; import { convertH160ToPublicKey, convertH160ToSS58, convertPublicKeyToSs58, toViemAddress } from "../src/address-utils"; -import { forceSetBalanceToEthAddress, disableWhiteListCheck, addNewSubnetwork, forceSetBalanceToSs58Address, startCall, burnedRegister } from "../src/subtensor"; +import { forceSetBalanceToEthAddress, disableWhiteListCheck, addNewSubnetwork, forceSetBalanceToSs58Address, startCall, burnedRegister, getStake } from "../src/subtensor"; import { ethers } from "ethers" import { tao } from "../src/balance-math"; import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking"; @@ -46,7 +46,7 @@ describe("bridge token contract deployment", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 // the unit in V2 is RAO, not ETH let stakeBalance = tao(20) - const stakeBefore = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) + const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet); const tx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid) await tx.wait() @@ -56,7 +56,7 @@ describe("bridge token contract deployment", () => { ); assert.ok(stakeFromContract > stakeBefore) - const stakeAfter = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) + const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) assert.ok(stakeAfter > stakeBefore) assert.ok(stakeFromContract > tao(20)) }) @@ -66,7 +66,7 @@ describe("bridge token contract deployment", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 const stakingPrecompile = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet); - const stakeBeforeDeposit = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) + const stakeBeforeDeposit = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) const contractFactory = new ethers.ContractFactory(ALPHA_POOL_CONTRACT_ABI, ALPHA_POOL_CONTRACT_BYTECODE, wallet) const contract = await contractFactory.deploy(hotkey.publicKey) @@ -103,11 +103,11 @@ describe("bridge token contract deployment", () => { await depositAlphaTx.wait() // compare wallet stake - const stakeAftereDeposit = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) + const stakeAftereDeposit = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) assert.ok(stakeAftereDeposit < stakeBeforeDeposit) // check the contract stake - const ContractStake = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(contractAddress), netuid) + const ContractStake = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(contractAddress), netuid) assert.ok(ContractStake > 0) // check the wallet alpha balance in contract, the actual swapped alpha could be less than alphaAmount in deposit call diff --git a/contract-tests/test/precompileWrapper.direct-call.test.ts b/contract-tests/test/precompileWrapper.direct-call.test.ts index 5d63dfbb44..fa1354f3ce 100644 --- a/contract-tests/test/precompileWrapper.direct-call.test.ts +++ b/contract-tests/test/precompileWrapper.direct-call.test.ts @@ -10,6 +10,7 @@ import { startCall, disableWhiteListCheck, forceSetBalanceToEthAddress, + getStake, } from "../src/subtensor"; import { ethers } from "ethers"; @@ -147,7 +148,8 @@ describe("PrecompileWrapper - Direct Call Tests", () => { it("Should add stake via wrapper", async () => { const stakeAmount = tao(2); - const stakeBefore = await api.query.SubtensorModule.Alpha.getValue( + const stakeBefore = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wrapperAddress), netuid @@ -161,7 +163,8 @@ describe("PrecompileWrapper - Direct Call Tests", () => { ); await addStakeTx.wait(); - const stakeAfter = await api.query.SubtensorModule.Alpha.getValue( + const stakeAfter = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wrapperAddress), netuid @@ -171,7 +174,8 @@ describe("PrecompileWrapper - Direct Call Tests", () => { it("Should remove stake via wrapper", async () => { const removeAmount = tao(1); - const stakeBefore = await api.query.SubtensorModule.Alpha.getValue( + const stakeBefore = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wrapperAddress), netuid @@ -184,7 +188,8 @@ describe("PrecompileWrapper - Direct Call Tests", () => { ); await removeStakeTx.wait(); - const stakeAfter = await api.query.SubtensorModule.Alpha.getValue( + const stakeAfter = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wrapperAddress), netuid diff --git a/contract-tests/test/staking.precompile.add-remove.test.ts b/contract-tests/test/staking.precompile.add-remove.test.ts index d7dfcb0d3d..9eef7d4dbf 100644 --- a/contract-tests/test/staking.precompile.add-remove.test.ts +++ b/contract-tests/test/staking.precompile.add-remove.test.ts @@ -11,6 +11,7 @@ import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, sendProxyCall, startCall, + getStake, } from "../src/subtensor" import { ETH_LOCAL_URL } from "../src/config"; import { ISTAKING_ADDRESS, ISTAKING_V2_ADDRESS, IStakingABI, IStakingV2ABI } from "../src/contracts/staking" @@ -54,7 +55,7 @@ describe("Test neuron precompile add remove stake", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 // ETH unit let stakeBalance = raoToEth(tao(20)) - const stakeBefore = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) + const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) const contract = new ethers.Contract(ISTAKING_ADDRESS, IStakingABI, wallet1); const tx = await contract.addStake(hotkey.publicKey, netuid, { value: stakeBalance.toString() }) await tx.wait() @@ -64,7 +65,7 @@ describe("Test neuron precompile add remove stake", () => { ); assert.ok(stakeFromContract > stakeBefore) - const stakeAfter = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) + const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) assert.ok(stakeAfter > stakeBefore) }) @@ -72,7 +73,7 @@ describe("Test neuron precompile add remove stake", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 // the unit in V2 is RAO, not ETH let stakeBalance = tao(20) - const stakeBefore = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) + const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet2); const tx = await contract.addStake(hotkey.publicKey, stakeBalance.toString(), netuid) await tx.wait() @@ -82,7 +83,7 @@ describe("Test neuron precompile add remove stake", () => { ); assert.ok(stakeFromContract > stakeBefore) - const stakeAfter = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) + const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) assert.ok(stakeAfter > stakeBefore) }) @@ -90,7 +91,7 @@ describe("Test neuron precompile add remove stake", () => { // wrong netuid let netuid = 12345; let stakeBalance = raoToEth(tao(20)) - const stakeBefore = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) + const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) const contract = new ethers.Contract(ISTAKING_ADDRESS, IStakingABI, wallet1); try { const tx = await contract.addStake(hotkey.publicKey, netuid, { value: stakeBalance.toString() }) @@ -104,7 +105,7 @@ describe("Test neuron precompile add remove stake", () => { await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet1.address), netuid) ); assert.equal(stakeFromContract, stakeBefore) - const stakeAfter = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) + const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid) assert.equal(stakeAfter, stakeBefore) }); @@ -113,7 +114,7 @@ describe("Test neuron precompile add remove stake", () => { let netuid = 12345; // the unit in V2 is RAO, not ETH let stakeBalance = tao(20) - const stakeBefore = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) + const stakeBefore = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) const contract = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet2); try { @@ -128,7 +129,7 @@ describe("Test neuron precompile add remove stake", () => { await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet2.address), netuid) ); assert.equal(stakeFromContract, stakeBefore) - const stakeAfter = await api.query.SubtensorModule.Alpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) + const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet2.address), netuid) assert.equal(stakeAfter, stakeBefore) }) @@ -248,7 +249,8 @@ describe("Test neuron precompile add remove stake", () => { assert.equal(proxiesAfterAdd[0][0].delegate, convertPublicKeyToSs58(proxy.publicKey)) - let stakeBefore = await api.query.SubtensorModule.Alpha.getValue( + let stakeBefore = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid @@ -261,7 +263,8 @@ describe("Test neuron precompile add remove stake", () => { }) await sendProxyCall(api, call.decodedCall, ss58Address, proxy) - let stakeAfter = await api.query.SubtensorModule.Alpha.getValue( + let stakeAfter = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid @@ -306,7 +309,8 @@ describe("Test neuron precompile add remove stake", () => { assert.equal(proxiesAfterAdd[0][0].delegate, convertPublicKeyToSs58(proxy.publicKey)) - let stakeBefore = await api.query.SubtensorModule.Alpha.getValue( + let stakeBefore = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid @@ -320,7 +324,8 @@ describe("Test neuron precompile add remove stake", () => { await sendProxyCall(api, call.decodedCall, ss58Address, proxy) - let stakeAfter = await api.query.SubtensorModule.Alpha.getValue( + let stakeAfter = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid diff --git a/contract-tests/test/staking.precompile.burn-alpha.test.ts b/contract-tests/test/staking.precompile.burn-alpha.test.ts index 825587602e..f98c988b52 100644 --- a/contract-tests/test/staking.precompile.burn-alpha.test.ts +++ b/contract-tests/test/staking.precompile.burn-alpha.test.ts @@ -10,6 +10,7 @@ import { convertH160ToPublicKey } from "../src/address-utils" import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, startCall, + getStake, } from "../src/subtensor" import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking" @@ -72,7 +73,8 @@ describe("Test staking precompile burn alpha", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 // Get current stake - const currentStake = await api.query.SubtensorModule.Alpha.getValue( + const currentStake = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet1.address), netuid diff --git a/contract-tests/test/staking.precompile.full-limit.test.ts b/contract-tests/test/staking.precompile.full-limit.test.ts index 156fd38f25..faf09d65fd 100644 --- a/contract-tests/test/staking.precompile.full-limit.test.ts +++ b/contract-tests/test/staking.precompile.full-limit.test.ts @@ -12,6 +12,7 @@ import { addStake, forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, + getStake, startCall, } from "../src/subtensor"; import { ethers } from "ethers"; @@ -51,7 +52,8 @@ describe("Test staking precompile add remove limit methods", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; let ss58Address = convertH160ToSS58(wallet1.address); - const alpha = await api.query.SubtensorModule.Alpha.getValue( + const alpha = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -72,7 +74,8 @@ describe("Test staking precompile add remove limit methods", () => { ); await tx.wait(); - const alphaAfterAddStake = await api.query.SubtensorModule.Alpha.getValue( + const alphaAfterAddStake = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -85,7 +88,8 @@ describe("Test staking precompile add remove limit methods", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; let ss58Address = convertH160ToSS58(wallet1.address); - const alpha = await api.query.SubtensorModule.Alpha.getValue( + const alpha = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -104,7 +108,8 @@ describe("Test staking precompile add remove limit methods", () => { ); await tx.wait(); - const alphaAfterRemoveStake = await api.query.SubtensorModule.Alpha.getValue( + const alphaAfterRemoveStake = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -120,7 +125,8 @@ describe("Test staking precompile add remove limit methods", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; let ss58Address = convertH160ToSS58(wallet2.address); - const alpha = await api.query.SubtensorModule.Alpha.getValue( + const alpha = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -141,7 +147,8 @@ describe("Test staking precompile add remove limit methods", () => { ); await tx.wait(); - const alphaAfterAddStake = await api.query.SubtensorModule.Alpha.getValue( + const alphaAfterAddStake = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -154,7 +161,8 @@ describe("Test staking precompile add remove limit methods", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; let ss58Address = convertH160ToSS58(wallet2.address); - const alpha = await api.query.SubtensorModule.Alpha.getValue( + const alpha = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -172,7 +180,8 @@ describe("Test staking precompile add remove limit methods", () => { ); await tx.wait(); - const alphaAfterRemoveStake = await api.query.SubtensorModule.Alpha.getValue( + const alphaAfterRemoveStake = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, diff --git a/contract-tests/test/staking.precompile.limit.test.ts b/contract-tests/test/staking.precompile.limit.test.ts index 759aaecce2..eff1394911 100644 --- a/contract-tests/test/staking.precompile.limit.test.ts +++ b/contract-tests/test/staking.precompile.limit.test.ts @@ -12,6 +12,7 @@ import { addStake, forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, + getStake, startCall, } from "../src/subtensor"; import { ethers } from "ethers"; @@ -47,7 +48,8 @@ describe("Test staking precompile add remove limit methods", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; let ss58Address = convertH160ToSS58(wallet1.address); - const alpha = await api.query.SubtensorModule.Alpha.getValue( + const alpha = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -68,7 +70,8 @@ describe("Test staking precompile add remove limit methods", () => { ); await tx.wait(); - const alphaAfterAddStake = await api.query.SubtensorModule.Alpha.getValue( + const alphaAfterAddStake = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -81,7 +84,8 @@ describe("Test staking precompile add remove limit methods", () => { let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; let ss58Address = convertH160ToSS58(wallet1.address); - const alpha = await api.query.SubtensorModule.Alpha.getValue( + const alpha = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, @@ -102,7 +106,8 @@ describe("Test staking precompile add remove limit methods", () => { ); await tx.wait(); - const alphaAfterRemoveStake = await api.query.SubtensorModule.Alpha.getValue( + const alphaAfterRemoveStake = await getStake( + api, convertPublicKeyToSs58(hotkey.publicKey), ss58Address, netuid, diff --git a/contract-tests/test/staking.precompile.reward.test.ts b/contract-tests/test/staking.precompile.reward.test.ts index d04620c91b..31e15c6225 100644 --- a/contract-tests/test/staking.precompile.reward.test.ts +++ b/contract-tests/test/staking.precompile.reward.test.ts @@ -9,7 +9,8 @@ import { setTxRateLimit, setTempo, setWeightsSetRateLimit, setSubnetOwnerCut, setMinDelegateTake, setActivityCutoff, addStake, setWeight, rootRegister, startCall, - disableAdminFreezeWindowAndOwnerHyperparamRateLimit + disableAdminFreezeWindowAndOwnerHyperparamRateLimit, + getStake } from "../src/subtensor" describe("Test neuron precompile reward", () => { @@ -63,7 +64,8 @@ describe("Test neuron precompile reward", () => { await addStake(api, netuid, convertPublicKeyToSs58(validator.publicKey), tao(100), coldkey) - const miner_alpha_before_emission = await api.query.SubtensorModule.Alpha.getValue( + const miner_alpha_before_emission = await getStake( + api, convertPublicKeyToSs58(miner.publicKey), convertPublicKeyToSs58(coldkey.publicKey), netuid @@ -87,7 +89,8 @@ describe("Test neuron precompile reward", () => { index = 0; while (index < 60) { - let miner_current_alpha = await api.query.SubtensorModule.Alpha.getValue( + let miner_current_alpha = await getStake( + api, convertPublicKeyToSs58(miner.publicKey), convertPublicKeyToSs58(coldkey.publicKey), netuid diff --git a/contract-tests/test/subnet.precompile.hyperparameter.test.ts b/contract-tests/test/subnet.precompile.hyperparameter.test.ts index 75d361a77f..2c9355ec40 100644 --- a/contract-tests/test/subnet.precompile.hyperparameter.test.ts +++ b/contract-tests/test/subnet.precompile.hyperparameter.test.ts @@ -517,7 +517,7 @@ describe("Test the Subnet precompile contract", () => { // const tx = await contract.setAlphaValues(netuid, newValue[0], newValue[1]); // await tx.wait(); - // let onchainValue = await api.query.SubtensorModule.AlphaValues.getValue(netuid) + // let onchainValue = await api.query.SubtensorModule.AlphaV2Values.getValue(netuid) // let value = await contract.getAlphaValues(netuid) // let valueFromContract = [Number(value[0]), Number(value[1])] diff --git a/docs/rust-setup.md b/docs/rust-setup.md index a3e4a952d7..a8f5cf46fb 100644 --- a/docs/rust-setup.md +++ b/docs/rust-setup.md @@ -67,7 +67,21 @@ Open the Terminal application and execute the following commands: # Make sure Homebrew is up-to-date, install protobuf and openssl brew update -brew install protobuf openssl +brew install protobuf openssl llvm@16 +``` + +Also, add the following lines at the end of your ~/.zshrc: + +``` +# LLVM 16 from Homebrew +export PATH="/opt/homebrew/opt/llvm@16/bin:$PATH" + +export CC="/opt/homebrew/opt/llvm@16/bin/clang" +export CXX="/opt/homebrew/opt/llvm@16/bin/clang++" +export LIBCLANG_PATH="/opt/homebrew/opt/llvm@16/lib/libclang.dylib" + +export LDFLAGS="-L/opt/homebrew/opt/llvm@16/lib" +export CPPFLAGS="-I/opt/homebrew/opt/llvm@16/include" ``` ### Windows diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000000..48f1c4409d --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,39 @@ +# Testing + +Typescript tests are run with [Moonwall](https://github.com/Moonsong-Labs/moonwall). To run these you will need to have pnpm installed: + +``` +# Use the correct Node version +nvm use + +# Install moonwall +sudo npm i -g pnpm + +# Change directory to test +cd ts-tests + +# Install dependencies +pnpm i + +# Run manual seal dev tests +pnpm moonwall test dev + +# Run zombienet tests +pnpm moonwall test zombienet + +# If you have MacOS, you might need to run zombinet test with sudo, because tmp folder +sudo pnpm moonwall test zombienet + +# Run smoke tests +pnpm moonwall test smoke_mainnet +``` + +Moonwall lets you also run the testing environment without performing any tests on it, as a method for you to manually test certain things: + +``` +# Dev tests in run mode +pnpm moonwall run dev + +# Zombinet test with run mode +pnpm moonwall run zombienet +``` diff --git a/e2e/.gitignore b/e2e/.gitignore deleted file mode 100644 index 8084bdaf01..0000000000 --- a/e2e/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ -node-subtensor/ -.papi \ No newline at end of file diff --git a/e2e/.prettierrc b/e2e/.prettierrc deleted file mode 100644 index 90abee2393..0000000000 --- a/e2e/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "printWidth": 100, - "semi": true, - "singleQuote": false, - "trailingComma": "all" -} diff --git a/e2e/README.md b/e2e/README.md deleted file mode 100644 index efd79de23f..0000000000 --- a/e2e/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# E2E Tests - -End-to-end tests that run against a local multi-node subtensor network. - -## Quick start - -```bash -cd e2e - -# 1. Set up the development environment (nvm, node, pnpm, jq, yq). -./setup_env.sh - -# 2. Build the node binary and generate polkadot-api type descriptors. -# Installs polkadot-api globally for the CLI and type resolution. -# Re-run this step whenever runtime metadata changes (new pallets, -# modified storage/calls, etc.) to keep descriptors in sync. -./bootstrap_types.sh - -# 3. Install dependencies (requires descriptors from step 2). -pnpm install - -# 4. Run a test suite. -pnpm --filter e2e-shield test # run the shield suite -pnpm --filter e2e-<name> test # run any suite by name -pnpm -r test # run all suites -``` - -## Creating a new test package - -```bash -./bootstrap_package.sh <name> -pnpm install -pnpm --filter e2e-<name> test -``` - -This creates a package with: - -- `package.json` — depends on `e2e-shared` and `polkadot-api` -- `vitest.config.ts` — sequential execution, 120s timeout, alphabetical sequencer -- `setup.ts` — global setup/teardown that spawns a 2-node network -- `tests/00-basic.test.ts` — sample test - -Edit `setup.ts` to configure the number of nodes, extra authorities, and -ports for your suite. Add test-specific dependencies to `package.json`. - -## How it works - -### Network lifecycle - -Each test suite manages its own local network via vitest's `globalSetup`: - -1. **setup()** generates a chain spec, optionally patches it with extra - authorities, spawns validator nodes, waits for peering and finalization, - then writes `NetworkState` to a JSON file under `/tmp/subtensor-e2e/`. -2. **Test files** read the state file in `beforeAll()` to get RPC ports and - connect via polkadot-api. Tests run sequentially (alphabetical by filename), - so later files can build on earlier state changes (e.g. scaling the network). -3. **teardown()** stops all nodes (including extras added mid-suite), cleans - up temp directories and the state file. - -### Shared utilities (`e2e-shared`) - -The `shared/` package provides reusable helpers for all test suites: -spawning and monitoring substrate nodes, generating and patching chain specs, -connecting polkadot-api clients with dev signers, and a custom vitest -sequencer that ensures test files run in alphabetical order. - -### Conventions - -- **File prefixes** — Name test files `00-`, `01-`, `02-` etc. The custom - sequencer sorts alphabetically, so numbering controls execution order. -- **State file** — Each suite writes to `/tmp/subtensor-e2e/<name>/`. Tests - can update this file mid-suite (e.g. to register extra nodes). -- **Catalog versions** — To add a new dependency, first pin its version in - `pnpm-workspace.yaml` under `catalog:`, then reference it in your - package's `package.json` with `"catalog:"` as the version. This prevents - version drift across packages. -- **Query at "best"** — Storage queries for values that change every block - (e.g. rotating keys) should use `{ at: "best" }` instead of the default - `"finalized"`, since finalized lags ~2 blocks behind with GRANDPA. -- **Built-in shortcuts** — Substrate dev accounts (`one`, `two`, `alice`, - `bob`, etc.) have their keys auto-injected. Custom authorities need - `insertKeys()` before starting the node. diff --git a/e2e/bootstrap_package.sh b/e2e/bootstrap_package.sh deleted file mode 100755 index e37dc7f26c..0000000000 --- a/e2e/bootstrap_package.sh +++ /dev/null @@ -1,286 +0,0 @@ -#!/bin/bash -# -# Scaffold a new e2e test package. -# -# Usage: -# ./bootstrap_package.sh <name> -# -# Example: -# ./bootstrap_package.sh staking -# -set -e - -if [ -z "$1" ]; then - echo "Usage: $0 <package-name>" - exit 1 -fi - -for cmd in jq yq; do - if ! command -v "$cmd" &>/dev/null; then - echo "ERROR: $cmd is required. Run ./setup_env.sh first." - exit 1 - fi -done - -NAME="$1" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -DIR="$SCRIPT_DIR/$NAME" -WORKSPACE="$SCRIPT_DIR/pnpm-workspace.yaml" - -if [ -d "$DIR" ]; then - echo "ERROR: Directory $DIR already exists" - exit 1 -fi - -echo "==> Creating package e2e-$NAME..." -mkdir -p "$DIR/tests" - -# -- package.json -- -jq -n \ - --arg name "e2e-$NAME" \ - '{ - name: $name, - version: "1.0.0", - type: "module", - scripts: { test: "vitest run" }, - dependencies: { - "e2e-shared": "workspace:*", - "@polkadot-api/descriptors": "file:../.papi/descriptors", - "polkadot-api": "catalog:" - }, - devDependencies: { - "@types/node": "catalog:", - "vitest": "catalog:" - } - }' > "$DIR/package.json" - -# -- tsconfig.json -- -jq -n '{ - compilerOptions: { - target: "ES2022", - module: "ESNext", - moduleResolution: "bundler", - esModuleInterop: true, - strict: true, - skipLibCheck: true, - types: ["node", "vitest/globals"] - } -}' > "$DIR/tsconfig.json" - -# -- vitest.config.ts -- -cat > "$DIR/vitest.config.ts" << 'EOF' -import { defineConfig } from "vitest/config"; -import AlphabeticalSequencer from "e2e-shared/sequencer.js"; - -export default defineConfig({ - test: { - globals: true, - testTimeout: 120_000, - hookTimeout: 300_000, - fileParallelism: false, - globalSetup: "./setup.ts", - include: ["tests/**/*.test.ts"], - sequence: { - sequencer: AlphabeticalSequencer, - }, - }, -}); -EOF - -# -- setup.ts -- -sed "s/__NAME__/$NAME/g" << 'SETUP_EOF' > "$DIR/setup.ts" -import { writeFile, readFile, rm, mkdir } from "node:fs/promises"; -import { - generateChainSpec, - insertKeys, - getGenesisPatch, - addAuthority, -} from "e2e-shared/chainspec.js"; -import { - startNode, - started, - peerCount, - finalizedBlocks, - stop, - log, - type Node, - type NodeOptions, -} from "e2e-shared/node.js"; - -const CHAIN_SPEC_PATH = "/tmp/subtensor-e2e/__NAME__/chain-spec.json"; -const STATE_FILE = "/tmp/subtensor-e2e/__NAME__/nodes.json"; - -export type NetworkState = { - binaryPath: string; - chainSpec: string; - nodes: { - name: string; - rpcPort: number; - port: number; - pid: number; - basePath: string; - }[]; -}; - -const nodes: Node[] = []; - -const BINARY_PATH = process.env.BINARY_PATH || "../../target/release/node-subtensor"; - -// The local chain spec has 2 built-in authorities (One, Two). -// Add extra authorities here if needed. -const EXTRA_AUTHORITY_SEEDS: string[] = []; - -type NodeConfig = Omit<NodeOptions, "binaryPath" | "chainSpec"> & { - keySeed?: string; -}; - -// TODO: Adjust node configs for your test suite. -const NODE_CONFIGS: NodeConfig[] = [ - { name: "one", port: 30333, rpcPort: 9944, basePath: "/tmp/subtensor-e2e/__NAME__/one", validator: true }, - { name: "two", port: 30334, rpcPort: 9945, basePath: "/tmp/subtensor-e2e/__NAME__/two", validator: true }, -]; - -export async function setup() { - log(`Setting up ${NODE_CONFIGS.length}-node network for __NAME__ E2E tests`); - log(`Binary path: ${BINARY_PATH}`); - - await mkdir("/tmp/subtensor-e2e/__NAME__", { recursive: true }); - - await generateChainSpec(BINARY_PATH, CHAIN_SPEC_PATH, (spec) => { - const patch = getGenesisPatch(spec); - for (const seed of EXTRA_AUTHORITY_SEEDS) { - addAuthority(patch, seed); - } - }); - - for (const config of NODE_CONFIGS) { - await rm(config.basePath, { recursive: true, force: true }); - } - - for (const config of NODE_CONFIGS) { - if (config.keySeed) { - insertKeys(BINARY_PATH, config.basePath, CHAIN_SPEC_PATH, config.keySeed); - } - } - - for (const config of NODE_CONFIGS) { - const node = startNode({ - binaryPath: BINARY_PATH, - chainSpec: CHAIN_SPEC_PATH, - ...config, - }); - nodes.push(node); - await started(node); - } - - const all = Promise.all.bind(Promise); - - await all(nodes.map((n) => peerCount(n, nodes.length - 1))); - log("All nodes peered"); - - await all(nodes.map((n) => finalizedBlocks(n, 3))); - log("All nodes finalized block 3"); - - const state: NetworkState = { - binaryPath: BINARY_PATH, - chainSpec: CHAIN_SPEC_PATH, - nodes: NODE_CONFIGS.map((c, i) => ({ - name: c.name, - rpcPort: c.rpcPort, - port: c.port, - pid: nodes[i].process.pid!, - basePath: c.basePath, - })), - }; - - await writeFile(STATE_FILE, JSON.stringify(state, null, 2)); - log("Network state written to " + STATE_FILE); -} - -export async function teardown() { - log("Tearing down __NAME__ E2E test network"); - - let state: NetworkState | undefined; - try { - const data = await readFile(STATE_FILE, "utf-8"); - state = JSON.parse(data); - } catch {} - - for (const node of nodes) { - try { - await stop(node); - } catch (e) { - log(`Warning: failed to stop ${node.name}: ${e}`); - } - } - - if (state) { - const ownPids = new Set(nodes.map((n) => n.process.pid)); - for (const nodeInfo of state.nodes) { - if (!ownPids.has(nodeInfo.pid)) { - try { - process.kill(nodeInfo.pid, "SIGTERM"); - log(`Killed extra node ${nodeInfo.name} (pid ${nodeInfo.pid})`); - } catch {} - } - } - - } - - await rm("/tmp/subtensor-e2e/__NAME__", { recursive: true, force: true }); - - log("Teardown complete"); -} -SETUP_EOF - -# -- tests/00-basic.test.ts -- -sed "s/__NAME__/$NAME/g" << 'TEST_EOF' > "$DIR/tests/00-basic.test.ts" -import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { readFile } from "node:fs/promises"; -import type { PolkadotClient, TypedApi } from "polkadot-api"; -import { subtensor } from "@polkadot-api/descriptors"; -import type { NetworkState } from "../setup.js"; -import { - connectClient, - createSigner, - waitForFinalizedBlocks, -} from "e2e-shared/client.js"; - -let client: PolkadotClient; -let api: TypedApi<typeof subtensor>; -let state: NetworkState; - -const alice = createSigner("//Alice"); - -beforeAll(async () => { - const data = await readFile("/tmp/subtensor-e2e/__NAME__/nodes.json", "utf-8"); - state = JSON.parse(data); - ({ client, api } = await connectClient(state.nodes[0].rpcPort)); - await waitForFinalizedBlocks(client, 3); -}); - -afterAll(() => { - client?.destroy(); -}); - -describe("__NAME__", () => { - it("should produce finalized blocks", async () => { - const block = await api.query.System.Number.getValue(); - expect(block).toBeGreaterThan(0); - }); -}); -TEST_EOF - -# -- Add to pnpm-workspace.yaml -- -if ! yq '.packages[] | select(. == "'"$NAME"'")' "$WORKSPACE" | grep -q .; then - yq -i '.packages += ["'"$NAME"'"]' "$WORKSPACE" - echo " Added '$NAME' to pnpm-workspace.yaml" -fi - -echo "==> Created e2e/$NAME/" -echo "" -echo "Next steps:" -echo " 1. Edit $NAME/setup.ts to configure your network" -echo " 2. Add test-specific dependencies to $NAME/package.json" -echo " 3. Run: pnpm install" -echo " 4. Run: cd $NAME && pnpm test" diff --git a/e2e/bootstrap_types.sh b/e2e/bootstrap_types.sh deleted file mode 100755 index 74039d37a8..0000000000 --- a/e2e/bootstrap_types.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# -# Build the node binary and (re)generate polkadot-api type descriptors. -# Installs polkadot-api globally for the CLI and type resolution. -# Run this whenever the runtime changes to keep descriptors in sync. -# -# Usage: -# ./bootstrap_types.sh # build + generate types -# ./bootstrap_types.sh --skip-build # generate types only (binary must exist) -# -set -e - -BASE_DIR="/tmp/subtensor-e2e" -mkdir -p $BASE_DIR - -BINARY="${BINARY_PATH:-../target/release/node-subtensor}" -NODE_LOG="${BASE_DIR}/bootstrap-node.log" - -if [ "$1" != "--skip-build" ]; then - echo "==> Building node-subtensor..." - pnpm build-node:debug - BINARY="../target/debug/node-subtensor" -fi - -echo "==> Starting dev node (logs at $NODE_LOG)..." -"$BINARY" --one --dev &>"$NODE_LOG" & -NODE_PID=$! -trap "kill $NODE_PID 2>/dev/null; wait $NODE_PID 2>/dev/null" EXIT - -TIMEOUT=60 -ELAPSED=0 -echo "==> Waiting for node to be ready (timeout: ${TIMEOUT}s)..." -until curl -sf -o /dev/null \ - -H "Content-Type: application/json" \ - -d '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' \ - http://localhost:9944; do - sleep 1 - ELAPSED=$((ELAPSED + 1)) - if [ "$ELAPSED" -ge "$TIMEOUT" ]; then - echo "ERROR: Node failed to start within ${TIMEOUT}s. Check $NODE_LOG" - exit 1 - fi -done - -echo "==> Installing polkadot-api globally..." -npm install -g polkadot-api - -echo "==> Generating papi types..." -pnpm generate-types - -echo "==> Done." diff --git a/e2e/package.json b/e2e/package.json deleted file mode 100644 index db6090fae4..0000000000 --- a/e2e/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "e2e", - "private": true, - "scripts": { - "build-node:debug": "cargo build --manifest-path ../Cargo.toml -p node-subtensor", - "build-node:release": "cargo build --manifest-path ../Cargo.toml --profile release -p node-subtensor", - "build-node:fast": "pnpm build-node:release --features fast-runtime", - "generate-types": "polkadot-api add subtensor --wsUrl ws://localhost:9944 --skip-codegen && polkadot-api", - "format": "prettier --write .", - "format:check": "prettier --check ." - }, - "dependencies": { - "@polkadot-api/descriptors": "file:.papi/descriptors", - "polkadot-api": "catalog:" - }, - "devDependencies": { - "prettier": "catalog:" - } -} diff --git a/e2e/pnpm-lock.yaml b/e2e/pnpm-lock.yaml deleted file mode 100644 index 982813a2d4..0000000000 --- a/e2e/pnpm-lock.yaml +++ /dev/null @@ -1,3822 +0,0 @@ -lockfileVersion: "9.0" - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -catalogs: - default: - "@noble/ciphers": - specifier: ^2.1.1 - version: 2.1.1 - "@polkadot-labs/hdkd": - specifier: ^0.0.25 - version: 0.0.25 - "@polkadot-labs/hdkd-helpers": - specifier: ^0.0.25 - version: 0.0.25 - "@polkadot/keyring": - specifier: ^14.0.1 - version: 14.0.1 - "@polkadot/util": - specifier: ^14.0.1 - version: 14.0.1 - "@polkadot/util-crypto": - specifier: ^14.0.1 - version: 14.0.1 - "@types/node": - specifier: ^24 - version: 24.10.13 - mlkem: - specifier: ^2.5.0 - version: 2.5.0 - polkadot-api: - specifier: ^1.22.0 - version: 1.23.3 - prettier: - specifier: ^3.0.0 - version: 3.8.1 - vitest: - specifier: ^4.0.0 - version: 4.0.18 - -importers: - .: - dependencies: - "@polkadot-api/descriptors": - specifier: file:.papi/descriptors - version: file:.papi/descriptors(polkadot-api@1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0)) - polkadot-api: - specifier: "catalog:" - version: 1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0) - devDependencies: - prettier: - specifier: "catalog:" - version: 3.8.1 - - shared: - dependencies: - "@polkadot-api/descriptors": - specifier: file:../.papi/descriptors - version: file:.papi/descriptors(polkadot-api@1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0)) - "@polkadot-labs/hdkd": - specifier: "catalog:" - version: 0.0.25 - "@polkadot-labs/hdkd-helpers": - specifier: "catalog:" - version: 0.0.25 - "@polkadot/keyring": - specifier: "catalog:" - version: 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) - polkadot-api: - specifier: "catalog:" - version: 1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0) - devDependencies: - "@types/node": - specifier: "catalog:" - version: 24.10.13 - vitest: - specifier: "catalog:" - version: 4.0.18(@types/node@24.10.13)(tsx@4.21.0) - - shield: - dependencies: - "@noble/ciphers": - specifier: "catalog:" - version: 2.1.1 - "@polkadot-api/descriptors": - specifier: file:../.papi/descriptors - version: file:.papi/descriptors(polkadot-api@1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0)) - "@polkadot/util": - specifier: "catalog:" - version: 14.0.1 - "@polkadot/util-crypto": - specifier: "catalog:" - version: 14.0.1(@polkadot/util@14.0.1) - e2e-shared: - specifier: workspace:* - version: link:../shared - mlkem: - specifier: "catalog:" - version: 2.5.0 - polkadot-api: - specifier: "catalog:" - version: 1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0) - devDependencies: - "@types/node": - specifier: "catalog:" - version: 24.10.13 - vitest: - specifier: "catalog:" - version: 4.0.18(@types/node@24.10.13)(tsx@4.21.0) - - staking: - dependencies: - e2e-shared: - specifier: workspace:* - version: link:../shared - devDependencies: - "@types/node": - specifier: "catalog:" - version: 24.10.13 - vitest: - specifier: "catalog:" - version: 4.0.18(@types/node@24.10.13)(tsx@4.21.0) - -packages: - "@babel/code-frame@7.29.0": - resolution: - { - integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-validator-identifier@7.28.5": - resolution: - { - integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, - } - engines: { node: ">=6.9.0" } - - "@commander-js/extra-typings@14.0.0": - resolution: - { - integrity: sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==, - } - peerDependencies: - commander: ~14.0.0 - - "@esbuild/aix-ppc64@0.25.12": - resolution: - { - integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [aix] - - "@esbuild/aix-ppc64@0.27.3": - resolution: - { - integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [aix] - - "@esbuild/android-arm64@0.25.12": - resolution: - { - integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [android] - - "@esbuild/android-arm64@0.27.3": - resolution: - { - integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [android] - - "@esbuild/android-arm@0.25.12": - resolution: - { - integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [android] - - "@esbuild/android-arm@0.27.3": - resolution: - { - integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [android] - - "@esbuild/android-x64@0.25.12": - resolution: - { - integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [android] - - "@esbuild/android-x64@0.27.3": - resolution: - { - integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [android] - - "@esbuild/darwin-arm64@0.25.12": - resolution: - { - integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [darwin] - - "@esbuild/darwin-arm64@0.27.3": - resolution: - { - integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [darwin] - - "@esbuild/darwin-x64@0.25.12": - resolution: - { - integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [darwin] - - "@esbuild/darwin-x64@0.27.3": - resolution: - { - integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [darwin] - - "@esbuild/freebsd-arm64@0.25.12": - resolution: - { - integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [freebsd] - - "@esbuild/freebsd-arm64@0.27.3": - resolution: - { - integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [freebsd] - - "@esbuild/freebsd-x64@0.25.12": - resolution: - { - integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [freebsd] - - "@esbuild/freebsd-x64@0.27.3": - resolution: - { - integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [freebsd] - - "@esbuild/linux-arm64@0.25.12": - resolution: - { - integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [linux] - - "@esbuild/linux-arm64@0.27.3": - resolution: - { - integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [linux] - - "@esbuild/linux-arm@0.25.12": - resolution: - { - integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [linux] - - "@esbuild/linux-arm@0.27.3": - resolution: - { - integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [linux] - - "@esbuild/linux-ia32@0.25.12": - resolution: - { - integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [linux] - - "@esbuild/linux-ia32@0.27.3": - resolution: - { - integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [linux] - - "@esbuild/linux-loong64@0.25.12": - resolution: - { - integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==, - } - engines: { node: ">=18" } - cpu: [loong64] - os: [linux] - - "@esbuild/linux-loong64@0.27.3": - resolution: - { - integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==, - } - engines: { node: ">=18" } - cpu: [loong64] - os: [linux] - - "@esbuild/linux-mips64el@0.25.12": - resolution: - { - integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==, - } - engines: { node: ">=18" } - cpu: [mips64el] - os: [linux] - - "@esbuild/linux-mips64el@0.27.3": - resolution: - { - integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==, - } - engines: { node: ">=18" } - cpu: [mips64el] - os: [linux] - - "@esbuild/linux-ppc64@0.25.12": - resolution: - { - integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [linux] - - "@esbuild/linux-ppc64@0.27.3": - resolution: - { - integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [linux] - - "@esbuild/linux-riscv64@0.25.12": - resolution: - { - integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==, - } - engines: { node: ">=18" } - cpu: [riscv64] - os: [linux] - - "@esbuild/linux-riscv64@0.27.3": - resolution: - { - integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==, - } - engines: { node: ">=18" } - cpu: [riscv64] - os: [linux] - - "@esbuild/linux-s390x@0.25.12": - resolution: - { - integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==, - } - engines: { node: ">=18" } - cpu: [s390x] - os: [linux] - - "@esbuild/linux-s390x@0.27.3": - resolution: - { - integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==, - } - engines: { node: ">=18" } - cpu: [s390x] - os: [linux] - - "@esbuild/linux-x64@0.25.12": - resolution: - { - integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [linux] - - "@esbuild/linux-x64@0.27.3": - resolution: - { - integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [linux] - - "@esbuild/netbsd-arm64@0.25.12": - resolution: - { - integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [netbsd] - - "@esbuild/netbsd-arm64@0.27.3": - resolution: - { - integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [netbsd] - - "@esbuild/netbsd-x64@0.25.12": - resolution: - { - integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [netbsd] - - "@esbuild/netbsd-x64@0.27.3": - resolution: - { - integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [netbsd] - - "@esbuild/openbsd-arm64@0.25.12": - resolution: - { - integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openbsd] - - "@esbuild/openbsd-arm64@0.27.3": - resolution: - { - integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openbsd] - - "@esbuild/openbsd-x64@0.25.12": - resolution: - { - integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [openbsd] - - "@esbuild/openbsd-x64@0.27.3": - resolution: - { - integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [openbsd] - - "@esbuild/openharmony-arm64@0.25.12": - resolution: - { - integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openharmony] - - "@esbuild/openharmony-arm64@0.27.3": - resolution: - { - integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openharmony] - - "@esbuild/sunos-x64@0.25.12": - resolution: - { - integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [sunos] - - "@esbuild/sunos-x64@0.27.3": - resolution: - { - integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [sunos] - - "@esbuild/win32-arm64@0.25.12": - resolution: - { - integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [win32] - - "@esbuild/win32-arm64@0.27.3": - resolution: - { - integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [win32] - - "@esbuild/win32-ia32@0.25.12": - resolution: - { - integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [win32] - - "@esbuild/win32-ia32@0.27.3": - resolution: - { - integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [win32] - - "@esbuild/win32-x64@0.25.12": - resolution: - { - integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [win32] - - "@esbuild/win32-x64@0.27.3": - resolution: - { - integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [win32] - - "@jridgewell/gen-mapping@0.3.13": - resolution: - { - integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, - } - - "@jridgewell/resolve-uri@3.1.2": - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: ">=6.0.0" } - - "@jridgewell/sourcemap-codec@1.5.5": - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } - - "@jridgewell/trace-mapping@0.3.31": - resolution: - { - integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, - } - - "@noble/ciphers@2.1.1": - resolution: - { - integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==, - } - engines: { node: ">= 20.19.0" } - - "@noble/curves@1.9.7": - resolution: - { - integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==, - } - engines: { node: ^14.21.3 || >=16 } - - "@noble/curves@2.0.1": - resolution: - { - integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==, - } - engines: { node: ">= 20.19.0" } - - "@noble/hashes@1.8.0": - resolution: - { - integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==, - } - engines: { node: ^14.21.3 || >=16 } - - "@noble/hashes@2.0.1": - resolution: - { - integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==, - } - engines: { node: ">= 20.19.0" } - - "@polkadot-api/cli@0.18.1": - resolution: - { - integrity: sha512-jPa8WSNPZWdy372sBAUnm0nU1XX5mLbmgkOOU39+zpYPSE12mYXyM3r7JuT5IHdAccEJr6qK2DplPFTeNSyq9A==, - } - hasBin: true - - "@polkadot-api/codegen@0.21.2": - resolution: - { - integrity: sha512-e1Of2TfB13YndPQ71WrtOIPfRrSlkG6wGprP8/VHC484kkt2JPDOY+io3NdPWkafDblDQ47aG0368sxT+4RSZA==, - } - - "@polkadot-api/descriptors@file:.papi/descriptors": - resolution: { directory: .papi/descriptors, type: directory } - peerDependencies: - polkadot-api: ">=1.21.0" - - "@polkadot-api/ink-contracts@0.4.6": - resolution: - { - integrity: sha512-wpFPa8CnGnmq+cFYMzuTEDmtt3ElBM0UWgTz4RpmI9E7knZ1ctWBhO7amXxOWcILqIG6sqWIE95x0cfF1PRcQg==, - } - - "@polkadot-api/json-rpc-provider-proxy@0.2.8": - resolution: - { - integrity: sha512-AC5KK4p2IamAQuqR0S3YaiiUDRB2r1pWNrdF0Mntm5XGYEmeiAILBmnFa7gyWwemhkTWPYrK5HCurlGfw2EsDA==, - } - - "@polkadot-api/json-rpc-provider@0.0.4": - resolution: - { - integrity: sha512-9cDijLIxzHOBuq6yHqpqjJ9jBmXrctjc1OFqU+tQrS96adQze3mTIH6DTgfb/0LMrqxzxffz1HQGrIlEH00WrA==, - } - - "@polkadot-api/known-chains@0.9.18": - resolution: - { - integrity: sha512-zdU4FA01lXcpNXUiFgSmFKIwDKbTw15KT4U6Zlqo6FPUMZgncVEbbS4dSgVrf+TGw9SDOUjGlEdyTHAiOAG5Tw==, - } - - "@polkadot-api/legacy-provider@0.3.8": - resolution: - { - integrity: sha512-Q747MN/7IUxxXGLWLQfhmSLqFyOLUsUFqQQytlEBjt66ZAv9VwYiHZ8JMBCnMzFuaUpKEWDT62ESKhgXn/hmEQ==, - } - peerDependencies: - rxjs: ">=7.8.0" - - "@polkadot-api/logs-provider@0.0.6": - resolution: - { - integrity: sha512-4WgHlvy+xee1ADaaVf6+MlK/+jGMtsMgAzvbQOJZnP4PfQuagoTqaeayk8HYKxXGphogLlPbD06tANxcb+nvAg==, - } - - "@polkadot-api/merkleize-metadata@1.1.29": - resolution: - { - integrity: sha512-z8ivYDdr4xlh50MQ7hLaSVw4VM6EV7gGgd+v/ej09nue0W08NG77zf7pXWeRKgOXe3+hPOSQQRSZT2OlIYRfqA==, - } - - "@polkadot-api/metadata-builders@0.13.9": - resolution: - { - integrity: sha512-V2GljT6StuK40pfmO5l53CvgFNgy60Trrv20mOZDCsFU9J82F+a1HYAABDYlRgoZ9d0IDwc+u+vI+RHUJoR4xw==, - } - - "@polkadot-api/metadata-compatibility@0.4.4": - resolution: - { - integrity: sha512-V4ye5d2ns32YC45Fdc/IF9Y7CgM8inzJbmHQ2DCPSNd6omTRLJd81gU9zU88QAqPAcH2gKGnS5UF+wLL2VagSQ==, - } - - "@polkadot-api/observable-client@0.17.3": - resolution: - { - integrity: sha512-SJhbMKBIzxNgUUy7ZWflYf/TX9soMqiR2WYyggA7U3DLhgdx4wzFjOSbxCk8RuX9Kf/AmJE4dfleu9HBSCZv6g==, - } - peerDependencies: - rxjs: ">=7.8.0" - - "@polkadot-api/pjs-signer@0.6.19": - resolution: - { - integrity: sha512-jTHKoanZg9ewupthOczWNb2pici+GK+TBQmp9MwhwGs/3uMD2144aA8VNNBEi8rMxOBZlvKYfGkgjiTEGbBwuQ==, - } - - "@polkadot-api/polkadot-sdk-compat@2.4.1": - resolution: - { - integrity: sha512-+sET0N3GpnKkLvsazBZEC5vhqAlamlL1KkJK9STB1tRxHSZcY/yBBa1Udn9DXJfX48kE9cnzfYldl9zsjqpARg==, - } - - "@polkadot-api/polkadot-signer@0.1.6": - resolution: - { - integrity: sha512-X7ghAa4r7doETtjAPTb50IpfGtrBmy3BJM5WCfNKa1saK04VFY9w+vDn+hwEcM4p0PcDHt66Ts74hzvHq54d9A==, - } - - "@polkadot-api/raw-client@0.1.1": - resolution: - { - integrity: sha512-HxalpNEo8JCYXfxKM5p3TrK8sEasTGMkGjBNLzD4TLye9IK2smdb5oTvp2yfkU1iuVBdmjr69uif4NaukOYo2g==, - } - - "@polkadot-api/signer@0.2.13": - resolution: - { - integrity: sha512-XBOtjFsRGETVm/aXeZnsvFcJ1qvtZhRtwUMmpCOBt9s8PWfILaQH/ecOegzda3utNIZGmXXaOoJ5w9Hc/6I3ww==, - } - - "@polkadot-api/signers-common@0.1.20": - resolution: - { - integrity: sha512-v1mrTdRjQOV17riZ8172OsOQ/RJbv1QsEpjwnvxzvdCnjuNpYwtYHZaE+cSdDBb4n1p73XIBMvB/uAK/QFC2JA==, - } - - "@polkadot-api/sm-provider@0.1.16": - resolution: - { - integrity: sha512-3LEDU7nkgtDx1A6ATHLLm3+nFAY6cdkNA9tGltfDzW0efACrhhfDjNqJdI1qLNY0wDyT1aGdoWr5r+4CckRpXA==, - } - peerDependencies: - "@polkadot-api/smoldot": ">=0.3" - - "@polkadot-api/smoldot@0.3.15": - resolution: - { - integrity: sha512-YyV+ytP8FcmKEgLRV7uXepJ5Y6md/7u2F8HKxmkWytmnGXO1z+umg2pHbOxLGifD9V2NhkPY+awpzErtVIzqAA==, - } - - "@polkadot-api/substrate-bindings@0.17.0": - resolution: - { - integrity: sha512-YdbkvG/27N5A94AiKE4soVjDy0Nw74Nn+KD29mUnFmIZvL3fsN/DTYkxvMDVsOuanFXyAIXmzDMoi7iky0fyIw==, - } - - "@polkadot-api/substrate-client@0.5.0": - resolution: - { - integrity: sha512-J+gyZONCak+n6NxADZWtldH+gatYORqEScMAgI9gGu43pHUe7/xNRCqnin0dgDIzmuL3m1ERglF8LR7YhB0nHQ==, - } - - "@polkadot-api/utils@0.2.0": - resolution: - { - integrity: sha512-nY3i5fQJoAxU4n3bD7Fs208/KR2J95SGfVc58kDjbRYN5a84kWaGEqzjBNtP9oqht49POM8Bm9mbIrkvC1Bzuw==, - } - - "@polkadot-api/wasm-executor@0.2.3": - resolution: - { - integrity: sha512-B2h1o+Qlo9idpASaHvMSoViB2I5ko5OAfwfhYF8LQDkTADK0B+SeStzNj1Qn+FG34wqTuv7HzBCdjaUgzYINJQ==, - } - - "@polkadot-api/ws-provider@0.7.5": - resolution: - { - integrity: sha512-2ZLEo0PAFeuOx2DUDkbex85HZMf9lgnmZ8oGB5+NaButIydkoqXy5SHYJNPc45GcZy2tvwzImMZInNMLa5GJhg==, - } - - "@polkadot-labs/hdkd-helpers@0.0.25": - resolution: - { - integrity: sha512-GwHayBuyHKfzvGD0vG47NbjFeiK6rRQHQAn1syut9nt0mhXMg4yb3tJ//IyM317qWuDU3HbD2OIp5jKDEQz2/A==, - } - - "@polkadot-labs/hdkd-helpers@0.0.27": - resolution: - { - integrity: sha512-GTSj/Mw5kwtZbefvq2BhvBnHvs7AY4OnJgppO0kE2S/AuDbD6288C9rmO6qwMNmiNVX8OrYMWaJcs46Mt1UbBw==, - } - - "@polkadot-labs/hdkd@0.0.25": - resolution: - { - integrity: sha512-+yZJC1TE4ZKdfoILw8nGxu3H/klrYXm9GdVB0kcyQDecq320ThUmM1M4l8d1F/3QD0Nez9NwHi9t5B++OgJU5A==, - } - - "@polkadot/keyring@14.0.1": - resolution: - { - integrity: sha512-kHydQPCeTvJrMC9VQO8LPhAhTUxzxfNF1HEknhZDBPPsxP/XpkYsEy/Ln1QzJmQqD5VsgwzLDE6cExbJ2CT9CA==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": 14.0.1 - "@polkadot/util-crypto": 14.0.1 - - "@polkadot/networks@14.0.1": - resolution: - { - integrity: sha512-wGlBtXDkusRAj4P7uxfPz80gLO1+j99MLBaQi3bEym2xrFrFhgIWVHOZlBit/1PfaBjhX2Z8XjRxaM2w1p7w2w==, - } - engines: { node: ">=18" } - - "@polkadot/util-crypto@14.0.1": - resolution: - { - integrity: sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": 14.0.1 - - "@polkadot/util@14.0.1": - resolution: - { - integrity: sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg==, - } - engines: { node: ">=18" } - - "@polkadot/wasm-bridge@7.5.4": - resolution: - { - integrity: sha512-6xaJVvoZbnbgpQYXNw9OHVNWjXmtcoPcWh7hlwx3NpfiLkkjljj99YS+XGZQlq7ks2fVCg7FbfknkNb8PldDaA==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - "@polkadot/x-randomvalues": "*" - - "@polkadot/wasm-crypto-asmjs@7.5.4": - resolution: - { - integrity: sha512-ZYwxQHAJ8pPt6kYk9XFmyuFuSS+yirJLonvP+DYbxOrARRUHfN4nzp4zcZNXUuaFhpbDobDSFn6gYzye6BUotA==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - - "@polkadot/wasm-crypto-init@7.5.4": - resolution: - { - integrity: sha512-U6s4Eo2rHs2n1iR01vTz/sOQ7eOnRPjaCsGWhPV+ZC/20hkVzwPAhiizu/IqMEol4tO2yiSheD4D6bn0KxUJhg==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - "@polkadot/x-randomvalues": "*" - - "@polkadot/wasm-crypto-wasm@7.5.4": - resolution: - { - integrity: sha512-PsHgLsVTu43eprwSvUGnxybtOEuHPES6AbApcs7y5ZbM2PiDMzYbAjNul098xJK/CPtrxZ0ePDFnaQBmIJyTFw==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - - "@polkadot/wasm-crypto@7.5.4": - resolution: - { - integrity: sha512-1seyClxa7Jd7kQjfnCzTTTfYhTa/KUTDUaD3DMHBk5Q4ZUN1D1unJgX+v1aUeXSPxmzocdZETPJJRZjhVOqg9g==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - "@polkadot/x-randomvalues": "*" - - "@polkadot/wasm-util@7.5.4": - resolution: - { - integrity: sha512-hqPpfhCpRAqCIn/CYbBluhh0TXmwkJnDRjxrU9Bnqtw9nMNa97D8JuOjdd2pi0rxm+eeLQ/f1rQMp71RMM9t4w==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - - "@polkadot/x-bigint@14.0.1": - resolution: - { - integrity: sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ==, - } - engines: { node: ">=18" } - - "@polkadot/x-global@14.0.1": - resolution: - { - integrity: sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA==, - } - engines: { node: ">=18" } - - "@polkadot/x-randomvalues@14.0.1": - resolution: - { - integrity: sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": "*" - - "@polkadot/x-textdecoder@14.0.1": - resolution: - { - integrity: sha512-CcWiPCuPVJsNk4Vq43lgFHqLRBQHb4r9RD7ZIYgmwoebES8TNm4g2ew9ToCzakFKSpzKu6I07Ne9wv/dt5zLuw==, - } - engines: { node: ">=18" } - - "@polkadot/x-textencoder@14.0.1": - resolution: - { - integrity: sha512-VY51SpQmF1ccmAGLfxhYnAe95Spfz049WZ/+kK4NfsGF9WejxVdU53Im5C80l45r8qHuYQsCWU3+t0FNunh2Kg==, - } - engines: { node: ">=18" } - - "@rollup/rollup-android-arm-eabi@4.57.1": - resolution: - { - integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==, - } - cpu: [arm] - os: [android] - - "@rollup/rollup-android-arm64@4.57.1": - resolution: - { - integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==, - } - cpu: [arm64] - os: [android] - - "@rollup/rollup-darwin-arm64@4.57.1": - resolution: - { - integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==, - } - cpu: [arm64] - os: [darwin] - - "@rollup/rollup-darwin-x64@4.57.1": - resolution: - { - integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==, - } - cpu: [x64] - os: [darwin] - - "@rollup/rollup-freebsd-arm64@4.57.1": - resolution: - { - integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==, - } - cpu: [arm64] - os: [freebsd] - - "@rollup/rollup-freebsd-x64@4.57.1": - resolution: - { - integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==, - } - cpu: [x64] - os: [freebsd] - - "@rollup/rollup-linux-arm-gnueabihf@4.57.1": - resolution: - { - integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==, - } - cpu: [arm] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-arm-musleabihf@4.57.1": - resolution: - { - integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==, - } - cpu: [arm] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-arm64-gnu@4.57.1": - resolution: - { - integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==, - } - cpu: [arm64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-arm64-musl@4.57.1": - resolution: - { - integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==, - } - cpu: [arm64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-loong64-gnu@4.57.1": - resolution: - { - integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==, - } - cpu: [loong64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-loong64-musl@4.57.1": - resolution: - { - integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==, - } - cpu: [loong64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-ppc64-gnu@4.57.1": - resolution: - { - integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==, - } - cpu: [ppc64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-ppc64-musl@4.57.1": - resolution: - { - integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==, - } - cpu: [ppc64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-riscv64-gnu@4.57.1": - resolution: - { - integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==, - } - cpu: [riscv64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-riscv64-musl@4.57.1": - resolution: - { - integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==, - } - cpu: [riscv64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-s390x-gnu@4.57.1": - resolution: - { - integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==, - } - cpu: [s390x] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-x64-gnu@4.57.1": - resolution: - { - integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==, - } - cpu: [x64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-x64-musl@4.57.1": - resolution: - { - integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==, - } - cpu: [x64] - os: [linux] - libc: [musl] - - "@rollup/rollup-openbsd-x64@4.57.1": - resolution: - { - integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==, - } - cpu: [x64] - os: [openbsd] - - "@rollup/rollup-openharmony-arm64@4.57.1": - resolution: - { - integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==, - } - cpu: [arm64] - os: [openharmony] - - "@rollup/rollup-win32-arm64-msvc@4.57.1": - resolution: - { - integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==, - } - cpu: [arm64] - os: [win32] - - "@rollup/rollup-win32-ia32-msvc@4.57.1": - resolution: - { - integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==, - } - cpu: [ia32] - os: [win32] - - "@rollup/rollup-win32-x64-gnu@4.57.1": - resolution: - { - integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==, - } - cpu: [x64] - os: [win32] - - "@rollup/rollup-win32-x64-msvc@4.57.1": - resolution: - { - integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==, - } - cpu: [x64] - os: [win32] - - "@rx-state/core@0.1.4": - resolution: - { - integrity: sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ==, - } - peerDependencies: - rxjs: ">=7" - - "@scure/base@1.2.6": - resolution: - { - integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==, - } - - "@scure/base@2.0.0": - resolution: - { - integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==, - } - - "@scure/sr25519@0.2.0": - resolution: - { - integrity: sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg==, - } - - "@scure/sr25519@0.3.0": - resolution: - { - integrity: sha512-SKsinX2sImunfcsH3seGrwH/OayBwwaJqVN8J1cJBNRCfbBq5q0jyTKGa9PcW1HWv9vXT6Yuq41JsxFLvF59ew==, - } - engines: { node: ">= 20.19.0" } - - "@scure/sr25519@1.0.0": - resolution: - { - integrity: sha512-b+uhK5akMINXZP95F3gJGcb5CMKYxf+q55fwMl0GoBwZDbWolmGNi1FrBSwuaZX5AhqS2byHiAueZgtDNpot2A==, - } - engines: { node: ">= 20.19.0" } - - "@sec-ant/readable-stream@0.4.1": - resolution: - { - integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==, - } - - "@sindresorhus/merge-streams@4.0.0": - resolution: - { - integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==, - } - engines: { node: ">=18" } - - "@standard-schema/spec@1.1.0": - resolution: - { - integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, - } - - "@substrate/ss58-registry@1.51.0": - resolution: - { - integrity: sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==, - } - - "@types/bn.js@5.2.0": - resolution: - { - integrity: sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==, - } - - "@types/chai@5.2.3": - resolution: - { - integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, - } - - "@types/deep-eql@4.0.2": - resolution: - { - integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, - } - - "@types/estree@1.0.8": - resolution: - { - integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, - } - - "@types/node@24.10.13": - resolution: - { - integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==, - } - - "@types/node@25.3.0": - resolution: - { - integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==, - } - - "@types/normalize-package-data@2.4.4": - resolution: - { - integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==, - } - - "@types/ws@8.18.1": - resolution: - { - integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==, - } - - "@vitest/expect@4.0.18": - resolution: - { - integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==, - } - - "@vitest/mocker@4.0.18": - resolution: - { - integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==, - } - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - "@vitest/pretty-format@4.0.18": - resolution: - { - integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==, - } - - "@vitest/runner@4.0.18": - resolution: - { - integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==, - } - - "@vitest/snapshot@4.0.18": - resolution: - { - integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==, - } - - "@vitest/spy@4.0.18": - resolution: - { - integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==, - } - - "@vitest/utils@4.0.18": - resolution: - { - integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==, - } - - acorn@8.16.0: - resolution: - { - integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==, - } - engines: { node: ">=0.4.0" } - hasBin: true - - ansi-regex@6.2.2: - resolution: - { - integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==, - } - engines: { node: ">=12" } - - any-promise@1.3.0: - resolution: - { - integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, - } - - assertion-error@2.0.1: - resolution: - { - integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, - } - engines: { node: ">=12" } - - bn.js@5.2.2: - resolution: - { - integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==, - } - - bundle-require@5.1.0: - resolution: - { - integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - peerDependencies: - esbuild: ">=0.18" - - cac@6.7.14: - resolution: - { - integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, - } - engines: { node: ">=8" } - - chai@6.2.2: - resolution: - { - integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, - } - engines: { node: ">=18" } - - chalk@5.6.2: - resolution: - { - integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==, - } - engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } - - chokidar@4.0.3: - resolution: - { - integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, - } - engines: { node: ">= 14.16.0" } - - cli-cursor@5.0.0: - resolution: - { - integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==, - } - engines: { node: ">=18" } - - cli-spinners@3.4.0: - resolution: - { - integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==, - } - engines: { node: ">=18.20" } - - commander@14.0.3: - resolution: - { - integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==, - } - engines: { node: ">=20" } - - commander@4.1.1: - resolution: - { - integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, - } - engines: { node: ">= 6" } - - confbox@0.1.8: - resolution: - { - integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==, - } - - consola@3.4.2: - resolution: - { - integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - - cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: ">= 8" } - - debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: ">=6.0" } - peerDependencies: - supports-color: "*" - peerDependenciesMeta: - supports-color: - optional: true - - deepmerge-ts@7.1.5: - resolution: - { - integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==, - } - engines: { node: ">=16.0.0" } - - detect-indent@7.0.2: - resolution: - { - integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==, - } - engines: { node: ">=12.20" } - - es-module-lexer@1.7.0: - resolution: - { - integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, - } - - esbuild@0.25.12: - resolution: - { - integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==, - } - engines: { node: ">=18" } - hasBin: true - - esbuild@0.27.3: - resolution: - { - integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==, - } - engines: { node: ">=18" } - hasBin: true - - estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } - - execa@9.6.1: - resolution: - { - integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==, - } - engines: { node: ^18.19.0 || >=20.5.0 } - - expect-type@1.3.0: - resolution: - { - integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==, - } - engines: { node: ">=12.0.0" } - - fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: ">=12.0.0" } - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - figures@6.1.0: - resolution: - { - integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==, - } - engines: { node: ">=18" } - - fix-dts-default-cjs-exports@1.0.1: - resolution: - { - integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==, - } - - fs.promises.exists@1.1.4: - resolution: - { - integrity: sha512-lJzUGWbZn8vhGWBedA+RYjB/BeJ+3458ljUfmplqhIeb6ewzTFWNPCR1HCiYCkXV9zxcHz9zXkJzMsEgDLzh3Q==, - } - - fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } - os: [darwin] - - get-east-asian-width@1.4.0: - resolution: - { - integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==, - } - engines: { node: ">=18" } - - get-stream@9.0.1: - resolution: - { - integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==, - } - engines: { node: ">=18" } - - get-tsconfig@4.13.6: - resolution: - { - integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==, - } - - hosted-git-info@7.0.2: - resolution: - { - integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==, - } - engines: { node: ^16.14.0 || >=18.0.0 } - - hosted-git-info@9.0.2: - resolution: - { - integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==, - } - engines: { node: ^20.17.0 || >=22.9.0 } - - human-signals@8.0.1: - resolution: - { - integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==, - } - engines: { node: ">=18.18.0" } - - imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: ">=0.8.19" } - - index-to-position@1.2.0: - resolution: - { - integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==, - } - engines: { node: ">=18" } - - is-interactive@2.0.0: - resolution: - { - integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==, - } - engines: { node: ">=12" } - - is-plain-obj@4.1.0: - resolution: - { - integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, - } - engines: { node: ">=12" } - - is-stream@4.0.1: - resolution: - { - integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==, - } - engines: { node: ">=18" } - - is-unicode-supported@2.1.0: - resolution: - { - integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==, - } - engines: { node: ">=18" } - - isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } - - joycon@3.1.1: - resolution: - { - integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==, - } - engines: { node: ">=10" } - - js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } - - lilconfig@3.1.3: - resolution: - { - integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==, - } - engines: { node: ">=14" } - - lines-and-columns@1.2.4: - resolution: - { - integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, - } - - load-tsconfig@0.2.5: - resolution: - { - integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - - lodash.sortby@4.7.0: - resolution: - { - integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==, - } - - log-symbols@7.0.1: - resolution: - { - integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==, - } - engines: { node: ">=18" } - - lru-cache@10.4.3: - resolution: - { - integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, - } - - lru-cache@11.2.6: - resolution: - { - integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==, - } - engines: { node: 20 || >=22 } - - magic-string@0.30.21: - resolution: - { - integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, - } - - mimic-function@5.0.1: - resolution: - { - integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==, - } - engines: { node: ">=18" } - - mlkem@2.5.0: - resolution: - { - integrity: sha512-TnSvGBs0EVPukQcdPF0882ZoYXYuD2rb+VgO0kUDbFi/XM1rJOwnQoFW3wGGuc3nG3AT/zp3oWJ86W7ewwKYyA==, - } - engines: { node: ">=16.0.0" } - - mlly@1.8.0: - resolution: - { - integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==, - } - - ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } - - mz@2.7.0: - resolution: - { - integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, - } - - nanoid@3.3.11: - resolution: - { - integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } - hasBin: true - - normalize-package-data@6.0.2: - resolution: - { - integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } - - normalize-package-data@8.0.0: - resolution: - { - integrity: sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==, - } - engines: { node: ^20.17.0 || >=22.9.0 } - - npm-run-path@6.0.0: - resolution: - { - integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==, - } - engines: { node: ">=18" } - - object-assign@4.1.1: - resolution: - { - integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, - } - engines: { node: ">=0.10.0" } - - obug@2.1.1: - resolution: - { - integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==, - } - - onetime@7.0.0: - resolution: - { - integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==, - } - engines: { node: ">=18" } - - ora@9.3.0: - resolution: - { - integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==, - } - engines: { node: ">=20" } - - parse-json@8.3.0: - resolution: - { - integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==, - } - engines: { node: ">=18" } - - parse-ms@4.0.0: - resolution: - { - integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==, - } - engines: { node: ">=18" } - - path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: ">=8" } - - path-key@4.0.0: - resolution: - { - integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==, - } - engines: { node: ">=12" } - - pathe@2.0.3: - resolution: - { - integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, - } - - picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } - - picomatch@4.0.3: - resolution: - { - integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, - } - engines: { node: ">=12" } - - pirates@4.0.7: - resolution: - { - integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, - } - engines: { node: ">= 6" } - - pkg-types@1.3.1: - resolution: - { - integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==, - } - - polkadot-api@1.23.3: - resolution: - { - integrity: sha512-wOWli6Cfk3bO1u/W8qmwriCIKxATkNea8Jyg1jj7GzAqafxy295BYPzYHy2mJZCQ0PAVFPR4/JvCXocTLBsp5A==, - } - hasBin: true - peerDependencies: - rxjs: ">=7.8.0" - - postcss-load-config@6.0.1: - resolution: - { - integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==, - } - engines: { node: ">= 18" } - peerDependencies: - jiti: ">=1.21.0" - postcss: ">=8.0.9" - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - jiti: - optional: true - postcss: - optional: true - tsx: - optional: true - yaml: - optional: true - - postcss@8.5.6: - resolution: - { - integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, - } - engines: { node: ^10 || ^12 || >=14 } - - prettier@3.8.1: - resolution: - { - integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, - } - engines: { node: ">=14" } - hasBin: true - - pretty-ms@9.3.0: - resolution: - { - integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==, - } - engines: { node: ">=18" } - - punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: ">=6" } - - read-pkg@10.1.0: - resolution: - { - integrity: sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==, - } - engines: { node: ">=20" } - - read-pkg@9.0.1: - resolution: - { - integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==, - } - engines: { node: ">=18" } - - readdirp@4.1.2: - resolution: - { - integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==, - } - engines: { node: ">= 14.18.0" } - - resolve-from@5.0.0: - resolution: - { - integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, - } - engines: { node: ">=8" } - - resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } - - restore-cursor@5.1.0: - resolution: - { - integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==, - } - engines: { node: ">=18" } - - rollup@4.57.1: - resolution: - { - integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==, - } - engines: { node: ">=18.0.0", npm: ">=8.0.0" } - hasBin: true - - rxjs@7.8.2: - resolution: - { - integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==, - } - - scale-ts@1.6.1: - resolution: - { - integrity: sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==, - } - - semver@7.7.4: - resolution: - { - integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, - } - engines: { node: ">=10" } - hasBin: true - - shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: ">=8" } - - shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: ">=8" } - - siginfo@2.0.0: - resolution: - { - integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, - } - - signal-exit@4.1.0: - resolution: - { - integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, - } - engines: { node: ">=14" } - - smoldot@2.0.40: - resolution: - { - integrity: sha512-h6XC/kKDLdZBBTI0X8y4ZxmaZ2KYVVB0+5isCQm6j26ljeNjHZUDOV+hf8VyoE23+jg00wrxNJ2IVcIAURxwtg==, - } - - sort-keys@5.1.0: - resolution: - { - integrity: sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==, - } - engines: { node: ">=12" } - - source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: ">=0.10.0" } - - source-map@0.8.0-beta.0: - resolution: - { - integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==, - } - engines: { node: ">= 8" } - deprecated: The work that was done in this beta branch won't be included in future versions - - spdx-correct@3.2.0: - resolution: - { - integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, - } - - spdx-exceptions@2.5.0: - resolution: - { - integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==, - } - - spdx-expression-parse@3.0.1: - resolution: - { - integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, - } - - spdx-license-ids@3.0.23: - resolution: - { - integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==, - } - - stackback@0.0.2: - resolution: - { - integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, - } - - std-env@3.10.0: - resolution: - { - integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==, - } - - stdin-discarder@0.3.1: - resolution: - { - integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==, - } - engines: { node: ">=18" } - - string-width@8.1.1: - resolution: - { - integrity: sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==, - } - engines: { node: ">=20" } - - strip-ansi@7.1.2: - resolution: - { - integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==, - } - engines: { node: ">=12" } - - strip-final-newline@4.0.0: - resolution: - { - integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==, - } - engines: { node: ">=18" } - - sucrase@3.35.1: - resolution: - { - integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==, - } - engines: { node: ">=16 || 14 >=14.17" } - hasBin: true - - tagged-tag@1.0.0: - resolution: - { - integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==, - } - engines: { node: ">=20" } - - thenify-all@1.6.0: - resolution: - { - integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==, - } - engines: { node: ">=0.8" } - - thenify@3.3.1: - resolution: - { - integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, - } - - tinybench@2.9.0: - resolution: - { - integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, - } - - tinyexec@0.3.2: - resolution: - { - integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==, - } - - tinyexec@1.0.2: - resolution: - { - integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==, - } - engines: { node: ">=18" } - - tinyglobby@0.2.15: - resolution: - { - integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, - } - engines: { node: ">=12.0.0" } - - tinyrainbow@3.0.3: - resolution: - { - integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==, - } - engines: { node: ">=14.0.0" } - - tr46@1.0.1: - resolution: - { - integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==, - } - - tree-kill@1.2.2: - resolution: - { - integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, - } - hasBin: true - - ts-interface-checker@0.1.13: - resolution: - { - integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==, - } - - tsc-prog@2.3.0: - resolution: - { - integrity: sha512-ycET2d75EgcX7y8EmG4KiZkLAwUzbY4xRhA6NU0uVbHkY4ZjrAAuzTMxXI85kOwATqPnBI5C/7y7rlpY0xdqHA==, - } - engines: { node: ">=12" } - peerDependencies: - typescript: ">=4" - - tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } - - tsup@8.5.0: - resolution: - { - integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==, - } - engines: { node: ">=18" } - hasBin: true - peerDependencies: - "@microsoft/api-extractor": ^7.36.0 - "@swc/core": ^1 - postcss: ^8.4.12 - typescript: ">=4.5.0" - peerDependenciesMeta: - "@microsoft/api-extractor": - optional: true - "@swc/core": - optional: true - postcss: - optional: true - typescript: - optional: true - - tsx@4.21.0: - resolution: - { - integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==, - } - engines: { node: ">=18.0.0" } - hasBin: true - - type-fest@4.41.0: - resolution: - { - integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==, - } - engines: { node: ">=16" } - - type-fest@5.4.4: - resolution: - { - integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==, - } - engines: { node: ">=20" } - - typescript@5.9.3: - resolution: - { - integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, - } - engines: { node: ">=14.17" } - hasBin: true - - ufo@1.6.3: - resolution: - { - integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==, - } - - undici-types@7.16.0: - resolution: - { - integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==, - } - - undici-types@7.18.2: - resolution: - { - integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==, - } - - unicorn-magic@0.1.0: - resolution: - { - integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==, - } - engines: { node: ">=18" } - - unicorn-magic@0.3.0: - resolution: - { - integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==, - } - engines: { node: ">=18" } - - unicorn-magic@0.4.0: - resolution: - { - integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==, - } - engines: { node: ">=20" } - - validate-npm-package-license@3.0.4: - resolution: - { - integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, - } - - vite@7.3.1: - resolution: - { - integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - hasBin: true - peerDependencies: - "@types/node": ^20.19.0 || >=22.12.0 - jiti: ">=1.21.0" - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: ">=0.54.8" - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - "@types/node": - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@4.0.18: - resolution: - { - integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==, - } - engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } - hasBin: true - peerDependencies: - "@edge-runtime/vm": "*" - "@opentelemetry/api": ^1.9.0 - "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 - "@vitest/browser-playwright": 4.0.18 - "@vitest/browser-preview": 4.0.18 - "@vitest/browser-webdriverio": 4.0.18 - "@vitest/ui": 4.0.18 - happy-dom: "*" - jsdom: "*" - peerDependenciesMeta: - "@edge-runtime/vm": - optional: true - "@opentelemetry/api": - optional: true - "@types/node": - optional: true - "@vitest/browser-playwright": - optional: true - "@vitest/browser-preview": - optional: true - "@vitest/browser-webdriverio": - optional: true - "@vitest/ui": - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - webidl-conversions@4.0.2: - resolution: - { - integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==, - } - - whatwg-url@7.1.0: - resolution: - { - integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==, - } - - which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: ">= 8" } - hasBin: true - - why-is-node-running@2.3.0: - resolution: - { - integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, - } - engines: { node: ">=8" } - hasBin: true - - write-file-atomic@5.0.1: - resolution: - { - integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } - - write-json-file@6.0.0: - resolution: - { - integrity: sha512-MNHcU3f9WxnNyR6MxsYSj64Jz0+dwIpisWKWq9gqLj/GwmA9INg3BZ3vt70/HB3GEwrnDQWr4RPrywnhNzmUFA==, - } - engines: { node: ">=18" } - - write-package@7.2.0: - resolution: - { - integrity: sha512-uMQTubF/vcu+Wd0b5BGtDmiXePd/+44hUWQz2nZPbs92/BnxRo74tqs+hqDo12RLiEd+CXFKUwxvvIZvtt34Jw==, - } - engines: { node: ">=18" } - - ws@8.19.0: - resolution: - { - integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==, - } - 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 - - yoctocolors@2.1.2: - resolution: - { - integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==, - } - engines: { node: ">=18" } - -snapshots: - "@babel/code-frame@7.29.0": - dependencies: - "@babel/helper-validator-identifier": 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - "@babel/helper-validator-identifier@7.28.5": {} - - "@commander-js/extra-typings@14.0.0(commander@14.0.3)": - dependencies: - commander: 14.0.3 - - "@esbuild/aix-ppc64@0.25.12": - optional: true - - "@esbuild/aix-ppc64@0.27.3": - optional: true - - "@esbuild/android-arm64@0.25.12": - optional: true - - "@esbuild/android-arm64@0.27.3": - optional: true - - "@esbuild/android-arm@0.25.12": - optional: true - - "@esbuild/android-arm@0.27.3": - optional: true - - "@esbuild/android-x64@0.25.12": - optional: true - - "@esbuild/android-x64@0.27.3": - optional: true - - "@esbuild/darwin-arm64@0.25.12": - optional: true - - "@esbuild/darwin-arm64@0.27.3": - optional: true - - "@esbuild/darwin-x64@0.25.12": - optional: true - - "@esbuild/darwin-x64@0.27.3": - optional: true - - "@esbuild/freebsd-arm64@0.25.12": - optional: true - - "@esbuild/freebsd-arm64@0.27.3": - optional: true - - "@esbuild/freebsd-x64@0.25.12": - optional: true - - "@esbuild/freebsd-x64@0.27.3": - optional: true - - "@esbuild/linux-arm64@0.25.12": - optional: true - - "@esbuild/linux-arm64@0.27.3": - optional: true - - "@esbuild/linux-arm@0.25.12": - optional: true - - "@esbuild/linux-arm@0.27.3": - optional: true - - "@esbuild/linux-ia32@0.25.12": - optional: true - - "@esbuild/linux-ia32@0.27.3": - optional: true - - "@esbuild/linux-loong64@0.25.12": - optional: true - - "@esbuild/linux-loong64@0.27.3": - optional: true - - "@esbuild/linux-mips64el@0.25.12": - optional: true - - "@esbuild/linux-mips64el@0.27.3": - optional: true - - "@esbuild/linux-ppc64@0.25.12": - optional: true - - "@esbuild/linux-ppc64@0.27.3": - optional: true - - "@esbuild/linux-riscv64@0.25.12": - optional: true - - "@esbuild/linux-riscv64@0.27.3": - optional: true - - "@esbuild/linux-s390x@0.25.12": - optional: true - - "@esbuild/linux-s390x@0.27.3": - optional: true - - "@esbuild/linux-x64@0.25.12": - optional: true - - "@esbuild/linux-x64@0.27.3": - optional: true - - "@esbuild/netbsd-arm64@0.25.12": - optional: true - - "@esbuild/netbsd-arm64@0.27.3": - optional: true - - "@esbuild/netbsd-x64@0.25.12": - optional: true - - "@esbuild/netbsd-x64@0.27.3": - optional: true - - "@esbuild/openbsd-arm64@0.25.12": - optional: true - - "@esbuild/openbsd-arm64@0.27.3": - optional: true - - "@esbuild/openbsd-x64@0.25.12": - optional: true - - "@esbuild/openbsd-x64@0.27.3": - optional: true - - "@esbuild/openharmony-arm64@0.25.12": - optional: true - - "@esbuild/openharmony-arm64@0.27.3": - optional: true - - "@esbuild/sunos-x64@0.25.12": - optional: true - - "@esbuild/sunos-x64@0.27.3": - optional: true - - "@esbuild/win32-arm64@0.25.12": - optional: true - - "@esbuild/win32-arm64@0.27.3": - optional: true - - "@esbuild/win32-ia32@0.25.12": - optional: true - - "@esbuild/win32-ia32@0.27.3": - optional: true - - "@esbuild/win32-x64@0.25.12": - optional: true - - "@esbuild/win32-x64@0.27.3": - optional: true - - "@jridgewell/gen-mapping@0.3.13": - dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 - "@jridgewell/trace-mapping": 0.3.31 - - "@jridgewell/resolve-uri@3.1.2": {} - - "@jridgewell/sourcemap-codec@1.5.5": {} - - "@jridgewell/trace-mapping@0.3.31": - dependencies: - "@jridgewell/resolve-uri": 3.1.2 - "@jridgewell/sourcemap-codec": 1.5.5 - - "@noble/ciphers@2.1.1": {} - - "@noble/curves@1.9.7": - dependencies: - "@noble/hashes": 1.8.0 - - "@noble/curves@2.0.1": - dependencies: - "@noble/hashes": 2.0.1 - - "@noble/hashes@1.8.0": {} - - "@noble/hashes@2.0.1": {} - - "@polkadot-api/cli@0.18.1(postcss@8.5.6)(tsx@4.21.0)": - dependencies: - "@commander-js/extra-typings": 14.0.0(commander@14.0.3) - "@polkadot-api/codegen": 0.21.2 - "@polkadot-api/ink-contracts": 0.4.6 - "@polkadot-api/json-rpc-provider": 0.0.4 - "@polkadot-api/known-chains": 0.9.18 - "@polkadot-api/legacy-provider": 0.3.8(rxjs@7.8.2) - "@polkadot-api/metadata-compatibility": 0.4.4 - "@polkadot-api/observable-client": 0.17.3(rxjs@7.8.2) - "@polkadot-api/polkadot-sdk-compat": 2.4.1 - "@polkadot-api/sm-provider": 0.1.16(@polkadot-api/smoldot@0.3.15) - "@polkadot-api/smoldot": 0.3.15 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/substrate-client": 0.5.0 - "@polkadot-api/utils": 0.2.0 - "@polkadot-api/wasm-executor": 0.2.3 - "@polkadot-api/ws-provider": 0.7.5 - "@types/node": 25.3.0 - commander: 14.0.3 - execa: 9.6.1 - fs.promises.exists: 1.1.4 - ora: 9.3.0 - read-pkg: 10.1.0 - rxjs: 7.8.2 - tsc-prog: 2.3.0(typescript@5.9.3) - tsup: 8.5.0(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) - typescript: 5.9.3 - write-package: 7.2.0 - transitivePeerDependencies: - - "@microsoft/api-extractor" - - "@swc/core" - - bufferutil - - jiti - - postcss - - supports-color - - tsx - - utf-8-validate - - yaml - - "@polkadot-api/codegen@0.21.2": - dependencies: - "@polkadot-api/ink-contracts": 0.4.6 - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/metadata-compatibility": 0.4.4 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/descriptors@file:.papi/descriptors(polkadot-api@1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0))": - dependencies: - polkadot-api: 1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0) - - "@polkadot-api/ink-contracts@0.4.6": - dependencies: - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/json-rpc-provider-proxy@0.2.8": {} - - "@polkadot-api/json-rpc-provider@0.0.4": {} - - "@polkadot-api/known-chains@0.9.18": {} - - "@polkadot-api/legacy-provider@0.3.8(rxjs@7.8.2)": - dependencies: - "@polkadot-api/json-rpc-provider": 0.0.4 - "@polkadot-api/raw-client": 0.1.1 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - rxjs: 7.8.2 - - "@polkadot-api/logs-provider@0.0.6": - dependencies: - "@polkadot-api/json-rpc-provider": 0.0.4 - - "@polkadot-api/merkleize-metadata@1.1.29": - dependencies: - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/metadata-builders@0.13.9": - dependencies: - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/metadata-compatibility@0.4.4": - dependencies: - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/substrate-bindings": 0.17.0 - - "@polkadot-api/observable-client@0.17.3(rxjs@7.8.2)": - dependencies: - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/substrate-client": 0.5.0 - "@polkadot-api/utils": 0.2.0 - rxjs: 7.8.2 - - "@polkadot-api/pjs-signer@0.6.19": - dependencies: - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/polkadot-signer": 0.1.6 - "@polkadot-api/signers-common": 0.1.20 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/polkadot-sdk-compat@2.4.1": - dependencies: - "@polkadot-api/json-rpc-provider": 0.0.4 - - "@polkadot-api/polkadot-signer@0.1.6": {} - - "@polkadot-api/raw-client@0.1.1": - dependencies: - "@polkadot-api/json-rpc-provider": 0.0.4 - - "@polkadot-api/signer@0.2.13": - dependencies: - "@noble/hashes": 2.0.1 - "@polkadot-api/merkleize-metadata": 1.1.29 - "@polkadot-api/polkadot-signer": 0.1.6 - "@polkadot-api/signers-common": 0.1.20 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/signers-common@0.1.20": - dependencies: - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/polkadot-signer": 0.1.6 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/sm-provider@0.1.16(@polkadot-api/smoldot@0.3.15)": - dependencies: - "@polkadot-api/json-rpc-provider": 0.0.4 - "@polkadot-api/json-rpc-provider-proxy": 0.2.8 - "@polkadot-api/smoldot": 0.3.15 - - "@polkadot-api/smoldot@0.3.15": - dependencies: - "@types/node": 24.10.13 - smoldot: 2.0.40 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - "@polkadot-api/substrate-bindings@0.17.0": - dependencies: - "@noble/hashes": 2.0.1 - "@polkadot-api/utils": 0.2.0 - "@scure/base": 2.0.0 - scale-ts: 1.6.1 - - "@polkadot-api/substrate-client@0.5.0": - dependencies: - "@polkadot-api/json-rpc-provider": 0.0.4 - "@polkadot-api/raw-client": 0.1.1 - "@polkadot-api/utils": 0.2.0 - - "@polkadot-api/utils@0.2.0": {} - - "@polkadot-api/wasm-executor@0.2.3": {} - - "@polkadot-api/ws-provider@0.7.5": - dependencies: - "@polkadot-api/json-rpc-provider": 0.0.4 - "@polkadot-api/json-rpc-provider-proxy": 0.2.8 - "@types/ws": 8.18.1 - ws: 8.19.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - "@polkadot-labs/hdkd-helpers@0.0.25": - dependencies: - "@noble/curves": 2.0.1 - "@noble/hashes": 2.0.1 - "@scure/base": 2.0.0 - "@scure/sr25519": 0.3.0 - scale-ts: 1.6.1 - - "@polkadot-labs/hdkd-helpers@0.0.27": - dependencies: - "@noble/curves": 2.0.1 - "@noble/hashes": 2.0.1 - "@scure/base": 2.0.0 - "@scure/sr25519": 1.0.0 - scale-ts: 1.6.1 - - "@polkadot-labs/hdkd@0.0.25": - dependencies: - "@polkadot-labs/hdkd-helpers": 0.0.27 - - "@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/util-crypto": 14.0.1(@polkadot/util@14.0.1) - tslib: 2.8.1 - - "@polkadot/networks@14.0.1": - dependencies: - "@polkadot/util": 14.0.1 - "@substrate/ss58-registry": 1.51.0 - tslib: 2.8.1 - - "@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1)": - dependencies: - "@noble/curves": 1.9.7 - "@noble/hashes": 1.8.0 - "@polkadot/networks": 14.0.1 - "@polkadot/util": 14.0.1 - "@polkadot/wasm-crypto": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-bigint": 14.0.1 - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - "@scure/base": 1.2.6 - "@scure/sr25519": 0.2.0 - tslib: 2.8.1 - - "@polkadot/util@14.0.1": - dependencies: - "@polkadot/x-bigint": 14.0.1 - "@polkadot/x-global": 14.0.1 - "@polkadot/x-textdecoder": 14.0.1 - "@polkadot/x-textencoder": 14.0.1 - "@types/bn.js": 5.2.0 - bn.js: 5.2.2 - tslib: 2.8.1 - - "@polkadot/wasm-bridge@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - tslib: 2.8.1 - - "@polkadot/wasm-crypto-asmjs@7.5.4(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - tslib: 2.8.1 - - "@polkadot/wasm-crypto-init@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-bridge": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-crypto-asmjs": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-crypto-wasm": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - tslib: 2.8.1 - - "@polkadot/wasm-crypto-wasm@7.5.4(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - tslib: 2.8.1 - - "@polkadot/wasm-crypto@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-bridge": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-crypto-asmjs": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-crypto-init": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-crypto-wasm": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - tslib: 2.8.1 - - "@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-bigint@14.0.1": - dependencies: - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-global@14.0.1": - dependencies: - tslib: 2.8.1 - - "@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-textdecoder@14.0.1": - dependencies: - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-textencoder@14.0.1": - dependencies: - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@rollup/rollup-android-arm-eabi@4.57.1": - optional: true - - "@rollup/rollup-android-arm64@4.57.1": - optional: true - - "@rollup/rollup-darwin-arm64@4.57.1": - optional: true - - "@rollup/rollup-darwin-x64@4.57.1": - optional: true - - "@rollup/rollup-freebsd-arm64@4.57.1": - optional: true - - "@rollup/rollup-freebsd-x64@4.57.1": - optional: true - - "@rollup/rollup-linux-arm-gnueabihf@4.57.1": - optional: true - - "@rollup/rollup-linux-arm-musleabihf@4.57.1": - optional: true - - "@rollup/rollup-linux-arm64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-arm64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-loong64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-loong64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-ppc64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-ppc64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-riscv64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-riscv64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-s390x-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-x64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-x64-musl@4.57.1": - optional: true - - "@rollup/rollup-openbsd-x64@4.57.1": - optional: true - - "@rollup/rollup-openharmony-arm64@4.57.1": - optional: true - - "@rollup/rollup-win32-arm64-msvc@4.57.1": - optional: true - - "@rollup/rollup-win32-ia32-msvc@4.57.1": - optional: true - - "@rollup/rollup-win32-x64-gnu@4.57.1": - optional: true - - "@rollup/rollup-win32-x64-msvc@4.57.1": - optional: true - - "@rx-state/core@0.1.4(rxjs@7.8.2)": - dependencies: - rxjs: 7.8.2 - - "@scure/base@1.2.6": {} - - "@scure/base@2.0.0": {} - - "@scure/sr25519@0.2.0": - dependencies: - "@noble/curves": 1.9.7 - "@noble/hashes": 1.8.0 - - "@scure/sr25519@0.3.0": - dependencies: - "@noble/curves": 2.0.1 - "@noble/hashes": 2.0.1 - - "@scure/sr25519@1.0.0": - dependencies: - "@noble/curves": 2.0.1 - "@noble/hashes": 2.0.1 - - "@sec-ant/readable-stream@0.4.1": {} - - "@sindresorhus/merge-streams@4.0.0": {} - - "@standard-schema/spec@1.1.0": {} - - "@substrate/ss58-registry@1.51.0": {} - - "@types/bn.js@5.2.0": - dependencies: - "@types/node": 25.3.0 - - "@types/chai@5.2.3": - dependencies: - "@types/deep-eql": 4.0.2 - assertion-error: 2.0.1 - - "@types/deep-eql@4.0.2": {} - - "@types/estree@1.0.8": {} - - "@types/node@24.10.13": - dependencies: - undici-types: 7.16.0 - - "@types/node@25.3.0": - dependencies: - undici-types: 7.18.2 - - "@types/normalize-package-data@2.4.4": {} - - "@types/ws@8.18.1": - dependencies: - "@types/node": 25.3.0 - - "@vitest/expect@4.0.18": - dependencies: - "@standard-schema/spec": 1.1.0 - "@types/chai": 5.2.3 - "@vitest/spy": 4.0.18 - "@vitest/utils": 4.0.18 - chai: 6.2.2 - tinyrainbow: 3.0.3 - - "@vitest/mocker@4.0.18(vite@7.3.1(@types/node@24.10.13)(tsx@4.21.0))": - dependencies: - "@vitest/spy": 4.0.18 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@24.10.13)(tsx@4.21.0) - - "@vitest/pretty-format@4.0.18": - dependencies: - tinyrainbow: 3.0.3 - - "@vitest/runner@4.0.18": - dependencies: - "@vitest/utils": 4.0.18 - pathe: 2.0.3 - - "@vitest/snapshot@4.0.18": - dependencies: - "@vitest/pretty-format": 4.0.18 - magic-string: 0.30.21 - pathe: 2.0.3 - - "@vitest/spy@4.0.18": {} - - "@vitest/utils@4.0.18": - dependencies: - "@vitest/pretty-format": 4.0.18 - tinyrainbow: 3.0.3 - - acorn@8.16.0: {} - - ansi-regex@6.2.2: {} - - any-promise@1.3.0: {} - - assertion-error@2.0.1: {} - - bn.js@5.2.2: {} - - bundle-require@5.1.0(esbuild@0.25.12): - dependencies: - esbuild: 0.25.12 - load-tsconfig: 0.2.5 - - cac@6.7.14: {} - - chai@6.2.2: {} - - chalk@5.6.2: {} - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - - cli-spinners@3.4.0: {} - - commander@14.0.3: {} - - commander@4.1.1: {} - - confbox@0.1.8: {} - - consola@3.4.2: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - deepmerge-ts@7.1.5: {} - - detect-indent@7.0.2: {} - - es-module-lexer@1.7.0: {} - - esbuild@0.25.12: - optionalDependencies: - "@esbuild/aix-ppc64": 0.25.12 - "@esbuild/android-arm": 0.25.12 - "@esbuild/android-arm64": 0.25.12 - "@esbuild/android-x64": 0.25.12 - "@esbuild/darwin-arm64": 0.25.12 - "@esbuild/darwin-x64": 0.25.12 - "@esbuild/freebsd-arm64": 0.25.12 - "@esbuild/freebsd-x64": 0.25.12 - "@esbuild/linux-arm": 0.25.12 - "@esbuild/linux-arm64": 0.25.12 - "@esbuild/linux-ia32": 0.25.12 - "@esbuild/linux-loong64": 0.25.12 - "@esbuild/linux-mips64el": 0.25.12 - "@esbuild/linux-ppc64": 0.25.12 - "@esbuild/linux-riscv64": 0.25.12 - "@esbuild/linux-s390x": 0.25.12 - "@esbuild/linux-x64": 0.25.12 - "@esbuild/netbsd-arm64": 0.25.12 - "@esbuild/netbsd-x64": 0.25.12 - "@esbuild/openbsd-arm64": 0.25.12 - "@esbuild/openbsd-x64": 0.25.12 - "@esbuild/openharmony-arm64": 0.25.12 - "@esbuild/sunos-x64": 0.25.12 - "@esbuild/win32-arm64": 0.25.12 - "@esbuild/win32-ia32": 0.25.12 - "@esbuild/win32-x64": 0.25.12 - - esbuild@0.27.3: - optionalDependencies: - "@esbuild/aix-ppc64": 0.27.3 - "@esbuild/android-arm": 0.27.3 - "@esbuild/android-arm64": 0.27.3 - "@esbuild/android-x64": 0.27.3 - "@esbuild/darwin-arm64": 0.27.3 - "@esbuild/darwin-x64": 0.27.3 - "@esbuild/freebsd-arm64": 0.27.3 - "@esbuild/freebsd-x64": 0.27.3 - "@esbuild/linux-arm": 0.27.3 - "@esbuild/linux-arm64": 0.27.3 - "@esbuild/linux-ia32": 0.27.3 - "@esbuild/linux-loong64": 0.27.3 - "@esbuild/linux-mips64el": 0.27.3 - "@esbuild/linux-ppc64": 0.27.3 - "@esbuild/linux-riscv64": 0.27.3 - "@esbuild/linux-s390x": 0.27.3 - "@esbuild/linux-x64": 0.27.3 - "@esbuild/netbsd-arm64": 0.27.3 - "@esbuild/netbsd-x64": 0.27.3 - "@esbuild/openbsd-arm64": 0.27.3 - "@esbuild/openbsd-x64": 0.27.3 - "@esbuild/openharmony-arm64": 0.27.3 - "@esbuild/sunos-x64": 0.27.3 - "@esbuild/win32-arm64": 0.27.3 - "@esbuild/win32-ia32": 0.27.3 - "@esbuild/win32-x64": 0.27.3 - - estree-walker@3.0.3: - dependencies: - "@types/estree": 1.0.8 - - execa@9.6.1: - dependencies: - "@sindresorhus/merge-streams": 4.0.0 - cross-spawn: 7.0.6 - figures: 6.1.0 - get-stream: 9.0.1 - human-signals: 8.0.1 - is-plain-obj: 4.1.0 - is-stream: 4.0.1 - npm-run-path: 6.0.0 - pretty-ms: 9.3.0 - signal-exit: 4.1.0 - strip-final-newline: 4.0.0 - yoctocolors: 2.1.2 - - expect-type@1.3.0: {} - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - figures@6.1.0: - dependencies: - is-unicode-supported: 2.1.0 - - fix-dts-default-cjs-exports@1.0.1: - dependencies: - magic-string: 0.30.21 - mlly: 1.8.0 - rollup: 4.57.1 - - fs.promises.exists@1.1.4: {} - - fsevents@2.3.3: - optional: true - - get-east-asian-width@1.4.0: {} - - get-stream@9.0.1: - dependencies: - "@sec-ant/readable-stream": 0.4.1 - is-stream: 4.0.1 - - get-tsconfig@4.13.6: - dependencies: - resolve-pkg-maps: 1.0.0 - optional: true - - hosted-git-info@7.0.2: - dependencies: - lru-cache: 10.4.3 - - hosted-git-info@9.0.2: - dependencies: - lru-cache: 11.2.6 - - human-signals@8.0.1: {} - - imurmurhash@0.1.4: {} - - index-to-position@1.2.0: {} - - is-interactive@2.0.0: {} - - is-plain-obj@4.1.0: {} - - is-stream@4.0.1: {} - - is-unicode-supported@2.1.0: {} - - isexe@2.0.0: {} - - joycon@3.1.1: {} - - js-tokens@4.0.0: {} - - lilconfig@3.1.3: {} - - lines-and-columns@1.2.4: {} - - load-tsconfig@0.2.5: {} - - lodash.sortby@4.7.0: {} - - log-symbols@7.0.1: - dependencies: - is-unicode-supported: 2.1.0 - yoctocolors: 2.1.2 - - lru-cache@10.4.3: {} - - lru-cache@11.2.6: {} - - magic-string@0.30.21: - dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 - - mimic-function@5.0.1: {} - - mlkem@2.5.0: {} - - mlly@1.8.0: - dependencies: - acorn: 8.16.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.3 - - ms@2.1.3: {} - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nanoid@3.3.11: {} - - normalize-package-data@6.0.2: - dependencies: - hosted-git-info: 7.0.2 - semver: 7.7.4 - validate-npm-package-license: 3.0.4 - - normalize-package-data@8.0.0: - dependencies: - hosted-git-info: 9.0.2 - semver: 7.7.4 - validate-npm-package-license: 3.0.4 - - npm-run-path@6.0.0: - dependencies: - path-key: 4.0.0 - unicorn-magic: 0.3.0 - - object-assign@4.1.1: {} - - obug@2.1.1: {} - - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - - ora@9.3.0: - dependencies: - chalk: 5.6.2 - cli-cursor: 5.0.0 - cli-spinners: 3.4.0 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 7.0.1 - stdin-discarder: 0.3.1 - string-width: 8.1.1 - - parse-json@8.3.0: - dependencies: - "@babel/code-frame": 7.29.0 - index-to-position: 1.2.0 - type-fest: 4.41.0 - - parse-ms@4.0.0: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - pathe@2.0.3: {} - - picocolors@1.1.1: {} - - picomatch@4.0.3: {} - - pirates@4.0.7: {} - - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.8.0 - pathe: 2.0.3 - - polkadot-api@1.23.3(postcss@8.5.6)(rxjs@7.8.2)(tsx@4.21.0): - dependencies: - "@polkadot-api/cli": 0.18.1(postcss@8.5.6)(tsx@4.21.0) - "@polkadot-api/ink-contracts": 0.4.6 - "@polkadot-api/json-rpc-provider": 0.0.4 - "@polkadot-api/known-chains": 0.9.18 - "@polkadot-api/logs-provider": 0.0.6 - "@polkadot-api/metadata-builders": 0.13.9 - "@polkadot-api/metadata-compatibility": 0.4.4 - "@polkadot-api/observable-client": 0.17.3(rxjs@7.8.2) - "@polkadot-api/pjs-signer": 0.6.19 - "@polkadot-api/polkadot-sdk-compat": 2.4.1 - "@polkadot-api/polkadot-signer": 0.1.6 - "@polkadot-api/signer": 0.2.13 - "@polkadot-api/sm-provider": 0.1.16(@polkadot-api/smoldot@0.3.15) - "@polkadot-api/smoldot": 0.3.15 - "@polkadot-api/substrate-bindings": 0.17.0 - "@polkadot-api/substrate-client": 0.5.0 - "@polkadot-api/utils": 0.2.0 - "@polkadot-api/ws-provider": 0.7.5 - "@rx-state/core": 0.1.4(rxjs@7.8.2) - rxjs: 7.8.2 - transitivePeerDependencies: - - "@microsoft/api-extractor" - - "@swc/core" - - bufferutil - - jiti - - postcss - - supports-color - - tsx - - utf-8-validate - - yaml - - postcss-load-config@6.0.1(postcss@8.5.6)(tsx@4.21.0): - dependencies: - lilconfig: 3.1.3 - optionalDependencies: - postcss: 8.5.6 - tsx: 4.21.0 - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prettier@3.8.1: {} - - pretty-ms@9.3.0: - dependencies: - parse-ms: 4.0.0 - - punycode@2.3.1: {} - - read-pkg@10.1.0: - dependencies: - "@types/normalize-package-data": 2.4.4 - normalize-package-data: 8.0.0 - parse-json: 8.3.0 - type-fest: 5.4.4 - unicorn-magic: 0.4.0 - - read-pkg@9.0.1: - dependencies: - "@types/normalize-package-data": 2.4.4 - normalize-package-data: 6.0.2 - parse-json: 8.3.0 - type-fest: 4.41.0 - unicorn-magic: 0.1.0 - - readdirp@4.1.2: {} - - resolve-from@5.0.0: {} - - resolve-pkg-maps@1.0.0: - optional: true - - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - - rollup@4.57.1: - dependencies: - "@types/estree": 1.0.8 - optionalDependencies: - "@rollup/rollup-android-arm-eabi": 4.57.1 - "@rollup/rollup-android-arm64": 4.57.1 - "@rollup/rollup-darwin-arm64": 4.57.1 - "@rollup/rollup-darwin-x64": 4.57.1 - "@rollup/rollup-freebsd-arm64": 4.57.1 - "@rollup/rollup-freebsd-x64": 4.57.1 - "@rollup/rollup-linux-arm-gnueabihf": 4.57.1 - "@rollup/rollup-linux-arm-musleabihf": 4.57.1 - "@rollup/rollup-linux-arm64-gnu": 4.57.1 - "@rollup/rollup-linux-arm64-musl": 4.57.1 - "@rollup/rollup-linux-loong64-gnu": 4.57.1 - "@rollup/rollup-linux-loong64-musl": 4.57.1 - "@rollup/rollup-linux-ppc64-gnu": 4.57.1 - "@rollup/rollup-linux-ppc64-musl": 4.57.1 - "@rollup/rollup-linux-riscv64-gnu": 4.57.1 - "@rollup/rollup-linux-riscv64-musl": 4.57.1 - "@rollup/rollup-linux-s390x-gnu": 4.57.1 - "@rollup/rollup-linux-x64-gnu": 4.57.1 - "@rollup/rollup-linux-x64-musl": 4.57.1 - "@rollup/rollup-openbsd-x64": 4.57.1 - "@rollup/rollup-openharmony-arm64": 4.57.1 - "@rollup/rollup-win32-arm64-msvc": 4.57.1 - "@rollup/rollup-win32-ia32-msvc": 4.57.1 - "@rollup/rollup-win32-x64-gnu": 4.57.1 - "@rollup/rollup-win32-x64-msvc": 4.57.1 - fsevents: 2.3.3 - - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - - scale-ts@1.6.1: {} - - semver@7.7.4: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - siginfo@2.0.0: {} - - signal-exit@4.1.0: {} - - smoldot@2.0.40: - dependencies: - ws: 8.19.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - sort-keys@5.1.0: - dependencies: - is-plain-obj: 4.1.0 - - source-map-js@1.2.1: {} - - source-map@0.8.0-beta.0: - dependencies: - whatwg-url: 7.1.0 - - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.23 - - spdx-exceptions@2.5.0: {} - - spdx-expression-parse@3.0.1: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.23 - - spdx-license-ids@3.0.23: {} - - stackback@0.0.2: {} - - std-env@3.10.0: {} - - stdin-discarder@0.3.1: {} - - string-width@8.1.1: - dependencies: - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - - strip-final-newline@4.0.0: {} - - sucrase@3.35.1: - dependencies: - "@jridgewell/gen-mapping": 0.3.13 - commander: 4.1.1 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.7 - tinyglobby: 0.2.15 - ts-interface-checker: 0.1.13 - - tagged-tag@1.0.0: {} - - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - tinybench@2.9.0: {} - - tinyexec@0.3.2: {} - - tinyexec@1.0.2: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - tinyrainbow@3.0.3: {} - - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - - tree-kill@1.2.2: {} - - ts-interface-checker@0.1.13: {} - - tsc-prog@2.3.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - - tslib@2.8.1: {} - - tsup@8.5.0(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3): - dependencies: - bundle-require: 5.1.0(esbuild@0.25.12) - cac: 6.7.14 - chokidar: 4.0.3 - consola: 3.4.2 - debug: 4.4.3 - esbuild: 0.25.12 - fix-dts-default-cjs-exports: 1.0.1 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.5.6)(tsx@4.21.0) - resolve-from: 5.0.0 - rollup: 4.57.1 - source-map: 0.8.0-beta.0 - sucrase: 3.35.1 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tree-kill: 1.2.2 - optionalDependencies: - postcss: 8.5.6 - typescript: 5.9.3 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - - tsx@4.21.0: - dependencies: - esbuild: 0.27.3 - get-tsconfig: 4.13.6 - optionalDependencies: - fsevents: 2.3.3 - optional: true - - type-fest@4.41.0: {} - - type-fest@5.4.4: - dependencies: - tagged-tag: 1.0.0 - - typescript@5.9.3: {} - - ufo@1.6.3: {} - - undici-types@7.16.0: {} - - undici-types@7.18.2: {} - - unicorn-magic@0.1.0: {} - - unicorn-magic@0.3.0: {} - - unicorn-magic@0.4.0: {} - - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - vite@7.3.1(@types/node@24.10.13)(tsx@4.21.0): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.57.1 - tinyglobby: 0.2.15 - optionalDependencies: - "@types/node": 24.10.13 - fsevents: 2.3.3 - tsx: 4.21.0 - - vitest@4.0.18(@types/node@24.10.13)(tsx@4.21.0): - dependencies: - "@vitest/expect": 4.0.18 - "@vitest/mocker": 4.0.18(vite@7.3.1(@types/node@24.10.13)(tsx@4.21.0)) - "@vitest/pretty-format": 4.0.18 - "@vitest/runner": 4.0.18 - "@vitest/snapshot": 4.0.18 - "@vitest/spy": 4.0.18 - "@vitest/utils": 4.0.18 - es-module-lexer: 1.7.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@24.10.13)(tsx@4.21.0) - why-is-node-running: 2.3.0 - optionalDependencies: - "@types/node": 24.10.13 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - - webidl-conversions@4.0.2: {} - - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - write-file-atomic@5.0.1: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - - write-json-file@6.0.0: - dependencies: - detect-indent: 7.0.2 - is-plain-obj: 4.1.0 - sort-keys: 5.1.0 - write-file-atomic: 5.0.1 - - write-package@7.2.0: - dependencies: - deepmerge-ts: 7.1.5 - read-pkg: 9.0.1 - sort-keys: 5.1.0 - type-fest: 4.41.0 - write-json-file: 6.0.0 - - ws@8.19.0: {} - - yoctocolors@2.1.2: {} diff --git a/e2e/pnpm-workspace.yaml b/e2e/pnpm-workspace.yaml deleted file mode 100644 index 5c59deeac3..0000000000 --- a/e2e/pnpm-workspace.yaml +++ /dev/null @@ -1,17 +0,0 @@ -packages: - - shared - - shield - - staking - -catalog: - "@noble/ciphers": "^2.1.1" - "@polkadot/keyring": "^14.0.1" - "@polkadot/util": "^14.0.1" - "@polkadot/util-crypto": "^14.0.1" - "@polkadot-labs/hdkd": "^0.0.25" - "@polkadot-labs/hdkd-helpers": "^0.0.25" - "@types/node": "^24" - "mlkem": "^2.5.0" - "polkadot-api": "^1.22.0" - "prettier": "^3.0.0" - "vitest": "^4.0.0" diff --git a/e2e/setup_env.sh b/e2e/setup_env.sh deleted file mode 100755 index 96b531e22b..0000000000 --- a/e2e/setup_env.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/bash -# -# Verify and set up the development environment for e2e tests. -# Checks for nvm, the correct Node.js version (.nvmrc), pnpm, jq, and yq. -# Installs what it can, exits with an error for what it cannot. -# -set -e - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -NVMRC="$SCRIPT_DIR/.nvmrc" - -check() { - local name="$1" - if command -v "$name" &>/dev/null; then - echo " $name: $(command -v "$name")" - return 0 - fi - return 1 -} - -echo "==> Checking prerequisites..." - -# -- nvm -- -NVM_DIR="${NVM_DIR:-$HOME/.nvm}" -if [ -s "$NVM_DIR/nvm.sh" ]; then - echo " nvm: $NVM_DIR" - # shellcheck source=/dev/null - source "$NVM_DIR/nvm.sh" -else - echo "ERROR: nvm not found. Install it from https://github.com/nvm-sh/nvm" - exit 1 -fi - -# -- Node.js (version from .nvmrc) -- -REQUIRED_NODE="$(cat "$NVMRC")" -if ! nvm ls "$REQUIRED_NODE" &>/dev/null; then - echo " Node $REQUIRED_NODE not installed, installing..." - nvm install "$REQUIRED_NODE" -fi -nvm use "$REQUIRED_NODE" -echo " node: $(node --version)" - -# -- pnpm -- -if ! check pnpm; then - echo " pnpm not found, installing..." - npm install -g pnpm - check pnpm || { echo "ERROR: Failed to install pnpm"; exit 1; } -fi - -# -- jq -- -if ! check jq; then - echo "ERROR: jq not found. Install it:" - echo " macOS: brew install jq" - echo " Ubuntu: sudo apt install jq" - exit 1 -fi - -# -- yq -- -if ! check yq; then - echo "ERROR: yq not found. Install it:" - echo " macOS: brew install yq" - echo " Ubuntu: sudo snap install yq" - exit 1 -fi - -echo "==> All prerequisites satisfied." diff --git a/e2e/shared/address.ts b/e2e/shared/address.ts deleted file mode 100644 index 75eff6342a..0000000000 --- a/e2e/shared/address.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { sr25519CreateDerive } from "@polkadot-labs/hdkd"; -import { - DEV_PHRASE, - entropyToMiniSecret, - mnemonicToEntropy, - KeyPair, -} from "@polkadot-labs/hdkd-helpers"; -import { getPolkadotSigner } from "polkadot-api/signer"; -import { PolkadotSigner } from "polkadot-api"; -import { randomBytes } from "crypto"; -import { ss58Address } from "@polkadot-labs/hdkd-helpers"; - -export const SS58_PREFIX = 42; - -// ─── KEYPAIR UTILITIES ─────────────────────────────────────────────────────── - -export function getKeypairFromPath(path: string): KeyPair { - const entropy = mnemonicToEntropy(DEV_PHRASE); - const miniSecret = entropyToMiniSecret(entropy); - const derive = sr25519CreateDerive(miniSecret); - return derive(path); -} - -export const getAlice = () => getKeypairFromPath("//Alice"); - -export function getRandomSubstrateKeypair(): KeyPair { - const seed = randomBytes(32); - const miniSecret = entropyToMiniSecret(seed); - const derive = sr25519CreateDerive(miniSecret); - return derive(""); -} - -// ─── SIGNER UTILITIES ──────────────────────────────────────────────────────── - -export function getSignerFromKeypair(keypair: KeyPair): PolkadotSigner { - return getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign); -} - -export function getSignerFromPath(path: string): PolkadotSigner { - return getSignerFromKeypair(getKeypairFromPath(path)); -} - -export const getAliceSigner = () => getSignerFromPath("//Alice"); - -// ─── ADDRESS UTILITIES ─────────────────────────────────────────────────────── - -export function convertPublicKeyToSs58(publicKey: Uint8Array): string { - return ss58Address(publicKey, SS58_PREFIX); -} diff --git a/e2e/shared/balance.ts b/e2e/shared/balance.ts deleted file mode 100644 index c54d2c7e18..0000000000 --- a/e2e/shared/balance.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; -import { TypedApi } from "polkadot-api"; -import { getAliceSigner } from "./address.js"; -import { waitForTransactionWithRetry } from "./transactions.js"; - -export const TAO = BigInt(1000000000); // 10^9 RAO per TAO - -export function tao(value: number): bigint { - return TAO * BigInt(value); -} - -export async function getBalance( - api: TypedApi<typeof subtensor>, - ss58Address: string, -): Promise<bigint> { - const account = await api.query.System.Account.getValue(ss58Address); - return account.data.free; -} - -export async function forceSetBalance( - api: TypedApi<typeof subtensor>, - ss58Address: string, - amount: bigint = tao(1e10), -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.Balances.force_set_balance({ - who: MultiAddress.Id(ss58Address), - new_free: amount, - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "force_set_balance"); -} diff --git a/e2e/shared/chainspec.ts b/e2e/shared/chainspec.ts deleted file mode 100644 index 514b6c0028..0000000000 --- a/e2e/shared/chainspec.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { spawn, execFileSync } from "node:child_process"; -import { writeFile, readFile } from "node:fs/promises"; -import { Keyring } from "@polkadot/keyring"; -import { log } from "./node.js"; - -// --------------------------------------------------------------------------- -// Chain spec generation -// --------------------------------------------------------------------------- - -/** - * Generate a raw chain spec. If `patchSpec` is provided, first generates a - * non-raw spec, applies the patch, then converts to raw. This allows adding - * extra authorities, balances, etc. without modifying the Rust chain spec. - */ -export const generateChainSpec = async ( - binaryPath: string, - outputPath: string, - patchSpec?: (spec: any) => void, -) => { - if (!patchSpec) { - return generateRawChainSpec(binaryPath, outputPath, "local"); - } - - // 2-step: generate non-raw → patch → generate raw. - const nonRawPath = outputPath + ".nonraw.json"; - - await new Promise<void>((resolve, reject) => { - const proc = spawn(binaryPath, [ - "build-spec", - "--disable-default-bootnode", - "--chain", - "local", - ]); - const chunks: Buffer[] = []; - proc.stdout.on("data", (chunk: Buffer) => chunks.push(chunk)); - let stderr = ""; - proc.stderr?.on("data", (chunk: Buffer) => { - stderr += chunk.toString(); - }); - proc.on("close", async (code) => { - if (code !== 0) { - reject(new Error(`Failed to generate non-raw chain spec (exit ${code}): ${stderr}`)); - return; - } - await writeFile(nonRawPath, Buffer.concat(chunks)); - resolve(); - }); - proc.on("error", reject); - }); - - const specJson = JSON.parse(await readFile(nonRawPath, "utf-8")); - patchSpec(specJson); - await writeFile(nonRawPath, JSON.stringify(specJson, null, 2)); - - await generateRawChainSpec(binaryPath, outputPath, nonRawPath); -}; - -async function generateRawChainSpec(binaryPath: string, outputPath: string, chain: string) { - return new Promise<void>((resolve, reject) => { - const proc = spawn(binaryPath, [ - "build-spec", - "--disable-default-bootnode", - "--raw", - "--chain", - chain, - ]); - - const chunks: Buffer[] = []; - proc.stdout.on("data", (chunk: Buffer) => chunks.push(chunk)); - - let stderr = ""; - proc.stderr?.on("data", (chunk: Buffer) => { - stderr += chunk.toString(); - }); - - proc.on("close", async (code) => { - if (code !== 0) { - reject(new Error(`Failed to generate chain spec (exit ${code}): ${stderr}`)); - return; - } - const data = Buffer.concat(chunks); - await writeFile(outputPath, data); - log(`Chain spec written to ${outputPath} (${data.length} bytes)`); - resolve(); - }); - - proc.on("error", reject); - }); -} - -// --------------------------------------------------------------------------- -// Chain spec patching helpers (composable) -// --------------------------------------------------------------------------- - -/** - * Extract the genesis runtime patch object from a non-raw chain spec. - * Works with both the `runtimeGenesis.patch` and legacy `runtime` formats. - */ -export function getGenesisPatch(spec: any): any { - const patch = spec.genesis?.runtimeGenesis?.patch ?? spec.genesis?.runtime; - if (!patch) throw new Error("Cannot find genesis patch in chain spec"); - return patch; -} - -/** Add an Aura authority (sr25519 address) to the chain spec. */ -export function addAuraAuthority(patch: any, address: string) { - if (patch.aura?.authorities) { - patch.aura.authorities.push(address); - } -} - -/** Add a GRANDPA authority (ed25519 address, weight) to the chain spec. */ -export function addGrandpaAuthority(patch: any, address: string, weight = 1) { - if (patch.grandpa?.authorities) { - patch.grandpa.authorities.push([address, weight]); - } -} - -/** Add a balance entry to the chain spec. */ -export function addBalance(patch: any, address: string, amount: number | bigint) { - if (patch.balances?.balances) { - patch.balances.balances.push([address, Number(amount)]); - } -} - -// --------------------------------------------------------------------------- -// Authority key helpers -// --------------------------------------------------------------------------- - -export type AuthorityKeys = { - aura: string; - grandpa: string; - account: string; -}; - -/** Derive authority keys (aura sr25519, grandpa ed25519, account) from a seed. */ -export function generateAuthorityKeys(seed: string): AuthorityKeys { - const sr = new Keyring({ type: "sr25519" }); - const ed = new Keyring({ type: "ed25519" }); - return { - aura: sr.addFromUri(`//${seed}`).address, - grandpa: ed.addFromUri(`//${seed}`).address, - account: sr.addFromUri(`//${seed}`).address, - }; -} - -/** - * Convenience: add a full authority (aura + grandpa + funded account) to a - * chain spec genesis patch. Derives keys from the given seed. - */ -export function addAuthority(patch: any, seed: string, balance = 2_000_000_000_000) { - const keys = generateAuthorityKeys(seed); - addAuraAuthority(patch, keys.aura); - addGrandpaAuthority(patch, keys.grandpa); - addBalance(patch, keys.account, balance); -} - -// --------------------------------------------------------------------------- -// Key insertion -// --------------------------------------------------------------------------- - -/** - * Insert Aura (sr25519) and GRANDPA (ed25519) keys into a node's keystore. - * Required for authority nodes that don't have a built-in substrate CLI shortcut. - */ -export const insertKeys = ( - binaryPath: string, - basePath: string, - chainSpec: string, - seed: string, -) => { - const run = (scheme: string, keyType: string) => { - execFileSync(binaryPath, [ - "key", - "insert", - "--base-path", - basePath, - "--chain", - chainSpec, - "--suri", - seed, - "--scheme", - scheme, - "--key-type", - keyType, - ]); - }; - run("sr25519", "aura"); - run("ed25519", "gran"); - log(`Inserted aura+grandpa keys for ${seed} into ${basePath}`); -}; diff --git a/e2e/shared/client.ts b/e2e/shared/client.ts deleted file mode 100644 index 1dd76dd48e..0000000000 --- a/e2e/shared/client.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { createClient, type PolkadotClient, type TypedApi } from "polkadot-api"; -import { getWsProvider } from "polkadot-api/ws-provider"; -import { getPolkadotSigner, type PolkadotSigner } from "polkadot-api/signer"; -import { sr25519CreateDerive } from "@polkadot-labs/hdkd"; -import { - DEV_PHRASE, - entropyToMiniSecret, - mnemonicToEntropy, - ss58Address, -} from "@polkadot-labs/hdkd-helpers"; -import { subtensor } from "@polkadot-api/descriptors"; - -const SECOND = 1000; - -export type ClientConnection = { - client: PolkadotClient; - api: TypedApi<typeof subtensor>; -}; - -export const connectClient = async (rpcPort: number): Promise<ClientConnection> => { - const provider = getWsProvider(`ws://localhost:${rpcPort}`); - const client = createClient(provider); - const api = client.getTypedApi(subtensor); - return { client, api }; -}; - -export type Signer = { - signer: PolkadotSigner; - address: string; -}; - -export const createSigner = (uri: string): Signer => { - const entropy = mnemonicToEntropy(DEV_PHRASE); - const miniSecret = entropyToMiniSecret(entropy); - const derive = sr25519CreateDerive(miniSecret); - const keypair = derive(uri); - return { - signer: getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign), - address: ss58Address(keypair.publicKey), - }; -}; - -export const getAccountNonce = async ( - api: TypedApi<typeof subtensor>, - address: string, -): Promise<number> => { - const account = await api.query.System.Account.getValue(address, { at: "best" }); - return account.nonce; -}; - -export const getBalance = async ( - api: TypedApi<typeof subtensor>, - address: string, -): Promise<bigint> => { - const account = await api.query.System.Account.getValue(address); - return account.data.free; -}; - -export const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)); - -/** Polls the chain until `count` new finalized blocks have been produced. */ -export async function waitForFinalizedBlocks( - client: PolkadotClient, - count: number, - pollInterval = 1 * SECOND, - timeout = 120 * SECOND, -): Promise<void> { - const startBlock = await client.getFinalizedBlock(); - const start = startBlock.number; - const target = start + count; - const deadline = Date.now() + timeout; - - while (Date.now() < deadline) { - await sleep(pollInterval); - const block = await client.getFinalizedBlock(); - if (block.number >= target) return; - } - - throw new Error( - `Timed out waiting for ${count} finalized blocks (from #${start}, target #${target})`, - ); -} diff --git a/e2e/shared/devnet-client.ts b/e2e/shared/devnet-client.ts deleted file mode 100644 index 776472fb5e..0000000000 --- a/e2e/shared/devnet-client.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { subtensor } from "@polkadot-api/descriptors"; -import { TypedApi, PolkadotClient, createClient } from "polkadot-api"; -import { getWsProvider } from "polkadot-api/ws-provider/web"; - -export const SUB_LOCAL_URL = "ws://localhost:9944"; - -let client: PolkadotClient | undefined = undefined; -let api: TypedApi<typeof subtensor> | undefined = undefined; - -export async function getClient(): Promise<PolkadotClient> { - if (client === undefined) { - const provider = getWsProvider(SUB_LOCAL_URL); - client = createClient(provider); - } - return client; -} - -export async function getDevnetApi(): Promise<TypedApi<typeof subtensor>> { - if (api === undefined) { - const c = await getClient(); - api = c.getTypedApi(subtensor); - } - return api; -} - -export function destroyClient(): void { - client?.destroy(); - client = undefined; - api = undefined; -} diff --git a/e2e/shared/index.ts b/e2e/shared/index.ts deleted file mode 100644 index 1e686b816d..0000000000 --- a/e2e/shared/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Node management -export { - startNode, - stop, - started, - peerCount, - finalizedBlocks, - innerEnsure, - log as nodeLog, - type NodeOptions, - type Node, -} from "./node.js"; -export * from "./chainspec.js"; -export * from "./sequencer.js"; - -// Client utilities (shield-style) -export { - connectClient, - createSigner, - getAccountNonce, - getBalance as getBalanceByAddress, - sleep, - waitForFinalizedBlocks, - type ClientConnection, - type Signer, -} from "./client.js"; - -// Blockchain API utilities (staking-tests style) -export * from "./logger.js"; -export * from "./devnet-client.js"; -export * from "./address.js"; -export * from "./transactions.js"; -export * from "./balance.js"; -export * from "./subnet.js"; -export * from "./staking.js"; diff --git a/e2e/shared/logger.ts b/e2e/shared/logger.ts deleted file mode 100644 index 041443353a..0000000000 --- a/e2e/shared/logger.ts +++ /dev/null @@ -1,7 +0,0 @@ -const LOG_INDENT = " "; - -export const log = { - tx: (label: string, msg: string) => console.log(`${LOG_INDENT}[${label}] ${msg}`), - info: (msg: string) => console.log(`${LOG_INDENT}${msg}`), - error: (label: string, msg: string) => console.error(`${LOG_INDENT}[${label}] ${msg}`), -}; diff --git a/e2e/shared/node.ts b/e2e/shared/node.ts deleted file mode 100644 index 5e93e5007a..0000000000 --- a/e2e/shared/node.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { spawn, ChildProcess } from "node:child_process"; - -const SECOND = 1000; -const MINUTE = 60 * SECOND; - -// Substrate CLI shortcuts that inject keystore keys automatically. -const SUBSTRATE_SHORTCUTS = new Set([ - "alice", - "bob", - "charlie", - "dave", - "eve", - "ferdie", - "one", - "two", -]); - -export type NodeOptions = { - binaryPath: string; - basePath: string; - name: string; - port: number; - rpcPort: number; - validator: boolean; - chainSpec: string; -}; - -export type Node = { - name: string; - binaryPath: string; - rpcPort: number; - port: number; - process: ChildProcess; -}; - -export const log = (message: string) => console.log(`[${new Date().toISOString()}] ${message}`); - -export const startNode = (opts: NodeOptions): Node => { - const nameArgs = SUBSTRATE_SHORTCUTS.has(opts.name) ? [`--${opts.name}`] : ["--name", opts.name]; - - const child = spawn(opts.binaryPath, [ - ...nameArgs, - ...["--chain", opts.chainSpec], - ...["--base-path", opts.basePath], - ...["--port", opts.port.toString()], - ...["--rpc-port", opts.rpcPort.toString()], - ...(opts.validator ? ["--validator"] : []), - "--rpc-cors=all", - "--allow-private-ipv4", - "--discover-local", - "--unsafe-force-node-key-generation", - ]); - - let lastStderr = ""; - child.stderr?.on("data", (chunk: Buffer) => { - lastStderr = chunk.toString(); - }); - child.on("error", (error) => console.error(`${opts.name} (error): ${error}`)); - child.on("close", (code) => { - if (code !== 0 && code !== null) { - log(`${opts.name}: process crashed with code ${code}. Last stderr: ${lastStderr}`); - } else { - log(`${opts.name}: process closed with code ${code}`); - } - }); - - return { - name: opts.name, - binaryPath: opts.binaryPath, - rpcPort: opts.rpcPort, - port: opts.port, - process: child, - }; -}; - -export const stop = (node: Node): Promise<void> => { - return new Promise((resolve, reject) => { - node.process.on("close", () => resolve()); - node.process.on("error", reject); - - if (!node.process.kill()) { - reject(new Error(`Failed to stop ${node.name}`)); - } - }); -}; - -export const started = (node: Node, timeout = 60 * SECOND) => { - const errorMessage = `${node.name} failed to start in time`; - - return innerEnsure(node, errorMessage, timeout, (data, ok) => { - if (data.includes("💤 Idle")) { - log(`${node.name}: started using ${node.binaryPath}`); - ok(); - } - }); -}; - -export const peerCount = (node: Node, expectedPeers: number, timeout = 60 * SECOND) => { - const errorMessage = `${node.name} failed to reach ${expectedPeers} peers in time`; - - return innerEnsure(node, errorMessage, timeout, (data, ok) => { - const maybePeers = /Idle \((?<peers>\d+) peers\)/.exec(data)?.groups?.peers; - if (!maybePeers) return; - - const peers = parseInt(maybePeers); - if (peers >= expectedPeers) { - log(`${node.name}: reached ${expectedPeers} peers`); - ok(); - } - }); -}; - -export const finalizedBlocks = (node: Node, expectedFinalized: number, timeout = 10 * MINUTE) => { - const errorMessage = `${node.name} failed to reach ${expectedFinalized} finalized blocks in time`; - - return innerEnsure(node, errorMessage, timeout, (data, ok) => { - const maybeFinalized = /finalized #(?<blocks>\d+)/.exec(data)?.groups?.blocks; - if (!maybeFinalized) return; - - const finalized = parseInt(maybeFinalized); - if (finalized >= expectedFinalized) { - log(`${node.name}: reached ${expectedFinalized} finalized blocks`); - ok(); - } - }); -}; - -export function innerEnsure( - node: Node, - errorMessage: string, - timeout: number, - f: (data: string, ok: () => void) => void, -) { - return new Promise<void>((resolve, reject) => { - const id = setTimeout(() => reject(new Error(errorMessage)), timeout); - - const fn = (chunk: Buffer) => { - const data = chunk.toString(); - f(data, () => { - clearTimeout(id); - node.process.stderr?.off("data", fn); - resolve(); - }); - }; - - node.process.stderr?.on("data", fn); - }); -} diff --git a/e2e/shared/package.json b/e2e/shared/package.json deleted file mode 100644 index 6efbfae4e6..0000000000 --- a/e2e/shared/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "e2e-shared", - "version": "1.0.0", - "type": "module", - "exports": { - ".": "./index.ts", - "./node.js": "./node.ts", - "./chainspec.js": "./chainspec.ts", - "./sequencer.js": "./sequencer.ts", - "./client.js": "./client.ts", - "./logger.js": "./logger.ts", - "./devnet-client.js": "./devnet-client.ts", - "./address.js": "./address.ts", - "./transactions.js": "./transactions.ts", - "./balance.js": "./balance.ts", - "./subnet.js": "./subnet.ts", - "./staking.js": "./staking.ts" - }, - "dependencies": { - "@polkadot/keyring": "catalog:", - "@polkadot-api/descriptors": "file:../.papi/descriptors", - "@polkadot-labs/hdkd": "catalog:", - "@polkadot-labs/hdkd-helpers": "catalog:", - "polkadot-api": "catalog:" - }, - "devDependencies": { - "@types/node": "catalog:", - "vitest": "catalog:" - } -} diff --git a/e2e/shared/pnpm-lock.yaml b/e2e/shared/pnpm-lock.yaml deleted file mode 100644 index 767156e260..0000000000 --- a/e2e/shared/pnpm-lock.yaml +++ /dev/null @@ -1,1628 +0,0 @@ -lockfileVersion: "9.0" - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - .: - dependencies: - "@polkadot/keyring": - specifier: ^14.0.1 - version: 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) - devDependencies: - "@types/node": - specifier: ^24 - version: 24.10.13 - vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/node@24.10.13) - -packages: - "@esbuild/aix-ppc64@0.27.3": - resolution: - { - integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [aix] - - "@esbuild/android-arm64@0.27.3": - resolution: - { - integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [android] - - "@esbuild/android-arm@0.27.3": - resolution: - { - integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [android] - - "@esbuild/android-x64@0.27.3": - resolution: - { - integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [android] - - "@esbuild/darwin-arm64@0.27.3": - resolution: - { - integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [darwin] - - "@esbuild/darwin-x64@0.27.3": - resolution: - { - integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [darwin] - - "@esbuild/freebsd-arm64@0.27.3": - resolution: - { - integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [freebsd] - - "@esbuild/freebsd-x64@0.27.3": - resolution: - { - integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [freebsd] - - "@esbuild/linux-arm64@0.27.3": - resolution: - { - integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [linux] - - "@esbuild/linux-arm@0.27.3": - resolution: - { - integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [linux] - - "@esbuild/linux-ia32@0.27.3": - resolution: - { - integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [linux] - - "@esbuild/linux-loong64@0.27.3": - resolution: - { - integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==, - } - engines: { node: ">=18" } - cpu: [loong64] - os: [linux] - - "@esbuild/linux-mips64el@0.27.3": - resolution: - { - integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==, - } - engines: { node: ">=18" } - cpu: [mips64el] - os: [linux] - - "@esbuild/linux-ppc64@0.27.3": - resolution: - { - integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [linux] - - "@esbuild/linux-riscv64@0.27.3": - resolution: - { - integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==, - } - engines: { node: ">=18" } - cpu: [riscv64] - os: [linux] - - "@esbuild/linux-s390x@0.27.3": - resolution: - { - integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==, - } - engines: { node: ">=18" } - cpu: [s390x] - os: [linux] - - "@esbuild/linux-x64@0.27.3": - resolution: - { - integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [linux] - - "@esbuild/netbsd-arm64@0.27.3": - resolution: - { - integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [netbsd] - - "@esbuild/netbsd-x64@0.27.3": - resolution: - { - integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [netbsd] - - "@esbuild/openbsd-arm64@0.27.3": - resolution: - { - integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openbsd] - - "@esbuild/openbsd-x64@0.27.3": - resolution: - { - integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [openbsd] - - "@esbuild/openharmony-arm64@0.27.3": - resolution: - { - integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openharmony] - - "@esbuild/sunos-x64@0.27.3": - resolution: - { - integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [sunos] - - "@esbuild/win32-arm64@0.27.3": - resolution: - { - integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [win32] - - "@esbuild/win32-ia32@0.27.3": - resolution: - { - integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [win32] - - "@esbuild/win32-x64@0.27.3": - resolution: - { - integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [win32] - - "@jridgewell/sourcemap-codec@1.5.5": - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } - - "@noble/curves@1.9.7": - resolution: - { - integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==, - } - engines: { node: ^14.21.3 || >=16 } - - "@noble/hashes@1.8.0": - resolution: - { - integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==, - } - engines: { node: ^14.21.3 || >=16 } - - "@polkadot/keyring@14.0.1": - resolution: - { - integrity: sha512-kHydQPCeTvJrMC9VQO8LPhAhTUxzxfNF1HEknhZDBPPsxP/XpkYsEy/Ln1QzJmQqD5VsgwzLDE6cExbJ2CT9CA==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": 14.0.1 - "@polkadot/util-crypto": 14.0.1 - - "@polkadot/networks@14.0.1": - resolution: - { - integrity: sha512-wGlBtXDkusRAj4P7uxfPz80gLO1+j99MLBaQi3bEym2xrFrFhgIWVHOZlBit/1PfaBjhX2Z8XjRxaM2w1p7w2w==, - } - engines: { node: ">=18" } - - "@polkadot/util-crypto@14.0.1": - resolution: - { - integrity: sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": 14.0.1 - - "@polkadot/util@14.0.1": - resolution: - { - integrity: sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg==, - } - engines: { node: ">=18" } - - "@polkadot/wasm-bridge@7.5.4": - resolution: - { - integrity: sha512-6xaJVvoZbnbgpQYXNw9OHVNWjXmtcoPcWh7hlwx3NpfiLkkjljj99YS+XGZQlq7ks2fVCg7FbfknkNb8PldDaA==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - "@polkadot/x-randomvalues": "*" - - "@polkadot/wasm-crypto-asmjs@7.5.4": - resolution: - { - integrity: sha512-ZYwxQHAJ8pPt6kYk9XFmyuFuSS+yirJLonvP+DYbxOrARRUHfN4nzp4zcZNXUuaFhpbDobDSFn6gYzye6BUotA==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - - "@polkadot/wasm-crypto-init@7.5.4": - resolution: - { - integrity: sha512-U6s4Eo2rHs2n1iR01vTz/sOQ7eOnRPjaCsGWhPV+ZC/20hkVzwPAhiizu/IqMEol4tO2yiSheD4D6bn0KxUJhg==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - "@polkadot/x-randomvalues": "*" - - "@polkadot/wasm-crypto-wasm@7.5.4": - resolution: - { - integrity: sha512-PsHgLsVTu43eprwSvUGnxybtOEuHPES6AbApcs7y5ZbM2PiDMzYbAjNul098xJK/CPtrxZ0ePDFnaQBmIJyTFw==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - - "@polkadot/wasm-crypto@7.5.4": - resolution: - { - integrity: sha512-1seyClxa7Jd7kQjfnCzTTTfYhTa/KUTDUaD3DMHBk5Q4ZUN1D1unJgX+v1aUeXSPxmzocdZETPJJRZjhVOqg9g==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - "@polkadot/x-randomvalues": "*" - - "@polkadot/wasm-util@7.5.4": - resolution: - { - integrity: sha512-hqPpfhCpRAqCIn/CYbBluhh0TXmwkJnDRjxrU9Bnqtw9nMNa97D8JuOjdd2pi0rxm+eeLQ/f1rQMp71RMM9t4w==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": "*" - - "@polkadot/x-bigint@14.0.1": - resolution: - { - integrity: sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ==, - } - engines: { node: ">=18" } - - "@polkadot/x-global@14.0.1": - resolution: - { - integrity: sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA==, - } - engines: { node: ">=18" } - - "@polkadot/x-randomvalues@14.0.1": - resolution: - { - integrity: sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg==, - } - engines: { node: ">=18" } - peerDependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": "*" - - "@polkadot/x-textdecoder@14.0.1": - resolution: - { - integrity: sha512-CcWiPCuPVJsNk4Vq43lgFHqLRBQHb4r9RD7ZIYgmwoebES8TNm4g2ew9ToCzakFKSpzKu6I07Ne9wv/dt5zLuw==, - } - engines: { node: ">=18" } - - "@polkadot/x-textencoder@14.0.1": - resolution: - { - integrity: sha512-VY51SpQmF1ccmAGLfxhYnAe95Spfz049WZ/+kK4NfsGF9WejxVdU53Im5C80l45r8qHuYQsCWU3+t0FNunh2Kg==, - } - engines: { node: ">=18" } - - "@rollup/rollup-android-arm-eabi@4.57.1": - resolution: - { - integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==, - } - cpu: [arm] - os: [android] - - "@rollup/rollup-android-arm64@4.57.1": - resolution: - { - integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==, - } - cpu: [arm64] - os: [android] - - "@rollup/rollup-darwin-arm64@4.57.1": - resolution: - { - integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==, - } - cpu: [arm64] - os: [darwin] - - "@rollup/rollup-darwin-x64@4.57.1": - resolution: - { - integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==, - } - cpu: [x64] - os: [darwin] - - "@rollup/rollup-freebsd-arm64@4.57.1": - resolution: - { - integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==, - } - cpu: [arm64] - os: [freebsd] - - "@rollup/rollup-freebsd-x64@4.57.1": - resolution: - { - integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==, - } - cpu: [x64] - os: [freebsd] - - "@rollup/rollup-linux-arm-gnueabihf@4.57.1": - resolution: - { - integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==, - } - cpu: [arm] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-arm-musleabihf@4.57.1": - resolution: - { - integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==, - } - cpu: [arm] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-arm64-gnu@4.57.1": - resolution: - { - integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==, - } - cpu: [arm64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-arm64-musl@4.57.1": - resolution: - { - integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==, - } - cpu: [arm64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-loong64-gnu@4.57.1": - resolution: - { - integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==, - } - cpu: [loong64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-loong64-musl@4.57.1": - resolution: - { - integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==, - } - cpu: [loong64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-ppc64-gnu@4.57.1": - resolution: - { - integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==, - } - cpu: [ppc64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-ppc64-musl@4.57.1": - resolution: - { - integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==, - } - cpu: [ppc64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-riscv64-gnu@4.57.1": - resolution: - { - integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==, - } - cpu: [riscv64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-riscv64-musl@4.57.1": - resolution: - { - integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==, - } - cpu: [riscv64] - os: [linux] - libc: [musl] - - "@rollup/rollup-linux-s390x-gnu@4.57.1": - resolution: - { - integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==, - } - cpu: [s390x] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-x64-gnu@4.57.1": - resolution: - { - integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==, - } - cpu: [x64] - os: [linux] - libc: [glibc] - - "@rollup/rollup-linux-x64-musl@4.57.1": - resolution: - { - integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==, - } - cpu: [x64] - os: [linux] - libc: [musl] - - "@rollup/rollup-openbsd-x64@4.57.1": - resolution: - { - integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==, - } - cpu: [x64] - os: [openbsd] - - "@rollup/rollup-openharmony-arm64@4.57.1": - resolution: - { - integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==, - } - cpu: [arm64] - os: [openharmony] - - "@rollup/rollup-win32-arm64-msvc@4.57.1": - resolution: - { - integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==, - } - cpu: [arm64] - os: [win32] - - "@rollup/rollup-win32-ia32-msvc@4.57.1": - resolution: - { - integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==, - } - cpu: [ia32] - os: [win32] - - "@rollup/rollup-win32-x64-gnu@4.57.1": - resolution: - { - integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==, - } - cpu: [x64] - os: [win32] - - "@rollup/rollup-win32-x64-msvc@4.57.1": - resolution: - { - integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==, - } - cpu: [x64] - os: [win32] - - "@scure/base@1.2.6": - resolution: - { - integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==, - } - - "@scure/sr25519@0.2.0": - resolution: - { - integrity: sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg==, - } - - "@substrate/ss58-registry@1.51.0": - resolution: - { - integrity: sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==, - } - - "@types/bn.js@5.2.0": - resolution: - { - integrity: sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==, - } - - "@types/chai@5.2.3": - resolution: - { - integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, - } - - "@types/deep-eql@4.0.2": - resolution: - { - integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, - } - - "@types/estree@1.0.8": - resolution: - { - integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, - } - - "@types/node@24.10.13": - resolution: - { - integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==, - } - - "@vitest/expect@3.2.4": - resolution: - { - integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==, - } - - "@vitest/mocker@3.2.4": - resolution: - { - integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==, - } - peerDependencies: - msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - "@vitest/pretty-format@3.2.4": - resolution: - { - integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==, - } - - "@vitest/runner@3.2.4": - resolution: - { - integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==, - } - - "@vitest/snapshot@3.2.4": - resolution: - { - integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==, - } - - "@vitest/spy@3.2.4": - resolution: - { - integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==, - } - - "@vitest/utils@3.2.4": - resolution: - { - integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==, - } - - assertion-error@2.0.1: - resolution: - { - integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, - } - engines: { node: ">=12" } - - bn.js@5.2.2: - resolution: - { - integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==, - } - - cac@6.7.14: - resolution: - { - integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, - } - engines: { node: ">=8" } - - chai@5.3.3: - resolution: - { - integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==, - } - engines: { node: ">=18" } - - check-error@2.1.3: - resolution: - { - integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==, - } - engines: { node: ">= 16" } - - debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: ">=6.0" } - peerDependencies: - supports-color: "*" - peerDependenciesMeta: - supports-color: - optional: true - - deep-eql@5.0.2: - resolution: - { - integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==, - } - engines: { node: ">=6" } - - es-module-lexer@1.7.0: - resolution: - { - integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, - } - - esbuild@0.27.3: - resolution: - { - integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==, - } - engines: { node: ">=18" } - hasBin: true - - estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } - - expect-type@1.3.0: - resolution: - { - integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==, - } - engines: { node: ">=12.0.0" } - - fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: ">=12.0.0" } - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } - os: [darwin] - - js-tokens@9.0.1: - resolution: - { - integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==, - } - - loupe@3.2.1: - resolution: - { - integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==, - } - - magic-string@0.30.21: - resolution: - { - integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, - } - - ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } - - nanoid@3.3.11: - resolution: - { - integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } - hasBin: true - - pathe@2.0.3: - resolution: - { - integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, - } - - pathval@2.0.1: - resolution: - { - integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==, - } - engines: { node: ">= 14.16" } - - picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } - - picomatch@4.0.3: - resolution: - { - integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, - } - engines: { node: ">=12" } - - postcss@8.5.6: - resolution: - { - integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, - } - engines: { node: ^10 || ^12 || >=14 } - - rollup@4.57.1: - resolution: - { - integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==, - } - engines: { node: ">=18.0.0", npm: ">=8.0.0" } - hasBin: true - - siginfo@2.0.0: - resolution: - { - integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, - } - - source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: ">=0.10.0" } - - stackback@0.0.2: - resolution: - { - integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, - } - - std-env@3.10.0: - resolution: - { - integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==, - } - - strip-literal@3.1.0: - resolution: - { - integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==, - } - - tinybench@2.9.0: - resolution: - { - integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, - } - - tinyexec@0.3.2: - resolution: - { - integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==, - } - - tinyglobby@0.2.15: - resolution: - { - integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, - } - engines: { node: ">=12.0.0" } - - tinypool@1.1.1: - resolution: - { - integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==, - } - engines: { node: ^18.0.0 || >=20.0.0 } - - tinyrainbow@2.0.0: - resolution: - { - integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==, - } - engines: { node: ">=14.0.0" } - - tinyspy@4.0.4: - resolution: - { - integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==, - } - engines: { node: ">=14.0.0" } - - tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } - - undici-types@7.16.0: - resolution: - { - integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==, - } - - vite-node@3.2.4: - resolution: - { - integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==, - } - engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } - hasBin: true - - vite@7.3.1: - resolution: - { - integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - hasBin: true - peerDependencies: - "@types/node": ^20.19.0 || >=22.12.0 - jiti: ">=1.21.0" - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: ">=0.54.8" - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - "@types/node": - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@3.2.4: - resolution: - { - integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==, - } - engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } - hasBin: true - peerDependencies: - "@edge-runtime/vm": "*" - "@types/debug": ^4.1.12 - "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 - "@vitest/browser": 3.2.4 - "@vitest/ui": 3.2.4 - happy-dom: "*" - jsdom: "*" - peerDependenciesMeta: - "@edge-runtime/vm": - optional: true - "@types/debug": - optional: true - "@types/node": - optional: true - "@vitest/browser": - optional: true - "@vitest/ui": - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - why-is-node-running@2.3.0: - resolution: - { - integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, - } - engines: { node: ">=8" } - hasBin: true - -snapshots: - "@esbuild/aix-ppc64@0.27.3": - optional: true - - "@esbuild/android-arm64@0.27.3": - optional: true - - "@esbuild/android-arm@0.27.3": - optional: true - - "@esbuild/android-x64@0.27.3": - optional: true - - "@esbuild/darwin-arm64@0.27.3": - optional: true - - "@esbuild/darwin-x64@0.27.3": - optional: true - - "@esbuild/freebsd-arm64@0.27.3": - optional: true - - "@esbuild/freebsd-x64@0.27.3": - optional: true - - "@esbuild/linux-arm64@0.27.3": - optional: true - - "@esbuild/linux-arm@0.27.3": - optional: true - - "@esbuild/linux-ia32@0.27.3": - optional: true - - "@esbuild/linux-loong64@0.27.3": - optional: true - - "@esbuild/linux-mips64el@0.27.3": - optional: true - - "@esbuild/linux-ppc64@0.27.3": - optional: true - - "@esbuild/linux-riscv64@0.27.3": - optional: true - - "@esbuild/linux-s390x@0.27.3": - optional: true - - "@esbuild/linux-x64@0.27.3": - optional: true - - "@esbuild/netbsd-arm64@0.27.3": - optional: true - - "@esbuild/netbsd-x64@0.27.3": - optional: true - - "@esbuild/openbsd-arm64@0.27.3": - optional: true - - "@esbuild/openbsd-x64@0.27.3": - optional: true - - "@esbuild/openharmony-arm64@0.27.3": - optional: true - - "@esbuild/sunos-x64@0.27.3": - optional: true - - "@esbuild/win32-arm64@0.27.3": - optional: true - - "@esbuild/win32-ia32@0.27.3": - optional: true - - "@esbuild/win32-x64@0.27.3": - optional: true - - "@jridgewell/sourcemap-codec@1.5.5": {} - - "@noble/curves@1.9.7": - dependencies: - "@noble/hashes": 1.8.0 - - "@noble/hashes@1.8.0": {} - - "@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/util-crypto": 14.0.1(@polkadot/util@14.0.1) - tslib: 2.8.1 - - "@polkadot/networks@14.0.1": - dependencies: - "@polkadot/util": 14.0.1 - "@substrate/ss58-registry": 1.51.0 - tslib: 2.8.1 - - "@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1)": - dependencies: - "@noble/curves": 1.9.7 - "@noble/hashes": 1.8.0 - "@polkadot/networks": 14.0.1 - "@polkadot/util": 14.0.1 - "@polkadot/wasm-crypto": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-bigint": 14.0.1 - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - "@scure/base": 1.2.6 - "@scure/sr25519": 0.2.0 - tslib: 2.8.1 - - "@polkadot/util@14.0.1": - dependencies: - "@polkadot/x-bigint": 14.0.1 - "@polkadot/x-global": 14.0.1 - "@polkadot/x-textdecoder": 14.0.1 - "@polkadot/x-textencoder": 14.0.1 - "@types/bn.js": 5.2.0 - bn.js: 5.2.2 - tslib: 2.8.1 - - "@polkadot/wasm-bridge@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - tslib: 2.8.1 - - "@polkadot/wasm-crypto-asmjs@7.5.4(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - tslib: 2.8.1 - - "@polkadot/wasm-crypto-init@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-bridge": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-crypto-asmjs": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-crypto-wasm": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - tslib: 2.8.1 - - "@polkadot/wasm-crypto-wasm@7.5.4(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - tslib: 2.8.1 - - "@polkadot/wasm-crypto@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-bridge": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-crypto-asmjs": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-crypto-init": 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) - "@polkadot/wasm-crypto-wasm": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-randomvalues": 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) - tslib: 2.8.1 - - "@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)": - dependencies: - "@polkadot/util": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-bigint@14.0.1": - dependencies: - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-global@14.0.1": - dependencies: - tslib: 2.8.1 - - "@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))": - dependencies: - "@polkadot/util": 14.0.1 - "@polkadot/wasm-util": 7.5.4(@polkadot/util@14.0.1) - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-textdecoder@14.0.1": - dependencies: - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@polkadot/x-textencoder@14.0.1": - dependencies: - "@polkadot/x-global": 14.0.1 - tslib: 2.8.1 - - "@rollup/rollup-android-arm-eabi@4.57.1": - optional: true - - "@rollup/rollup-android-arm64@4.57.1": - optional: true - - "@rollup/rollup-darwin-arm64@4.57.1": - optional: true - - "@rollup/rollup-darwin-x64@4.57.1": - optional: true - - "@rollup/rollup-freebsd-arm64@4.57.1": - optional: true - - "@rollup/rollup-freebsd-x64@4.57.1": - optional: true - - "@rollup/rollup-linux-arm-gnueabihf@4.57.1": - optional: true - - "@rollup/rollup-linux-arm-musleabihf@4.57.1": - optional: true - - "@rollup/rollup-linux-arm64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-arm64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-loong64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-loong64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-ppc64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-ppc64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-riscv64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-riscv64-musl@4.57.1": - optional: true - - "@rollup/rollup-linux-s390x-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-x64-gnu@4.57.1": - optional: true - - "@rollup/rollup-linux-x64-musl@4.57.1": - optional: true - - "@rollup/rollup-openbsd-x64@4.57.1": - optional: true - - "@rollup/rollup-openharmony-arm64@4.57.1": - optional: true - - "@rollup/rollup-win32-arm64-msvc@4.57.1": - optional: true - - "@rollup/rollup-win32-ia32-msvc@4.57.1": - optional: true - - "@rollup/rollup-win32-x64-gnu@4.57.1": - optional: true - - "@rollup/rollup-win32-x64-msvc@4.57.1": - optional: true - - "@scure/base@1.2.6": {} - - "@scure/sr25519@0.2.0": - dependencies: - "@noble/curves": 1.9.7 - "@noble/hashes": 1.8.0 - - "@substrate/ss58-registry@1.51.0": {} - - "@types/bn.js@5.2.0": - dependencies: - "@types/node": 24.10.13 - - "@types/chai@5.2.3": - dependencies: - "@types/deep-eql": 4.0.2 - assertion-error: 2.0.1 - - "@types/deep-eql@4.0.2": {} - - "@types/estree@1.0.8": {} - - "@types/node@24.10.13": - dependencies: - undici-types: 7.16.0 - - "@vitest/expect@3.2.4": - dependencies: - "@types/chai": 5.2.3 - "@vitest/spy": 3.2.4 - "@vitest/utils": 3.2.4 - chai: 5.3.3 - tinyrainbow: 2.0.0 - - "@vitest/mocker@3.2.4(vite@7.3.1(@types/node@24.10.13))": - dependencies: - "@vitest/spy": 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@24.10.13) - - "@vitest/pretty-format@3.2.4": - dependencies: - tinyrainbow: 2.0.0 - - "@vitest/runner@3.2.4": - dependencies: - "@vitest/utils": 3.2.4 - pathe: 2.0.3 - strip-literal: 3.1.0 - - "@vitest/snapshot@3.2.4": - dependencies: - "@vitest/pretty-format": 3.2.4 - magic-string: 0.30.21 - pathe: 2.0.3 - - "@vitest/spy@3.2.4": - dependencies: - tinyspy: 4.0.4 - - "@vitest/utils@3.2.4": - dependencies: - "@vitest/pretty-format": 3.2.4 - loupe: 3.2.1 - tinyrainbow: 2.0.0 - - assertion-error@2.0.1: {} - - bn.js@5.2.2: {} - - cac@6.7.14: {} - - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.3 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 - - check-error@2.1.3: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - deep-eql@5.0.2: {} - - es-module-lexer@1.7.0: {} - - esbuild@0.27.3: - optionalDependencies: - "@esbuild/aix-ppc64": 0.27.3 - "@esbuild/android-arm": 0.27.3 - "@esbuild/android-arm64": 0.27.3 - "@esbuild/android-x64": 0.27.3 - "@esbuild/darwin-arm64": 0.27.3 - "@esbuild/darwin-x64": 0.27.3 - "@esbuild/freebsd-arm64": 0.27.3 - "@esbuild/freebsd-x64": 0.27.3 - "@esbuild/linux-arm": 0.27.3 - "@esbuild/linux-arm64": 0.27.3 - "@esbuild/linux-ia32": 0.27.3 - "@esbuild/linux-loong64": 0.27.3 - "@esbuild/linux-mips64el": 0.27.3 - "@esbuild/linux-ppc64": 0.27.3 - "@esbuild/linux-riscv64": 0.27.3 - "@esbuild/linux-s390x": 0.27.3 - "@esbuild/linux-x64": 0.27.3 - "@esbuild/netbsd-arm64": 0.27.3 - "@esbuild/netbsd-x64": 0.27.3 - "@esbuild/openbsd-arm64": 0.27.3 - "@esbuild/openbsd-x64": 0.27.3 - "@esbuild/openharmony-arm64": 0.27.3 - "@esbuild/sunos-x64": 0.27.3 - "@esbuild/win32-arm64": 0.27.3 - "@esbuild/win32-ia32": 0.27.3 - "@esbuild/win32-x64": 0.27.3 - - estree-walker@3.0.3: - dependencies: - "@types/estree": 1.0.8 - - expect-type@1.3.0: {} - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - fsevents@2.3.3: - optional: true - - js-tokens@9.0.1: {} - - loupe@3.2.1: {} - - magic-string@0.30.21: - dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 - - ms@2.1.3: {} - - nanoid@3.3.11: {} - - pathe@2.0.3: {} - - pathval@2.0.1: {} - - picocolors@1.1.1: {} - - picomatch@4.0.3: {} - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - rollup@4.57.1: - dependencies: - "@types/estree": 1.0.8 - optionalDependencies: - "@rollup/rollup-android-arm-eabi": 4.57.1 - "@rollup/rollup-android-arm64": 4.57.1 - "@rollup/rollup-darwin-arm64": 4.57.1 - "@rollup/rollup-darwin-x64": 4.57.1 - "@rollup/rollup-freebsd-arm64": 4.57.1 - "@rollup/rollup-freebsd-x64": 4.57.1 - "@rollup/rollup-linux-arm-gnueabihf": 4.57.1 - "@rollup/rollup-linux-arm-musleabihf": 4.57.1 - "@rollup/rollup-linux-arm64-gnu": 4.57.1 - "@rollup/rollup-linux-arm64-musl": 4.57.1 - "@rollup/rollup-linux-loong64-gnu": 4.57.1 - "@rollup/rollup-linux-loong64-musl": 4.57.1 - "@rollup/rollup-linux-ppc64-gnu": 4.57.1 - "@rollup/rollup-linux-ppc64-musl": 4.57.1 - "@rollup/rollup-linux-riscv64-gnu": 4.57.1 - "@rollup/rollup-linux-riscv64-musl": 4.57.1 - "@rollup/rollup-linux-s390x-gnu": 4.57.1 - "@rollup/rollup-linux-x64-gnu": 4.57.1 - "@rollup/rollup-linux-x64-musl": 4.57.1 - "@rollup/rollup-openbsd-x64": 4.57.1 - "@rollup/rollup-openharmony-arm64": 4.57.1 - "@rollup/rollup-win32-arm64-msvc": 4.57.1 - "@rollup/rollup-win32-ia32-msvc": 4.57.1 - "@rollup/rollup-win32-x64-gnu": 4.57.1 - "@rollup/rollup-win32-x64-msvc": 4.57.1 - fsevents: 2.3.3 - - siginfo@2.0.0: {} - - source-map-js@1.2.1: {} - - stackback@0.0.2: {} - - std-env@3.10.0: {} - - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - - tinybench@2.9.0: {} - - tinyexec@0.3.2: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyspy@4.0.4: {} - - tslib@2.8.1: {} - - undici-types@7.16.0: {} - - vite-node@3.2.4(@types/node@24.10.13): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@24.10.13) - transitivePeerDependencies: - - "@types/node" - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite@7.3.1(@types/node@24.10.13): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.57.1 - tinyglobby: 0.2.15 - optionalDependencies: - "@types/node": 24.10.13 - fsevents: 2.3.3 - - vitest@3.2.4(@types/node@24.10.13): - dependencies: - "@types/chai": 5.2.3 - "@vitest/expect": 3.2.4 - "@vitest/mocker": 3.2.4(vite@7.3.1(@types/node@24.10.13)) - "@vitest/pretty-format": 3.2.4 - "@vitest/runner": 3.2.4 - "@vitest/snapshot": 3.2.4 - "@vitest/spy": 3.2.4 - "@vitest/utils": 3.2.4 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@24.10.13) - vite-node: 3.2.4(@types/node@24.10.13) - why-is-node-running: 2.3.0 - optionalDependencies: - "@types/node": 24.10.13 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 diff --git a/e2e/shared/sequencer.ts b/e2e/shared/sequencer.ts deleted file mode 100644 index a87490d89b..0000000000 --- a/e2e/shared/sequencer.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { BaseSequencer } from "vitest/node"; -import type { TestSpecification } from "vitest/node"; - -/** - * Sorts test files alphabetically by their module path. - * - * Vitest's default sequencer orders files by cached duration/failure history, - * which does not respect numeric prefixes (00-, 01-, 02-, ...). This sequencer - * ensures files always run in the order they are named, which matters for - * multi-file suites where later files depend on state set up by earlier ones - * (e.g. a scaling test that adds nodes for subsequent edge-case tests). - */ -export default class AlphabeticalSequencer extends BaseSequencer { - async shard(files: TestSpecification[]): Promise<TestSpecification[]> { - return super.shard(files); - } - - async sort(files: TestSpecification[]): Promise<TestSpecification[]> { - return files.sort((a, b) => a.moduleId.localeCompare(b.moduleId)); - } -} diff --git a/e2e/shared/staking.ts b/e2e/shared/staking.ts deleted file mode 100644 index 96241f64e9..0000000000 --- a/e2e/shared/staking.ts +++ /dev/null @@ -1,508 +0,0 @@ -import { subtensor } from "@polkadot-api/descriptors"; -import { TypedApi } from "polkadot-api"; -import { KeyPair } from "@polkadot-labs/hdkd-helpers"; -import { getSignerFromKeypair, getAliceSigner } from "./address.js"; -import { waitForTransactionWithRetry } from "./transactions.js"; - -// U64F64 is a 128-bit fixed-point type with 64 fractional bits. -// Raw storage values must be divided by 2^64 to get the actual value. -const U64F64_FRACTIONAL_BITS = 64n; -const U64F64_MULTIPLIER = 1n << U64F64_FRACTIONAL_BITS; // 2^64 - -/** - * Convert a raw U64F64 storage value to its integer part (truncated). - */ -export function u64f64ToInt(raw: bigint): bigint { - return raw >> U64F64_FRACTIONAL_BITS; -} - -/** - * Convert an integer to U64F64 raw format for use in extrinsics. - */ -export function intToU64f64(value: bigint): bigint { - return value << U64F64_FRACTIONAL_BITS; -} - -/** - * Convert a raw U64F64 storage value to a decimal number for display. - */ -export function u64f64ToNumber(raw: bigint): number { - return Number(raw) / Number(U64F64_MULTIPLIER); -} - -export async function addStake( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, - netuid: number, - amount: bigint, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.add_stake({ - hotkey: hotkey, - netuid: netuid, - amount_staked: amount, - }); - await waitForTransactionWithRetry(api, tx, signer, "add_stake"); -} - -export async function addStakeLimit( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, - netuid: number, - amount: bigint, - limitPrice: bigint, - allowPartial: boolean, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.add_stake_limit({ - hotkey: hotkey, - netuid: netuid, - amount_staked: amount, - limit_price: limitPrice, - allow_partial: allowPartial, - }); - await waitForTransactionWithRetry(api, tx, signer, "add_stake_limit"); -} - -export async function removeStake( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, - netuid: number, - amount: bigint, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.remove_stake({ - hotkey: hotkey, - netuid: netuid, - amount_unstaked: amount, - }); - await waitForTransactionWithRetry(api, tx, signer, "remove_stake"); -} - -export async function removeStakeLimit( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, - netuid: number, - amount: bigint, - limitPrice: bigint, - allowPartial: boolean, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.remove_stake_limit({ - hotkey: hotkey, - netuid: netuid, - amount_unstaked: amount, - limit_price: limitPrice, - allow_partial: allowPartial, - }); - await waitForTransactionWithRetry(api, tx, signer, "remove_stake_limit"); -} - -export async function removeStakeFullLimit( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, - netuid: number, - limitPrice: bigint | undefined, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.remove_stake_full_limit({ - hotkey: hotkey, - netuid: netuid, - limit_price: limitPrice, - }); - await waitForTransactionWithRetry(api, tx, signer, "remove_stake_full_limit"); -} - -export async function unstakeAll( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.unstake_all({ - hotkey: hotkey, - }); - await waitForTransactionWithRetry(api, tx, signer, "unstake_all"); -} - -export async function unstakeAllAlpha( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.unstake_all_alpha({ - hotkey: hotkey, - }); - await waitForTransactionWithRetry(api, tx, signer, "unstake_all_alpha"); -} - -/** - * Get stake shares (Alpha) for a hotkey/coldkey/netuid triplet. - * Returns the integer part of the U64F64 value. - */ -export async function getStake( - api: TypedApi<typeof subtensor>, - hotkey: string, - coldkey: string, - netuid: number, -): Promise<bigint> { - const raw = await api.query.SubtensorModule.Alpha.getValue(hotkey, coldkey, netuid); - return u64f64ToInt(raw); -} - -/** - * Get raw stake shares (Alpha) in U64F64 format. - * Use this when you need the raw value for extrinsics like transfer_stake. - */ -export async function getStakeRaw( - api: TypedApi<typeof subtensor>, - hotkey: string, - coldkey: string, - netuid: number, -): Promise<bigint> { - return await api.query.SubtensorModule.Alpha.getValue(hotkey, coldkey, netuid); -} - -export async function transferStake( - api: TypedApi<typeof subtensor>, - originColdkey: KeyPair, - destinationColdkey: string, - hotkey: string, - originNetuid: number, - destinationNetuid: number, - amount: bigint, -): Promise<void> { - const signer = getSignerFromKeypair(originColdkey); - const tx = api.tx.SubtensorModule.transfer_stake({ - destination_coldkey: destinationColdkey, - hotkey: hotkey, - origin_netuid: originNetuid, - destination_netuid: destinationNetuid, - alpha_amount: amount, - }); - await waitForTransactionWithRetry(api, tx, signer, "transfer_stake"); -} - -export async function moveStake( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - originHotkey: string, - destinationHotkey: string, - originNetuid: number, - destinationNetuid: number, - amount: bigint, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.move_stake({ - origin_hotkey: originHotkey, - destination_hotkey: destinationHotkey, - origin_netuid: originNetuid, - destination_netuid: destinationNetuid, - alpha_amount: amount, - }); - await waitForTransactionWithRetry(api, tx, signer, "move_stake"); -} - -export async function swapStake( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, - originNetuid: number, - destinationNetuid: number, - amount: bigint, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.swap_stake({ - hotkey: hotkey, - origin_netuid: originNetuid, - destination_netuid: destinationNetuid, - alpha_amount: amount, - }); - await waitForTransactionWithRetry(api, tx, signer, "swap_stake"); -} - -export async function swapStakeLimit( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - hotkey: string, - originNetuid: number, - destinationNetuid: number, - amount: bigint, - limitPrice: bigint, - allowPartial: boolean, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.swap_stake_limit({ - hotkey: hotkey, - origin_netuid: originNetuid, - destination_netuid: destinationNetuid, - alpha_amount: amount, - limit_price: limitPrice, - allow_partial: allowPartial, - }); - await waitForTransactionWithRetry(api, tx, signer, "swap_stake_limit"); -} - -export type RootClaimType = "Swap" | "Keep" | { type: "KeepSubnets"; subnets: number[] }; - -export async function getRootClaimType( - api: TypedApi<typeof subtensor>, - coldkey: string, -): Promise<RootClaimType> { - const result = await api.query.SubtensorModule.RootClaimType.getValue(coldkey); - if (result.type === "KeepSubnets") { - return { type: "KeepSubnets", subnets: result.value.subnets as number[] }; - } - return result.type as "Swap" | "Keep"; -} - -export async function setRootClaimType( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - claimType: RootClaimType, -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - let newRootClaimType; - if (typeof claimType === "string") { - newRootClaimType = { type: claimType, value: undefined }; - } else { - newRootClaimType = { type: "KeepSubnets", value: { subnets: claimType.subnets } }; - } - const tx = api.tx.SubtensorModule.set_root_claim_type({ - new_root_claim_type: newRootClaimType, - }); - await waitForTransactionWithRetry(api, tx, signer, "set_root_claim_type"); -} - -export async function claimRoot( - api: TypedApi<typeof subtensor>, - coldkey: KeyPair, - subnets: number[], -): Promise<void> { - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.claim_root({ - subnets: subnets, - }); - await waitForTransactionWithRetry(api, tx, signer, "claim_root"); -} - -export async function getNumRootClaims(api: TypedApi<typeof subtensor>): Promise<bigint> { - return await api.query.SubtensorModule.NumRootClaim.getValue(); -} - -export async function sudoSetNumRootClaims( - api: TypedApi<typeof subtensor>, - newValue: bigint, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.SubtensorModule.sudo_set_num_root_claims({ - new_value: newValue, - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_num_root_claims"); -} - -export async function getRootClaimThreshold( - api: TypedApi<typeof subtensor>, - netuid: number, -): Promise<bigint> { - return await api.query.SubtensorModule.RootClaimableThreshold.getValue(netuid); -} - -export async function sudoSetRootClaimThreshold( - api: TypedApi<typeof subtensor>, - netuid: number, - newValue: bigint, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.SubtensorModule.sudo_set_root_claim_threshold({ - netuid: netuid, - new_value: newValue, - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_root_claim_threshold"); -} - -export async function getTempo(api: TypedApi<typeof subtensor>, netuid: number): Promise<number> { - return await api.query.SubtensorModule.Tempo.getValue(netuid); -} - -export async function sudoSetTempo( - api: TypedApi<typeof subtensor>, - netuid: number, - tempo: number, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.AdminUtils.sudo_set_tempo({ - netuid: netuid, - tempo: tempo, - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_tempo"); -} - -export async function waitForBlocks( - api: TypedApi<typeof subtensor>, - numBlocks: number, -): Promise<void> { - const startBlock = await api.query.System.Number.getValue(); - const targetBlock = startBlock + numBlocks; - - while (true) { - const currentBlock = await api.query.System.Number.getValue(); - if (currentBlock >= targetBlock) { - break; - } - await new Promise((resolve) => setTimeout(resolve, 1000)); - } -} - -export async function getRootClaimable( - api: TypedApi<typeof subtensor>, - hotkey: string, -): Promise<Map<number, bigint>> { - const result = await api.query.SubtensorModule.RootClaimable.getValue(hotkey); - const claimableMap = new Map<number, bigint>(); - for (const [netuid, amount] of result) { - claimableMap.set(netuid, amount); - } - return claimableMap; -} - -export async function getRootClaimed( - api: TypedApi<typeof subtensor>, - netuid: number, - hotkey: string, - coldkey: string, -): Promise<bigint> { - return await api.query.SubtensorModule.RootClaimed.getValue(netuid, hotkey, coldkey); -} - -export async function isSubtokenEnabled( - api: TypedApi<typeof subtensor>, - netuid: number, -): Promise<boolean> { - return await api.query.SubtensorModule.SubtokenEnabled.getValue(netuid); -} - -export async function sudoSetSubtokenEnabled( - api: TypedApi<typeof subtensor>, - netuid: number, - enabled: boolean, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.AdminUtils.sudo_set_subtoken_enabled({ - netuid: netuid, - subtoken_enabled: enabled, - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_subtoken_enabled"); -} - -export async function isNetworkAdded( - api: TypedApi<typeof subtensor>, - netuid: number, -): Promise<boolean> { - return await api.query.SubtensorModule.NetworksAdded.getValue(netuid); -} - -export async function getAdminFreezeWindow(api: TypedApi<typeof subtensor>): Promise<number> { - return await api.query.SubtensorModule.AdminFreezeWindow.getValue(); -} - -export async function sudoSetAdminFreezeWindow( - api: TypedApi<typeof subtensor>, - window: number, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.AdminUtils.sudo_set_admin_freeze_window({ - window: window, - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_admin_freeze_window"); -} - -export async function sudoSetEmaPriceHalvingPeriod( - api: TypedApi<typeof subtensor>, - netuid: number, - emaPriceHalvingPeriod: number, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.AdminUtils.sudo_set_ema_price_halving_period({ - netuid: netuid, - ema_halving: BigInt(emaPriceHalvingPeriod), - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_ema_price_halving_period"); -} - -export async function sudoSetLockReductionInterval( - api: TypedApi<typeof subtensor>, - interval: number, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.AdminUtils.sudo_set_lock_reduction_interval({ - interval: BigInt(interval), - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_lock_reduction_interval"); -} - -export async function sudoSetSubnetMovingAlpha( - api: TypedApi<typeof subtensor>, - alpha: bigint, -): Promise<void> { - const alice = getAliceSigner(); - const internalCall = api.tx.AdminUtils.sudo_set_subnet_moving_alpha({ - alpha: alpha, - }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "sudo_set_subnet_moving_alpha"); -} - -// Debug helpers for claim_root investigation -export async function getSubnetTAO( - api: TypedApi<typeof subtensor>, - netuid: number, -): Promise<bigint> { - return await api.query.SubtensorModule.SubnetTAO.getValue(netuid); -} - -export async function getSubnetMovingPrice( - api: TypedApi<typeof subtensor>, - netuid: number, -): Promise<bigint> { - return await api.query.SubtensorModule.SubnetMovingPrice.getValue(netuid); -} - -export async function getPendingRootAlphaDivs( - api: TypedApi<typeof subtensor>, - netuid: number, -): Promise<bigint> { - return await api.query.SubtensorModule.PendingRootAlphaDivs.getValue(netuid); -} - -export async function getTaoWeight(api: TypedApi<typeof subtensor>): Promise<bigint> { - return await api.query.SubtensorModule.TaoWeight.getValue(); -} - -export async function getSubnetAlphaIn( - api: TypedApi<typeof subtensor>, - netuid: number, -): Promise<bigint> { - return await api.query.SubtensorModule.SubnetAlphaIn.getValue(netuid); -} - -export async function getTotalHotkeyAlpha( - api: TypedApi<typeof subtensor>, - hotkey: string, - netuid: number, -): Promise<bigint> { - return await api.query.SubtensorModule.TotalHotkeyAlpha.getValue(hotkey, netuid); -} diff --git a/e2e/shared/subnet.ts b/e2e/shared/subnet.ts deleted file mode 100644 index e15bd7cbe9..0000000000 --- a/e2e/shared/subnet.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { subtensor } from "@polkadot-api/descriptors"; -import { TypedApi } from "polkadot-api"; -import { KeyPair } from "@polkadot-labs/hdkd-helpers"; -import { getAliceSigner, getSignerFromKeypair, convertPublicKeyToSs58 } from "./address.js"; -import { waitForTransactionWithRetry } from "./transactions.js"; -import { log } from "./logger.js"; - -export async function addNewSubnetwork( - api: TypedApi<typeof subtensor>, - hotkey: KeyPair, - coldkey: KeyPair, -): Promise<number> { - const alice = getAliceSigner(); - const totalNetworks = await api.query.SubtensorModule.TotalNetworks.getValue(); - - // Disable network rate limit for testing - const rateLimit = await api.query.SubtensorModule.NetworkRateLimit.getValue(); - if (rateLimit !== BigInt(0)) { - const internalCall = api.tx.AdminUtils.sudo_set_network_rate_limit({ rate_limit: BigInt(0) }); - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); - await waitForTransactionWithRetry(api, tx, alice, "set_network_rate_limit"); - } - - const signer = getSignerFromKeypair(coldkey); - const registerNetworkTx = api.tx.SubtensorModule.register_network({ - hotkey: convertPublicKeyToSs58(hotkey.publicKey), - }); - await waitForTransactionWithRetry(api, registerNetworkTx, signer, "register_network"); - - return totalNetworks; -} - -export async function burnedRegister( - api: TypedApi<typeof subtensor>, - netuid: number, - hotkeyAddress: string, - coldkey: KeyPair, -): Promise<void> { - const registered = await api.query.SubtensorModule.Uids.getValue(netuid, hotkeyAddress); - if (registered !== undefined) { - log.tx("burned_register", `skipped: hotkey already registered on netuid ${netuid}`); - return; - } - - await new Promise((resolve) => setTimeout(resolve, 1000)); - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.burned_register({ hotkey: hotkeyAddress, netuid: netuid }); - await waitForTransactionWithRetry(api, tx, signer, "burned_register"); -} - -export async function startCall( - api: TypedApi<typeof subtensor>, - netuid: number, - coldkey: KeyPair, -): Promise<void> { - const registerBlock = Number( - await api.query.SubtensorModule.NetworkRegisteredAt.getValue(netuid), - ); - let currentBlock = await api.query.System.Number.getValue(); - const duration = Number(await api.constants.SubtensorModule.InitialStartCallDelay); - - while (currentBlock - registerBlock <= duration) { - await new Promise((resolve) => setTimeout(resolve, 2000)); - currentBlock = await api.query.System.Number.getValue(); - } - - await new Promise((resolve) => setTimeout(resolve, 2000)); - - const signer = getSignerFromKeypair(coldkey); - const tx = api.tx.SubtensorModule.start_call({ netuid: netuid }); - await waitForTransactionWithRetry(api, tx, signer, "start_call"); - - await new Promise((resolve) => setTimeout(resolve, 1000)); -} diff --git a/e2e/shared/transactions.ts b/e2e/shared/transactions.ts deleted file mode 100644 index f6bb700335..0000000000 --- a/e2e/shared/transactions.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { subtensor } from "@polkadot-api/descriptors"; -import { TypedApi, Transaction, PolkadotSigner } from "polkadot-api"; -import { log } from "./logger.js"; - -export const TX_TIMEOUT = 5000; - -export async function waitForTransactionWithRetry( - api: TypedApi<typeof subtensor>, - tx: Transaction<{}, string, string, void>, - signer: PolkadotSigner, - label: string, - maxRetries = 1, -): Promise<void> { - let success = false; - let retries = 0; - - while (!success && retries < maxRetries) { - await waitForTransactionCompletion(tx, signer, label) - .then(() => { - success = true; - }) - .catch((error) => { - log.tx(label, `error: ${error}`); - }); - await new Promise((resolve) => setTimeout(resolve, 1000)); - retries += 1; - } - - if (!success) { - throw new Error(`[${label}] failed after ${maxRetries} retries`); - } -} - -async function waitForTransactionCompletion( - tx: Transaction<{}, string, string, void>, - signer: PolkadotSigner, - label: string, -): Promise<void> { - return new Promise<void>((resolve, reject) => { - let txHash = ""; - const subscription = tx.signSubmitAndWatch(signer).subscribe({ - next(value) { - txHash = value.txHash; - if (value.type === "finalized") { - log.tx(label, `finalized: ${value.txHash}`); - subscription.unsubscribe(); - clearTimeout(timeoutId); - if (!value.ok) { - const errorStr = JSON.stringify(value.dispatchError, null, 2); - log.tx(label, `dispatch error: ${errorStr}`); - reject(new Error(`[${label}] dispatch error: ${errorStr}`)); - } else { - resolve(); - } - } - }, - error(err) { - log.error(label, `failed: ${err}`); - subscription.unsubscribe(); - clearTimeout(timeoutId); - reject(err); - }, - }); - - const timeoutId = setTimeout(() => { - subscription.unsubscribe(); - log.tx(label, `timeout for tx: ${txHash}`); - reject(new Error(`[${label}] timeout`)); - }, TX_TIMEOUT); - }); -} diff --git a/e2e/shared/tsconfig.json b/e2e/shared/tsconfig.json deleted file mode 100644 index b4cbbb843b..0000000000 --- a/e2e/shared/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "types": ["node"] - } -} diff --git a/e2e/shield/.gitignore b/e2e/shield/.gitignore deleted file mode 100644 index 3e6b0f99ff..0000000000 --- a/e2e/shield/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -node-subtensor/ diff --git a/e2e/shield/helpers.ts b/e2e/shield/helpers.ts deleted file mode 100644 index 1b8af56450..0000000000 --- a/e2e/shield/helpers.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { TypedApi, PolkadotSigner } from "polkadot-api"; -import { Binary } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; -import { xchacha20poly1305 } from "@noble/ciphers/chacha.js"; -import { randomBytes } from "@noble/ciphers/utils.js"; -import { MlKem768 } from "mlkem"; -import { xxhashAsU8a } from "@polkadot/util-crypto"; -import type { subtensor } from "@polkadot-api/descriptors"; - -export const getNextKey = async ( - api: TypedApi<typeof subtensor>, -): Promise<Uint8Array | undefined> => { - // Query at "best" (not default "finalized") because keys rotate every block - // and finalized lags ~2 blocks behind best with GRANDPA. Using finalized - // would return a stale key whose hash won't match CurrentKey/NextKey at - // block-building time, causing InvalidShieldedTxPubKeyHash rejection. - const key = await api.query.MevShield.NextKey.getValue({ at: "best" }); - if (!key) return undefined; - if (key instanceof Binary) return key.asBytes(); - return hexToU8a(key as string); -}; - -export const getCurrentKey = async ( - api: TypedApi<typeof subtensor>, -): Promise<Uint8Array | undefined> => { - const key = await api.query.MevShield.CurrentKey.getValue({ at: "best" }); - if (!key) return undefined; - if (key instanceof Binary) return key.asBytes(); - return hexToU8a(key as string); -}; - -export const encryptTransaction = async ( - plaintext: Uint8Array, - publicKey: Uint8Array, -): Promise<Uint8Array> => { - const keyHash = xxhashAsU8a(publicKey, 128); - - const mlKem = new MlKem768(); - const [kemCt, sharedSecret] = await mlKem.encap(publicKey); - - const nonce = randomBytes(24); - const chacha = xchacha20poly1305(sharedSecret, nonce); - const aeadCt = chacha.encrypt(plaintext); - - const kemLenBytes = new Uint8Array(2); - new DataView(kemLenBytes.buffer).setUint16(0, kemCt.length, true); - - return new Uint8Array([...keyHash, ...kemLenBytes, ...kemCt, ...nonce, ...aeadCt]); -}; - -export const submitEncrypted = async ( - api: TypedApi<typeof subtensor>, - signer: PolkadotSigner, - innerTxBytes: Uint8Array, - publicKey: Uint8Array, - nonce?: number, -) => { - const ciphertext = await encryptTransaction(innerTxBytes, publicKey); - return submitEncryptedRaw(api, signer, ciphertext, nonce); -}; - -export const submitEncryptedRaw = async ( - api: TypedApi<typeof subtensor>, - signer: PolkadotSigner, - ciphertext: Uint8Array, - nonce?: number, -) => { - const tx = api.tx.MevShield.submit_encrypted({ - ciphertext: Binary.fromBytes(ciphertext), - }); - return tx.signAndSubmit(signer, { - ...(nonce !== undefined ? { nonce } : {}), - mortality: { mortal: true, period: 8 }, - }); -}; diff --git a/e2e/shield/package.json b/e2e/shield/package.json deleted file mode 100644 index d04f2acf6a..0000000000 --- a/e2e/shield/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "e2e-shield", - "version": "1.0.0", - "type": "module", - "scripts": { - "test": "vitest run" - }, - "dependencies": { - "e2e-shared": "workspace:*", - "@noble/ciphers": "catalog:", - "@polkadot/util": "catalog:", - "@polkadot/util-crypto": "catalog:", - "@polkadot-api/descriptors": "file:../.papi/descriptors", - "mlkem": "catalog:", - "polkadot-api": "catalog:" - }, - "devDependencies": { - "@types/node": "catalog:", - "vitest": "catalog:" - } -} diff --git a/e2e/shield/setup.ts b/e2e/shield/setup.ts deleted file mode 100644 index 3ba1294026..0000000000 --- a/e2e/shield/setup.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { writeFile, readFile, rm, mkdir } from "node:fs/promises"; -import { generateChainSpec, insertKeys } from "e2e-shared/chainspec.js"; -import { - startNode, - started, - peerCount, - finalizedBlocks, - stop, - log, - type Node, - type NodeOptions, -} from "e2e-shared/node.js"; - -const BASE_DIR = "/tmp/subtensor-e2e/shield-tests"; -const CHAIN_SPEC_PATH = `${BASE_DIR}/chain-spec.json`; -const STATE_FILE = `${BASE_DIR}/nodes.json`; - -export type NetworkState = { - binaryPath: string; - chainSpec: string; - nodes: { - name: string; - rpcPort: number; - port: number; - pid: number; - basePath: string; - }[]; -}; - -const nodes: Node[] = []; - -const BINARY_PATH = process.env.BINARY_PATH || "../../target/release/node-subtensor"; - -type NodeConfig = Omit<NodeOptions, "binaryPath" | "chainSpec"> & { - keySeed?: string; -}; - -const NODE_CONFIGS: NodeConfig[] = [ - { name: "one", port: 30333, rpcPort: 9944, basePath: `${BASE_DIR}/one`, validator: true }, - { name: "two", port: 30334, rpcPort: 9945, basePath: `${BASE_DIR}/two`, validator: true }, - { - name: "three", - port: 30335, - rpcPort: 9946, - basePath: `${BASE_DIR}/three`, - validator: true, - keySeed: "//Three", - }, -]; - -export async function setup() { - log(`Setting up ${NODE_CONFIGS.length}-node network for shield E2E tests`); - log(`Binary path: ${BINARY_PATH}`); - - await mkdir(BASE_DIR, { recursive: true }); - - await generateChainSpec(BINARY_PATH, CHAIN_SPEC_PATH); - - for (const config of NODE_CONFIGS) { - await rm(config.basePath, { recursive: true, force: true }); - } - - // Insert keys for authority nodes that don't have built-in substrate shortcuts. - for (const config of NODE_CONFIGS) { - if (config.keySeed) { - insertKeys(BINARY_PATH, config.basePath, CHAIN_SPEC_PATH, config.keySeed); - } - } - - for (const config of NODE_CONFIGS) { - const node = startNode({ - binaryPath: BINARY_PATH, - chainSpec: CHAIN_SPEC_PATH, - ...config, - }); - nodes.push(node); - await started(node); - } - - const all = Promise.all.bind(Promise); - - await all(nodes.map((n) => peerCount(n, nodes.length - 1))); - log("All nodes peered"); - - await all(nodes.map((n) => finalizedBlocks(n, 3))); - log("All nodes finalized block 3"); - - const state: NetworkState = { - binaryPath: BINARY_PATH, - chainSpec: CHAIN_SPEC_PATH, - nodes: NODE_CONFIGS.map((c, i) => ({ - name: c.name, - rpcPort: c.rpcPort, - port: c.port, - pid: nodes[i].process.pid!, - basePath: c.basePath, - })), - }; - - await writeFile(STATE_FILE, JSON.stringify(state, null, 2)); - log("Network state written to " + STATE_FILE); -} - -export async function teardown() { - log("Tearing down shield E2E test network"); - - // Read the state file to find ALL nodes (including extras added by scaling tests). - let state: NetworkState | undefined; - try { - const data = await readFile(STATE_FILE, "utf-8"); - state = JSON.parse(data); - } catch {} - - // Stop nodes we have handles to (from globalSetup). - for (const node of nodes) { - try { - await stop(node); - } catch (e) { - log(`Warning: failed to stop ${node.name}: ${e}`); - } - } - - // Kill any extra nodes (added by scaling tests) by PID. - if (state) { - const ownPids = new Set(nodes.map((n) => n.process.pid)); - for (const nodeInfo of state.nodes) { - if (!ownPids.has(nodeInfo.pid)) { - try { - process.kill(nodeInfo.pid, "SIGTERM"); - log(`Killed extra node ${nodeInfo.name} (pid ${nodeInfo.pid})`); - } catch { - // Already dead, ignore. - } - } - } - } - - // Clean up the entire suite directory in one shot. - await rm(BASE_DIR, { recursive: true, force: true }); - - log("Teardown complete"); -} - -export async function readNetworkState(): Promise<NetworkState> { - const data = await readFile(STATE_FILE, "utf-8"); - return JSON.parse(data); -} diff --git a/e2e/shield/tests/00-basic.test.ts b/e2e/shield/tests/00-basic.test.ts deleted file mode 100644 index 8eca09daf6..0000000000 --- a/e2e/shield/tests/00-basic.test.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { readFile } from "node:fs/promises"; -import type { PolkadotClient, TypedApi } from "polkadot-api"; -import { Binary } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; -import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; -import type { NetworkState } from "../setup.js"; -import { - connectClient, - createSigner, - getAccountNonce, - getBalance, - waitForFinalizedBlocks, -} from "e2e-shared/client.js"; -import { getNextKey, getCurrentKey, encryptTransaction, submitEncrypted } from "../helpers.js"; - -let client: PolkadotClient; -let api: TypedApi<typeof subtensor>; -let state: NetworkState; - -const alice = createSigner("//Alice"); -const bob = createSigner("//Bob"); -const charlie = createSigner("//Charlie"); - -beforeAll(async () => { - const data = await readFile("/tmp/subtensor-e2e/shield-tests/nodes.json", "utf-8"); - state = JSON.parse(data); - ({ client, api } = await connectClient(state.nodes[0].rpcPort)); - - // Wait for enough finalized blocks so the inherent has had time to run - // and keys have rotated at least once. - await waitForFinalizedBlocks(client, 3); -}); - -afterAll(() => { - client?.destroy(); -}); - -describe("MEV Shield — key rotation", () => { - it("NextKey and CurrentKey are populated and rotate across blocks", async () => { - const nextKey1 = await getNextKey(api); - expect(nextKey1).toBeDefined(); - expect(nextKey1!.length).toBe(1184); // ML-KEM-768 public key - - const currentKey1 = await getCurrentKey(api); - expect(currentKey1).toBeDefined(); - expect(currentKey1!.length).toBe(1184); - - await waitForFinalizedBlocks(client, 2); - - const nextKey2 = await getNextKey(api); - expect(nextKey2).toBeDefined(); - // Keys should have rotated — nextKey changes each block. - expect(nextKey2).not.toEqual(nextKey1); - - const currentKey2 = await getCurrentKey(api); - expect(currentKey2).toBeDefined(); - expect(currentKey2).not.toEqual(currentKey1); - }); - - it("AuthorKeys stores per-author keys", async () => { - const authorities = await api.query.Aura.Authorities.getValue(); - expect(authorities.length).toBeGreaterThan(0); - - let foundKeys = 0; - for (const authority of authorities) { - const key = await api.query.MevShield.AuthorKeys.getValue(authority); - if (key) foundKeys++; - } - - expect(foundKeys).toBeGreaterThan(0); - }); -}); - -describe("MEV Shield — encrypted transactions", () => { - it("Happy path: wrapper and inner tx are included in the same block", async () => { - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 10_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); - - it("Failed inner tx: wrapper succeeds but inner transfer has no effect", async () => { - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - // Encrypt a transfer of more than Alice has. - // The wrapper is valid (correct key_hash, valid encryption), but the - // inner transfer should fail at dispatch with InsufficientBalance. - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 9_000_000_000_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - // The inner transfer failed, so bob's balance should not increase. - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBe(balanceBefore); - }); - - it("Malformed ciphertext is rejected at pool level", async () => { - const nonce = await getAccountNonce(api, alice.address); - - // 5 bytes of garbage — not valid ciphertext at all. - const garbage = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]); - - const tx = api.tx.MevShield.submit_encrypted({ - ciphertext: Binary.fromBytes(garbage), - }); - - // Pool validation rejects with FailedShieldedTxParsing (Custom code 23). - await expect( - tx.signAndSubmit(alice.signer, { nonce, mortality: { mortal: true, period: 8 } }), - ).rejects.toThrow(); - }); - - it("Multiple encrypted txs in same block", async () => { - // Use different signers to avoid nonce ordering issues between - // the outer wrappers and decrypted inner transactions. - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, charlie.address); - - const senders = [alice, bob]; - const amount = 1_000_000_000n; - const txPromises = []; - - for (const sender of senders) { - const nonce = await getAccountNonce(api, sender.address); - - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(charlie.address), - value: amount, - }).sign(sender.signer, { nonce: nonce + 1 }); - - txPromises.push(submitEncrypted(api, sender.signer, hexToU8a(innerTxHex), nextKey!, nonce)); - } - - await Promise.all(txPromises); - - const balanceAfter = await getBalance(api, charlie.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); - - it("Wrong key hash is not included by the block proposer", async () => { - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 1_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - const ciphertext = await encryptTransaction(hexToU8a(innerTxHex), nextKey!); - - // Tamper the first 16 bytes (key_hash). - const tampered = new Uint8Array(ciphertext); - for (let i = 0; i < 16; i++) tampered[i] = 0xff; - - const tx = api.tx.MevShield.submit_encrypted({ - ciphertext: Binary.fromBytes(tampered), - }); - const signedHex = await tx.sign(alice.signer, { - nonce, - mortality: { mortal: true, period: 8 }, - }); - // Send without waiting — the tx enters the pool but the block - // proposer will skip it because the key_hash doesn't match. - client.submit(signedHex).catch(() => {}); - - await waitForFinalizedBlocks(client, 3); - - // The inner transfer should NOT have executed. - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBe(balanceBefore); - }); - - it("Stale key is not included after rotation", async () => { - const staleKey = await getNextKey(api); - expect(staleKey).toBeDefined(); - - // Wait for enough blocks that the key has rotated past both - // currentKey and nextKey positions. - await waitForFinalizedBlocks(client, 3); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 1_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - const ciphertext = await encryptTransaction(hexToU8a(innerTxHex), staleKey!); - - const tx = api.tx.MevShield.submit_encrypted({ - ciphertext: Binary.fromBytes(ciphertext), - }); - const signedHex = await tx.sign(alice.signer, { - nonce, - mortality: { mortal: true, period: 8 }, - }); - // Send without waiting — the block proposer will reject because - // key_hash no longer matches currentKey or nextKey. - client.submit(signedHex).catch(() => {}); - - await waitForFinalizedBlocks(client, 3); - - // The inner transfer should NOT have executed. - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBe(balanceBefore); - }); -}); diff --git a/e2e/shield/tests/01-scaling.test.ts b/e2e/shield/tests/01-scaling.test.ts deleted file mode 100644 index 386124a655..0000000000 --- a/e2e/shield/tests/01-scaling.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { readFile, writeFile, rm } from "node:fs/promises"; -import type { PolkadotClient, TypedApi } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; -import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; -import type { NetworkState } from "../setup.js"; -import { - connectClient, - createSigner, - getAccountNonce, - getBalance, - waitForFinalizedBlocks, -} from "e2e-shared/client.js"; -import { startNode, started, log } from "e2e-shared/node.js"; -import { getNextKey, submitEncrypted } from "../helpers.js"; - -let client: PolkadotClient; -let api: TypedApi<typeof subtensor>; -let state: NetworkState; - -const alice = createSigner("//Alice"); -const bob = createSigner("//Bob"); -const charlie = createSigner("//Charlie"); - -// Extra nodes join as non-authority full nodes. -const EXTRA_NODE_CONFIGS = [ - { name: "four", port: 30336, rpcPort: 9947, basePath: "/tmp/subtensor-e2e/shield-tests/four" }, - { name: "five", port: 30337, rpcPort: 9948, basePath: "/tmp/subtensor-e2e/shield-tests/five" }, - { name: "six", port: 30338, rpcPort: 9949, basePath: "/tmp/subtensor-e2e/shield-tests/six" }, -]; - -beforeAll(async () => { - const data = await readFile("/tmp/subtensor-e2e/shield-tests/nodes.json", "utf-8"); - state = JSON.parse(data); - ({ client, api } = await connectClient(state.nodes[0].rpcPort)); - - // Start 3 additional full nodes to scale from 3 → 6. - for (const config of EXTRA_NODE_CONFIGS) { - await rm(config.basePath, { recursive: true, force: true }); - - const node = startNode({ - ...config, - binaryPath: state.binaryPath, - validator: false, - chainSpec: state.chainSpec, - }); - await started(node); - log(`Extra node ${config.name} started`); - - // Track in state file so global teardown can clean up. - state.nodes.push({ - name: config.name, - rpcPort: config.rpcPort, - port: config.port, - pid: node.process.pid!, - basePath: config.basePath, - }); - } - - // Persist updated state for subsequent test files (edge-cases). - await writeFile("/tmp/subtensor-e2e/shield-tests/nodes.json", JSON.stringify(state, null, 2)); -}); - -afterAll(() => { - client?.destroy(); -}); - -describe("MEV Shield — 6 node scaling", () => { - it("Network scales to 6 nodes with full peering", async () => { - expect(state.nodes.length).toBe(6); - - // Verify the network is healthy by checking finalization continues. - await waitForFinalizedBlocks(client, 2); - }); - - it("Key rotation continues with more peers", async () => { - const key1 = await getNextKey(api); - expect(key1).toBeDefined(); - - await waitForFinalizedBlocks(client, 2); - - const key2 = await getNextKey(api); - expect(key2).toBeDefined(); - expect(key2!.length).toBe(1184); - }); - - it("Encrypted tx works with 6 nodes", async () => { - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 5_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); - - it("Multiple encrypted txs in same block with 6 nodes", async () => { - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, charlie.address); - - const senders = [alice, bob]; - const amount = 1_000_000_000n; - const txPromises = []; - - for (const sender of senders) { - const nonce = await getAccountNonce(api, sender.address); - - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(charlie.address), - value: amount, - }).sign(sender.signer, { nonce: nonce + 1 }); - - txPromises.push(submitEncrypted(api, sender.signer, hexToU8a(innerTxHex), nextKey!, nonce)); - } - - await Promise.all(txPromises); - - const balanceAfter = await getBalance(api, charlie.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); -}); diff --git a/e2e/shield/tests/02-edge-cases.test.ts b/e2e/shield/tests/02-edge-cases.test.ts deleted file mode 100644 index 55dc53f134..0000000000 --- a/e2e/shield/tests/02-edge-cases.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { readFile } from "node:fs/promises"; -import type { PolkadotClient, TypedApi } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; -import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; -import type { NetworkState } from "../setup.js"; -import { connectClient, createSigner, getAccountNonce, getBalance } from "e2e-shared/client.js"; -import { getNextKey, submitEncrypted } from "../helpers.js"; - -let client: PolkadotClient; -let api: TypedApi<typeof subtensor>; -let state: NetworkState; - -const alice = createSigner("//Alice"); -const bob = createSigner("//Bob"); - -beforeAll(async () => { - const data = await readFile("/tmp/subtensor-e2e/shield-tests/nodes.json", "utf-8"); - state = JSON.parse(data); - ({ client, api } = await connectClient(state.nodes[0].rpcPort)); -}); - -afterAll(() => { - client?.destroy(); -}); - -describe("MEV Shield — edge cases", () => { - it("Encrypted tx persists across blocks (CurrentKey fallback)", async () => { - // The idea: submit an encrypted tx right at a block boundary. - // Even if the key rotates (NextKey changes), the old key becomes - // CurrentKey, so the extension still accepts it. - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 2_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - // Submit and wait for finalization — the tx may land in the next block - // or the one after, where CurrentKey = the old NextKey. - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); - - it("Valid ciphertext with invalid inner call", async () => { - // Encrypt garbage bytes (not a valid extrinsic) using a valid NextKey. - // The wrapper tx should be included in a block because: - // - The ciphertext is well-formed (key_hash, kem_ct, nonce, aead_ct) - // - The key_hash matches a known key - // But the inner decrypted bytes won't decode as a valid extrinsic, - // so no inner transaction should execute. - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - // Garbage "inner transaction" bytes — not a valid extrinsic at all. - const garbageInner = new Uint8Array(64); - for (let i = 0; i < 64; i++) garbageInner[i] = (i * 7 + 13) & 0xff; - - const nonce = await getAccountNonce(api, alice.address); - - await submitEncrypted(api, alice.signer, garbageInner, nextKey!, nonce); - - // No balance change — the garbage inner call could not have been a valid transfer. - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBe(balanceBefore); - }); -}); diff --git a/e2e/shield/tests/03-timing.test.ts b/e2e/shield/tests/03-timing.test.ts deleted file mode 100644 index c7309708d2..0000000000 --- a/e2e/shield/tests/03-timing.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { readFile } from "node:fs/promises"; -import type { PolkadotClient, TypedApi } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; -import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; -import type { NetworkState } from "../setup.js"; -import { - connectClient, - createSigner, - getAccountNonce, - getBalance, - waitForFinalizedBlocks, - sleep, -} from "e2e-shared/client.js"; -import { getNextKey, submitEncrypted } from "../helpers.js"; - -let client: PolkadotClient; -let api: TypedApi<typeof subtensor>; -let state: NetworkState; - -const alice = createSigner("//Alice"); -const bob = createSigner("//Bob"); - -beforeAll(async () => { - const data = await readFile("/tmp/subtensor-e2e/shield-tests/nodes.json", "utf-8"); - state = JSON.parse(data); - ({ client, api } = await connectClient(state.nodes[0].rpcPort)); -}); - -afterAll(() => { - client?.destroy(); -}); - -describe("MEV Shield — timing boundaries", () => { - it("Submit immediately after a new block", async () => { - // Wait for a fresh finalized block, then immediately read NextKey and submit. - // This tests the "just after block" boundary where keys just rotated. - await waitForFinalizedBlocks(client, 1); - - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 1_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); - - it("Submit mid-block (~6s after block)", async () => { - // Wait for a block, then sleep 6s (half of 12s slot) before submitting. - // The key should still be valid — the same NextKey applies until the next block. - await waitForFinalizedBlocks(client, 1); - await sleep(6_000); - - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 1_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); - - it("Submit just before next block (~11s after block)", async () => { - // Wait for a block, then sleep ~11s to submit right before the next slot. - // The tx enters the pool just as the next block is about to be produced. - // It should still be included because the N+2 author hasn't changed yet, - // and PendingKey will match on the next block's proposer check. - await waitForFinalizedBlocks(client, 1); - await sleep(11_000); - - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 1_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); - - it("Read key, wait full slot (12s), then submit", async () => { - // Read NextKey, wait a full slot duration, then submit. - // After one full slot, the key rotates: old NextKey becomes PendingKey. - // The tx should still be included by the target N+2 author. - const nextKey = await getNextKey(api); - expect(nextKey).toBeDefined(); - - await sleep(12_000); - - const balanceBefore = await getBalance(api, bob.address); - - const nonce = await getAccountNonce(api, alice.address); - const innerTxHex = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 1_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - await submitEncrypted(api, alice.signer, hexToU8a(innerTxHex), nextKey!, nonce); - - const balanceAfter = await getBalance(api, bob.address); - expect(balanceAfter).toBeGreaterThan(balanceBefore); - }); -}); diff --git a/e2e/shield/tests/04-mortality.test.ts b/e2e/shield/tests/04-mortality.test.ts deleted file mode 100644 index 76e704f82b..0000000000 --- a/e2e/shield/tests/04-mortality.test.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { readFile, writeFile, rm } from "node:fs/promises"; -import type { PolkadotClient, TypedApi } from "polkadot-api"; -import { Binary } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; -import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; -import type { NetworkState } from "../setup.js"; -import { - connectClient, - createSigner, - getAccountNonce, - getBalance, - sleep, -} from "e2e-shared/client.js"; -import { startNode, started, peerCount, stop, log, type Node } from "e2e-shared/node.js"; -import { getNextKey, encryptTransaction } from "../helpers.js"; - -let authorityClient: PolkadotClient; -let authorityApi: TypedApi<typeof subtensor>; -let extraClient: PolkadotClient; -let extraApi: TypedApi<typeof subtensor>; -let state: NetworkState; -let extraNode: Node; - -const alice = createSigner("//Alice"); -const bob = createSigner("//Bob"); - -const EXTRA_NODE = { - name: "mortality-test", - port: 30339, - rpcPort: 9950, - basePath: "/tmp/subtensor-e2e/shield-tests/mortality-test", -}; - -// MAX_SHIELD_ERA_PERIOD is 8 blocks. With 12s slots, that's ~96s. -const MAX_ERA_BLOCKS = 8; -const SLOT_DURATION_MS = 12_000; -const POLL_INTERVAL_MS = 3_000; - -beforeAll(async () => { - const data = await readFile("/tmp/subtensor-e2e/shield-tests/nodes.json", "utf-8"); - state = JSON.parse(data); - - // Connect to an authority node for key queries. - ({ client: authorityClient, api: authorityApi } = await connectClient(state.nodes[0].rpcPort)); - - // Start a non-authority node to submit txs to. - await rm(EXTRA_NODE.basePath, { recursive: true, force: true }); - extraNode = startNode({ - ...EXTRA_NODE, - binaryPath: state.binaryPath, - validator: false, - chainSpec: state.chainSpec, - }); - await started(extraNode); - await peerCount(extraNode, state.nodes.length); - log(`Extra non-authority node started for mortality tests`); - - // Track for teardown. - state.nodes.push({ - ...EXTRA_NODE, - pid: extraNode.process.pid!, - }); - await writeFile("/tmp/subtensor-e2e/shield-tests/nodes.json", JSON.stringify(state, null, 2)); - - ({ client: extraClient, api: extraApi } = await connectClient(EXTRA_NODE.rpcPort)); -}); - -afterAll(async () => { - extraClient?.destroy(); - authorityClient?.destroy(); - if (extraNode) { - try { - await stop(extraNode); - } catch {} - } -}); - -describe("MEV Shield — mortality eviction", () => { - it( - "Tx with tampered key_hash submitted to non-authority is evicted within mortality window", - async () => { - // Read a valid NextKey from an authority node, encrypt a real inner tx. - const nextKey = await getNextKey(authorityApi); - expect(nextKey).toBeDefined(); - - const balanceBefore = await getBalance(extraApi, bob.address); - - const nonce = await getAccountNonce(extraApi, alice.address); - const innerTxHex = await extraApi.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(bob.address), - value: 1_000_000_000n, - }).sign(alice.signer, { nonce: nonce + 1 }); - - // Encrypt with valid key, then tamper the key_hash so no proposer will include it. - const ciphertext = await encryptTransaction(hexToU8a(innerTxHex), nextKey!); - const tampered = new Uint8Array(ciphertext); - for (let i = 0; i < 16; i++) tampered[i] = 0xff; - - const tx = extraApi.tx.MevShield.submit_encrypted({ - ciphertext: Binary.fromBytes(tampered), - }); - - // Sign with short mortality (must be ≤ MAX_SHIELD_ERA_PERIOD=8 to pass - // CheckMortality validation). The tx enters the pool but no proposer - // will include it (tampered key_hash doesn't match PendingKey). - const signedHex = await tx.sign(alice.signer, { - nonce, - mortality: { mortal: true, period: 8 }, - }); - - // Submit via raw RPC to get immediate feedback on pool acceptance. - let txHash: string; - try { - txHash = await extraClient._request("author_submitExtrinsic", [signedHex]); - log(`Tx submitted successfully, hash: ${txHash}`); - } catch (err: unknown) { - throw new Error(`Tx rejected at pool entry: ${err}`); - } - - // Verify it's in the pool. - await sleep(1_000); - const pending: string[] = await extraClient._request("author_pendingExtrinsics", []); - log(`Pool has ${pending.length} pending tx(s)`); - - // Now poll until the tx disappears (mortality eviction). - const start = Date.now(); - const maxPollMs = (MAX_ERA_BLOCKS + 4) * SLOT_DURATION_MS; - let evicted = false; - - log(`Waiting for mortality eviction (up to ${maxPollMs / 1000}s)...`); - - while (Date.now() - start < maxPollMs) { - await sleep(POLL_INTERVAL_MS); - - const pending: string[] = await extraClient._request("author_pendingExtrinsics", []); - - if (pending.length === 0) { - evicted = true; - break; - } - } - - const elapsed = Date.now() - start; - log(`Tx ${evicted ? "evicted" : "still in pool"} after ${(elapsed / 1000).toFixed(1)}s`); - - expect(evicted).toBe(true); - - // Eviction should happen within the mortality window plus margin. - const maxExpectedMs = (MAX_ERA_BLOCKS + 2) * SLOT_DURATION_MS; - expect(elapsed).toBeLessThan(maxExpectedMs); - - // The inner transfer should NOT have executed. - const balanceAfter = await getBalance(extraApi, bob.address); - expect(balanceAfter).toBe(balanceBefore); - }, - // Longer timeout: wait for mortality window + setup overhead. - (MAX_ERA_BLOCKS + 8) * SLOT_DURATION_MS, - ); -}); diff --git a/e2e/shield/tsconfig.json b/e2e/shield/tsconfig.json deleted file mode 100644 index c2f86d9e2c..0000000000 --- a/e2e/shield/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "types": ["node", "vitest/globals"] - } -} diff --git a/e2e/shield/vitest.config.ts b/e2e/shield/vitest.config.ts deleted file mode 100644 index d9c2978930..0000000000 --- a/e2e/shield/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineConfig } from "vitest/config"; -import AlphabeticalSequencer from "e2e-shared/sequencer.js"; - -export default defineConfig({ - test: { - globals: true, - testTimeout: 120_000, - hookTimeout: 300_000, - fileParallelism: false, - globalSetup: "./setup.ts", - include: ["tests/**/*.test.ts"], - sequence: { - sequencer: AlphabeticalSequencer, - }, - }, -}); diff --git a/e2e/staking/package.json b/e2e/staking/package.json deleted file mode 100644 index 80648d3d0f..0000000000 --- a/e2e/staking/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "e2e-staking", - "version": "1.0.0", - "type": "module", - "license": "ISC", - "scripts": { - "test": "vitest run" - }, - "dependencies": { - "e2e-shared": "workspace:*" - }, - "devDependencies": { - "@types/node": "catalog:", - "vitest": "catalog:" - }, - "prettier": { - "singleQuote": false, - "trailingComma": "all", - "printWidth": 120 - } -} diff --git a/e2e/staking/setup.ts b/e2e/staking/setup.ts deleted file mode 100644 index d1887be5a6..0000000000 --- a/e2e/staking/setup.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { rm, mkdir } from "node:fs/promises"; -import { - generateChainSpec, - insertKeys, - startNode, - started, - peerCount, - finalizedBlocks, - stop, - nodeLog, - destroyClient, - getDevnetApi, - sudoSetLockReductionInterval, - log, - type Node, - type NodeOptions, -} from "e2e-shared"; - -const CHAIN_SPEC_PATH = "/tmp/subtensor-e2e/staking-tests/chain-spec.json"; -const BASE_DIR = "/tmp/subtensor-e2e/staking-tests"; - -const BINARY_PATH = process.env.BINARY_PATH || "../../target/release/node-subtensor"; - -const nodes: Node[] = []; - -type NodeConfig = Omit<NodeOptions, "binaryPath" | "chainSpec"> & { - keySeed?: string; -}; - -const NODE_CONFIGS: NodeConfig[] = [ - { name: "one", port: 30433, rpcPort: 9944, basePath: `${BASE_DIR}/one`, validator: true }, - { name: "two", port: 30434, rpcPort: 9945, basePath: `${BASE_DIR}/two`, validator: true }, - { - name: "three", - port: 30435, - rpcPort: 9946, - basePath: `${BASE_DIR}/three`, - validator: true, - keySeed: "//Three", - }, -]; - -async function startNetwork() { - nodeLog(`Setting up ${NODE_CONFIGS.length}-node network for staking E2E tests`); - nodeLog(`Binary path: ${BINARY_PATH}`); - - await mkdir(BASE_DIR, { recursive: true }); - - // Generate local chain spec (built-in has One, Two and Three as authorities) - await generateChainSpec(BINARY_PATH, CHAIN_SPEC_PATH); - - // Clean up old base paths - for (const config of NODE_CONFIGS) { - await rm(config.basePath, { recursive: true, force: true }); - } - - // Insert keys for authority nodes that don't have built-in substrate shortcuts. - for (const config of NODE_CONFIGS) { - if (config.keySeed) { - insertKeys(BINARY_PATH, config.basePath, CHAIN_SPEC_PATH, config.keySeed); - } - } - - // Start all validator nodes - for (const config of NODE_CONFIGS) { - const node = startNode({ - binaryPath: BINARY_PATH, - chainSpec: CHAIN_SPEC_PATH, - ...config, - }); - nodes.push(node); - await started(node); - } - - const all = Promise.all.bind(Promise); - - // Wait for nodes to peer with each other - await all(nodes.map((n) => peerCount(n, nodes.length - 1))); - nodeLog("All nodes peered"); - - // Wait for block finalization - await all(nodes.map((n) => finalizedBlocks(n, 3))); - nodeLog("All nodes finalized block 3"); -} - -async function stopNetwork() { - nodeLog("Stopping staking-tests network"); - - for (const node of nodes) { - try { - await stop(node); - } catch (e) { - nodeLog(`Warning: failed to stop ${node.name}: ${e}`); - } - } - - // Clean up the suite directory - await rm(BASE_DIR, { recursive: true, force: true }); - - nodeLog("Network stopped"); -} - -export async function setup() { - // Start the network - await startNetwork(); - - // Connect to the network and configure for tests - const api = await getDevnetApi(); - log.info("Setup: set lock reduction interval to 1 for instant lock cost decay"); - - // Set lock reduction interval to 1 block to make network registration lock cost decay instantly. - // By default, the lock cost doubles with each subnet registration and decays over 14 days (100,800 blocks). - // Without this, tests creating multiple subnets would fail with CannotAffordLockCost. - await sudoSetLockReductionInterval(api, 1); -} - -export async function teardown() { - // Destroy the API client first - destroyClient(); - - // Stop the network - await stopNetwork(); -} diff --git a/e2e/staking/test/add-stake-limit.test.ts b/e2e/staking/test/add-stake-limit.test.ts deleted file mode 100644 index 63e1bb6ba0..0000000000 --- a/e2e/staking/test/add-stake-limit.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, it, expect, beforeAll } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - startCall, - addStakeLimit, - getStake, - tao, - log, -} from "e2e-shared"; - -describe("▶ add_stake_limit extrinsic", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkeyAddress = convertPublicKeyToSs58(hotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - let netuid: number; - - beforeAll(async () => { - const api = await getDevnetApi(); - await forceSetBalance(api, hotkeyAddress); - await forceSetBalance(api, coldkeyAddress); - netuid = await addNewSubnetwork(api, hotkey, coldkey); - await startCall(api, netuid, coldkey); - }); - - it("should add stake with price limit (allow partial)", async () => { - const api = await getDevnetApi(); - - // Get initial stake - const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - - // Add stake with limit price and allow partial fills, limit_price is MAX TAO per Alpha willing to pay. - const stakeAmount = tao(44); - const limitPrice = tao(6); - await addStakeLimit(api, coldkey, hotkeyAddress, netuid, stakeAmount, limitPrice, true); - - // Verify stake increased - const stakeAfter = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - expect(stakeAfter, "Stake should increase").toBeGreaterThan(stakeBefore); - - log.info("✅ Successfully added stake with limit (allow partial)."); - }); - - it("should add stake with price limit (fill or kill)", async () => { - const api = await getDevnetApi(); - - // Get initial stake - const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - - // Add stake with limit price (fill or kill mode), limit_price is MAX TAO per Alpha willing to pay - const stakeAmount = tao(44); - const limitPrice = tao(6); - await addStakeLimit(api, coldkey, hotkeyAddress, netuid, stakeAmount, limitPrice, false); - - // Verify stake increased - const stakeAfter = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - expect(stakeAfter, "Stake should increase").toBeGreaterThan(stakeBefore); - - log.info("✅ Successfully added stake with limit (fill or kill)."); - }); -}); diff --git a/e2e/staking/test/add-stake.test.ts b/e2e/staking/test/add-stake.test.ts deleted file mode 100644 index fd3eecf052..0000000000 --- a/e2e/staking/test/add-stake.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { describe, it, expect, beforeAll } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - startCall, - addStake, - getStake, - tao, - log, -} from "e2e-shared"; - -describe("▶ add_stake extrinsic", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkeyAddress = convertPublicKeyToSs58(hotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - let netuid: number; - - beforeAll(async () => { - const api = await getDevnetApi(); - await forceSetBalance(api, hotkeyAddress); - await forceSetBalance(api, coldkeyAddress); - netuid = await addNewSubnetwork(api, hotkey, coldkey); - await startCall(api, netuid, coldkey); - }); - - it("should add stake to a hotkey", async () => { - const api = await getDevnetApi(); - - // Get initial stake - const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - - // Add stake - const stakeAmount = tao(100); - await addStake(api, coldkey, hotkeyAddress, netuid, stakeAmount); - - // Verify stake increased - const stakeAfter = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - expect(stakeAfter, "Stake should increase after adding stake").toBeGreaterThan(stakeBefore); - - log.info("✅ Successfully added stake."); - }); -}); diff --git a/e2e/staking/test/claim-root.test.ts b/e2e/staking/test/claim-root.test.ts deleted file mode 100644 index e6ca55876c..0000000000 --- a/e2e/staking/test/claim-root.test.ts +++ /dev/null @@ -1,471 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - startCall, - getRootClaimType, - setRootClaimType, - getNumRootClaims, - sudoSetNumRootClaims, - getRootClaimThreshold, - sudoSetRootClaimThreshold, - addStake, - getStake, - claimRoot, - sudoSetTempo, - waitForBlocks, - getRootClaimable, - getRootClaimed, - isSubtokenEnabled, - sudoSetSubtokenEnabled, - sudoSetAdminFreezeWindow, - sudoSetEmaPriceHalvingPeriod, - getSubnetTAO, - getSubnetMovingPrice, - getPendingRootAlphaDivs, - getTaoWeight, - getSubnetAlphaIn, - getTotalHotkeyAlpha, - sudoSetSubnetMovingAlpha, - tao, - log, -} from "e2e-shared"; - -describe("▶ set_root_claim_type extrinsic", () => { - it("should set root claim type to Keep", async () => { - const api = await getDevnetApi(); - - const coldkey = getRandomSubstrateKeypair(); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, coldkeyAddress); - - // Check initial claim type (default is "Swap") - const claimTypeBefore = await getRootClaimType(api, coldkeyAddress); - log.info(`Root claim type before: ${claimTypeBefore}`); - - // Set root claim type to Keep - await setRootClaimType(api, coldkey, "Keep"); - - // Verify claim type changed - const claimTypeAfter = await getRootClaimType(api, coldkeyAddress); - log.info(`Root claim type after: ${claimTypeAfter}`); - - expect(claimTypeAfter).toBe("Keep"); - - log.info("✅ Successfully set root claim type to Keep."); - }); - - it("should set root claim type to Swap", async () => { - const api = await getDevnetApi(); - - const coldkey = getRandomSubstrateKeypair(); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, coldkeyAddress); - - // First set to Keep so we can verify the change to Swap - await setRootClaimType(api, coldkey, "Keep"); - const claimTypeBefore = await getRootClaimType(api, coldkeyAddress); - log.info(`Root claim type before: ${claimTypeBefore}`); - expect(claimTypeBefore).toBe("Keep"); - - // Set root claim type to Swap - await setRootClaimType(api, coldkey, "Swap"); - - // Verify claim type changed - const claimTypeAfter = await getRootClaimType(api, coldkeyAddress); - log.info(`Root claim type after: ${claimTypeAfter}`); - - expect(claimTypeAfter).toBe("Swap"); - - log.info("✅ Successfully set root claim type to Swap."); - }); - - it("should set root claim type to KeepSubnets", async () => { - const api = await getDevnetApi(); - - const coldkey = getRandomSubstrateKeypair(); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, coldkeyAddress); - - // Check initial claim type (default is "Swap") - const claimTypeBefore = await getRootClaimType(api, coldkeyAddress); - log.info(`Root claim type before: ${JSON.stringify(claimTypeBefore)}`); - - // Set root claim type to KeepSubnets with specific subnets - const subnetsToKeep = [1, 2]; - await setRootClaimType(api, coldkey, { type: "KeepSubnets", subnets: subnetsToKeep }); - - // Verify claim type changed - const claimTypeAfter = await getRootClaimType(api, coldkeyAddress); - log.info(`Root claim type after: ${JSON.stringify(claimTypeAfter)}`); - - expect(typeof claimTypeAfter).toBe("object"); - expect((claimTypeAfter as { type: string }).type).toBe("KeepSubnets"); - expect((claimTypeAfter as { subnets: number[] }).subnets).toEqual(subnetsToKeep); - - log.info("✅ Successfully set root claim type to KeepSubnets."); - }); -}); - -describe("▶ sudo_set_num_root_claims extrinsic", () => { - it("should set num root claims", async () => { - const api = await getDevnetApi(); - - // Get initial value - const numClaimsBefore = await getNumRootClaims(api); - log.info(`Num root claims before: ${numClaimsBefore}`); - - // Set new value (different from current) - const newValue = numClaimsBefore + 5n; - await sudoSetNumRootClaims(api, newValue); - - // Verify value changed - const numClaimsAfter = await getNumRootClaims(api); - log.info(`Num root claims after: ${numClaimsAfter}`); - - expect(numClaimsAfter).toBe(newValue); - - log.info("✅ Successfully set num root claims."); - }); -}); - -describe("▶ sudo_set_root_claim_threshold extrinsic", () => { - it("should set root claim threshold for subnet", async () => { - const api = await getDevnetApi(); - - // Create a subnet to test with - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkeyAddress = convertPublicKeyToSs58(hotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, hotkeyAddress); - await forceSetBalance(api, coldkeyAddress); - - const netuid = await addNewSubnetwork(api, hotkey, coldkey); - await startCall(api, netuid, coldkey); - - // Get initial threshold - const thresholdBefore = await getRootClaimThreshold(api, netuid); - log.info(`Root claim threshold before: ${thresholdBefore}`); - - // Set new threshold value (MAX_ROOT_CLAIM_THRESHOLD is 10_000_000) - // The value is stored as I96F32 fixed-point with 32 fractional bits - const newThreshold = 1_000_000n; - await sudoSetRootClaimThreshold(api, netuid, newThreshold); - - // Verify threshold changed - // I96F32 encoding: newThreshold * 2^32 = 1_000_000 * 4294967296 = 4294967296000000 - const thresholdAfter = await getRootClaimThreshold(api, netuid); - log.info(`Root claim threshold after: ${thresholdAfter}`); - - const expectedStoredValue = newThreshold * (1n << 32n); // I96F32 encoding - expect(thresholdAfter).toBe(expectedStoredValue); - - log.info("✅ Successfully set root claim threshold."); - }); -}); - -// Root subnet netuid is 0 -const ROOT_NETUID = 0; - -describe("▶ claim_root extrinsic", () => { - it("should claim root dividends with Keep type (stake to dynamic subnet)", async () => { - const api = await getDevnetApi(); - - // Setup accounts - // - owner1Hotkey/owner1Coldkey: subnet 1 owner - // - owner2Hotkey/owner2Coldkey: subnet 2 owner (needed for root_sell_flag) - // - stakerColdkey: the coldkey that will stake on root and claim dividends - const owner1Hotkey = getRandomSubstrateKeypair(); - const owner1Coldkey = getRandomSubstrateKeypair(); - const owner2Hotkey = getRandomSubstrateKeypair(); - const owner2Coldkey = getRandomSubstrateKeypair(); - const stakerColdkey = getRandomSubstrateKeypair(); - const owner1HotkeyAddress = convertPublicKeyToSs58(owner1Hotkey.publicKey); - const owner1ColdkeyAddress = convertPublicKeyToSs58(owner1Coldkey.publicKey); - const owner2HotkeyAddress = convertPublicKeyToSs58(owner2Hotkey.publicKey); - const owner2ColdkeyAddress = convertPublicKeyToSs58(owner2Coldkey.publicKey); - const stakerColdkeyAddress = convertPublicKeyToSs58(stakerColdkey.publicKey); - - // Fund all accounts - await forceSetBalance(api, owner1HotkeyAddress); - await forceSetBalance(api, owner1ColdkeyAddress); - await forceSetBalance(api, owner2HotkeyAddress); - await forceSetBalance(api, owner2ColdkeyAddress); - await forceSetBalance(api, stakerColdkeyAddress); - - // Disable admin freeze window to allow enabling subtoken for ROOT - await sudoSetAdminFreezeWindow(api, 0); - log.info("Admin freeze window set to 0"); - - // Enable subtoken for ROOT subnet (required for staking on root) - const subtokenEnabledBefore = await isSubtokenEnabled(api, ROOT_NETUID); - if (!subtokenEnabledBefore) { - await sudoSetSubtokenEnabled(api, ROOT_NETUID, true); - const subtokenEnabledAfter = await isSubtokenEnabled(api, ROOT_NETUID); - log.info(`ROOT subtoken enabled: ${subtokenEnabledAfter}`); - expect(subtokenEnabledAfter).toBe(true); - } - - // Create TWO dynamic subnets - needed for root_sell_flag to become true - // root_sell_flag = sum(moving_prices) > 1.0 - // Each subnet's moving price approaches 1.0 via EMA, so 2 subnets can exceed threshold - const netuid1 = await addNewSubnetwork(api, owner1Hotkey, owner1Coldkey); - await startCall(api, netuid1, owner1Coldkey); - log.info(`Created subnet 1 with netuid: ${netuid1}`); - - const netuid2 = await addNewSubnetwork(api, owner2Hotkey, owner2Coldkey); - await startCall(api, netuid2, owner2Coldkey); - log.info(`Created subnet 2 with netuid: ${netuid2}`); - - // Set short tempo for faster emission distribution - await sudoSetTempo(api, netuid1, 1); - await sudoSetTempo(api, netuid2, 1); - log.info("Set tempo to 1 for both subnets"); - - // Set EMA price halving period to 1 for fast moving price convergence - // Formula: alpha = SubnetMovingAlpha * blocks/(blocks + halving_time) - // With halving_time=1: after 10 blocks, alpha ≈ 0.91, moving price ≈ 0.91 - // With 2 subnets at ~0.9 each, total > 1.0 enabling root_sell_flag - await sudoSetEmaPriceHalvingPeriod(api, netuid1, 1); - await sudoSetEmaPriceHalvingPeriod(api, netuid2, 1); - log.info("Set EMA halving period to 1 for fast price convergence"); - - // Set SubnetMovingAlpha to 1.0 (default is 0.000003 which is way too slow) - // I96F32 encoding: 1.0 * 2^32 = 4294967296 - const movingAlpha = BigInt(4294967296); // 1.0 in I96F32 - await sudoSetSubnetMovingAlpha(api, movingAlpha); - log.info("Set SubnetMovingAlpha to 1.0 for fast EMA convergence"); - - // Set threshold to 0 to allow claiming any amount - await sudoSetRootClaimThreshold(api, netuid1, 0n); - await sudoSetRootClaimThreshold(api, netuid2, 0n); - - // Add stake to ROOT subnet for the staker (makes them eligible for root dividends) - const rootStakeAmount = tao(100); - await addStake(api, stakerColdkey, owner1HotkeyAddress, ROOT_NETUID, rootStakeAmount); - log.info(`Added ${rootStakeAmount} stake to root subnet for staker`); - - // Verify root stake was added - const rootStake = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, ROOT_NETUID); - log.info(`Root stake: ${rootStake}`); - expect(rootStake, "Should have stake on root subnet").toBeGreaterThan(0n); - - // Add stake to both dynamic subnets (owner stake to enable emissions flow) - const subnetStakeAmount = tao(50); - await addStake(api, owner1Coldkey, owner1HotkeyAddress, netuid1, subnetStakeAmount); - await addStake(api, owner2Coldkey, owner2HotkeyAddress, netuid2, subnetStakeAmount); - log.info(`Added ${subnetStakeAmount} owner stake to subnets ${netuid1} and ${netuid2}`); - - // Get initial stake on subnet 1 for the staker (should be 0) - const stakerSubnetStakeBefore = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, netuid1); - log.info(`Staker subnet stake before claim: ${stakerSubnetStakeBefore}`); - - // Set root claim type to Keep (keep alpha on subnet instead of swapping to TAO) - await setRootClaimType(api, stakerColdkey, "Keep"); - const claimType = await getRootClaimType(api, stakerColdkeyAddress); - log.info(`Root claim type: ${claimType}`); - expect(claimType).toBe("Keep"); - - // Wait for blocks to: - // 1. Allow moving prices to converge (need sum > 1.0 for root_sell_flag) - // 2. Accumulate PendingRootAlphaDivs - // 3. Distribute emissions at tempo boundary - const blocksToWait = 25; - log.info(`Waiting for ${blocksToWait} blocks for moving prices to converge and emissions to accumulate...`); - await waitForBlocks(api, blocksToWait); - - // Debug: Check key storage values - const subnetTaoRoot = await getSubnetTAO(api, ROOT_NETUID); - const subnetTao1 = await getSubnetTAO(api, netuid1); - const subnetTao2 = await getSubnetTAO(api, netuid2); - log.info(`SubnetTAO - ROOT: ${subnetTaoRoot}, netuid1: ${subnetTao1}, netuid2: ${subnetTao2}`); - - const movingPrice1 = await getSubnetMovingPrice(api, netuid1); - const movingPrice2 = await getSubnetMovingPrice(api, netuid2); - log.info(`SubnetMovingPrice - netuid1: ${movingPrice1}, netuid2: ${movingPrice2}`); - // Note: Moving price is I96F32, so divide by 2^32 to get actual value - const mp1Float = Number(movingPrice1) / 2 ** 32; - const mp2Float = Number(movingPrice2) / 2 ** 32; - log.info(`SubnetMovingPrice (float) - netuid1: ${mp1Float}, netuid2: ${mp2Float}, sum: ${mp1Float + mp2Float}`); - - const pendingDivs1 = await getPendingRootAlphaDivs(api, netuid1); - const pendingDivs2 = await getPendingRootAlphaDivs(api, netuid2); - log.info(`PendingRootAlphaDivs - netuid1: ${pendingDivs1}, netuid2: ${pendingDivs2}`); - - const taoWeight = await getTaoWeight(api); - log.info(`TaoWeight: ${taoWeight}`); - - const alphaIn1 = await getSubnetAlphaIn(api, netuid1); - const alphaIn2 = await getSubnetAlphaIn(api, netuid2); - log.info(`SubnetAlphaIn - netuid1: ${alphaIn1}, netuid2: ${alphaIn2}`); - - const totalHotkeyAlpha1 = await getTotalHotkeyAlpha(api, owner1HotkeyAddress, netuid1); - log.info(`TotalHotkeyAlpha for hotkey1 on netuid1: ${totalHotkeyAlpha1}`); - - // Check if there are any claimable dividends - const claimable = await getRootClaimable(api, owner1HotkeyAddress); - const claimableStr = [...claimable.entries()].map(([k, v]) => `[${k}: ${v.toString()}]`).join(", "); - log.info(`RootClaimable entries for hotkey1: ${claimableStr || "(none)"}`); - - // Call claim_root to claim dividends for subnet 1 - await claimRoot(api, stakerColdkey, [netuid1]); - log.info("Called claim_root"); - - // Get stake on subnet 1 after claim - const stakerSubnetStakeAfter = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, netuid1); - log.info(`Staker subnet stake after claim: ${stakerSubnetStakeAfter}`); - - // Check RootClaimed value - const rootClaimed = await getRootClaimed(api, netuid1, owner1HotkeyAddress, stakerColdkeyAddress); - log.info(`RootClaimed value: ${rootClaimed}`); - - // Verify dividends were claimed - expect(stakerSubnetStakeAfter, "Stake should increase after claiming root dividends").toBeGreaterThan( - stakerSubnetStakeBefore, - ); - log.info(`✅ Root claim successful: stake increased from ${stakerSubnetStakeBefore} to ${stakerSubnetStakeAfter}`); - }); - - it("should claim root dividends with Swap type (swap to TAO on ROOT)", async () => { - const api = await getDevnetApi(); - - // Setup accounts - // - owner1Hotkey/owner1Coldkey: subnet 1 owner - // - owner2Hotkey/owner2Coldkey: subnet 2 owner (needed for root_sell_flag) - // - stakerColdkey: the coldkey that will stake on root and claim dividends - const owner1Hotkey = getRandomSubstrateKeypair(); - const owner1Coldkey = getRandomSubstrateKeypair(); - const owner2Hotkey = getRandomSubstrateKeypair(); - const owner2Coldkey = getRandomSubstrateKeypair(); - const stakerColdkey = getRandomSubstrateKeypair(); - const owner1HotkeyAddress = convertPublicKeyToSs58(owner1Hotkey.publicKey); - const owner1ColdkeyAddress = convertPublicKeyToSs58(owner1Coldkey.publicKey); - const owner2HotkeyAddress = convertPublicKeyToSs58(owner2Hotkey.publicKey); - const owner2ColdkeyAddress = convertPublicKeyToSs58(owner2Coldkey.publicKey); - const stakerColdkeyAddress = convertPublicKeyToSs58(stakerColdkey.publicKey); - - // Fund all accounts - await forceSetBalance(api, owner1HotkeyAddress); - await forceSetBalance(api, owner1ColdkeyAddress); - await forceSetBalance(api, owner2HotkeyAddress); - await forceSetBalance(api, owner2ColdkeyAddress); - await forceSetBalance(api, stakerColdkeyAddress); - - // Disable admin freeze window to allow enabling subtoken for ROOT - await sudoSetAdminFreezeWindow(api, 0); - log.info("Admin freeze window set to 0"); - - // Create TWO dynamic subnets - const netuid1 = await addNewSubnetwork(api, owner1Hotkey, owner1Coldkey); - await startCall(api, netuid1, owner1Coldkey); - log.info(`Created subnet 1 with netuid: ${netuid1}`); - - const netuid2 = await addNewSubnetwork(api, owner2Hotkey, owner2Coldkey); - await startCall(api, netuid2, owner2Coldkey); - log.info(`Created subnet 2 with netuid: ${netuid2}`); - - // Set short tempo for faster emission distribution - await sudoSetTempo(api, netuid1, 1); - await sudoSetTempo(api, netuid2, 1); - log.info("Set tempo to 1 for both subnets"); - - // Set EMA price halving period to 1 for fast moving price convergence - await sudoSetEmaPriceHalvingPeriod(api, netuid1, 1); - await sudoSetEmaPriceHalvingPeriod(api, netuid2, 1); - log.info("Set EMA halving period to 1 for fast price convergence"); - - // Set SubnetMovingAlpha to 1.0 (default is 0.000003 which is way too slow) - // I96F32 encoding: 1.0 * 2^32 = 4294967296 - const movingAlpha = BigInt(4294967296); // 1.0 in I96F32 - await sudoSetSubnetMovingAlpha(api, movingAlpha); - log.info("Set SubnetMovingAlpha to 1.0 for fast EMA convergence"); - - // Set threshold to 0 to allow claiming any amount - await sudoSetRootClaimThreshold(api, netuid1, 0n); - await sudoSetRootClaimThreshold(api, netuid2, 0n); - - // Add stake to ROOT subnet for the staker - const rootStakeAmount = tao(100); - await addStake(api, stakerColdkey, owner1HotkeyAddress, ROOT_NETUID, rootStakeAmount); - log.info(`Added ${rootStakeAmount} stake to root subnet for staker`); - - // Get initial ROOT stake - const rootStakeBefore = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, ROOT_NETUID); - log.info(`Root stake before: ${rootStakeBefore}`); - - // Add stake to both dynamic subnets (owner stake to enable emissions flow) - const subnetStakeAmount = tao(50); - await addStake(api, owner1Coldkey, owner1HotkeyAddress, netuid1, subnetStakeAmount); - await addStake(api, owner2Coldkey, owner2HotkeyAddress, netuid2, subnetStakeAmount); - log.info(`Added ${subnetStakeAmount} owner stake to subnets ${netuid1} and ${netuid2}`); - - // Set root claim type to Swap (swap alpha to TAO and add to ROOT stake) - await setRootClaimType(api, stakerColdkey, "Swap"); - const claimType = await getRootClaimType(api, stakerColdkeyAddress); - log.info(`Root claim type: ${claimType}`); - expect(claimType).toBe("Swap"); - - // Wait for blocks - const blocksToWait = 25; - log.info(`Waiting for ${blocksToWait} blocks for emissions to accumulate...`); - await waitForBlocks(api, blocksToWait); - - // Debug: Check moving prices - const movingPrice1 = await getSubnetMovingPrice(api, netuid1); - const movingPrice2 = await getSubnetMovingPrice(api, netuid2); - const mp1Float = Number(movingPrice1) / 2 ** 32; - const mp2Float = Number(movingPrice2) / 2 ** 32; - log.info(`SubnetMovingPrice (float) - netuid1: ${mp1Float}, netuid2: ${mp2Float}, sum: ${mp1Float + mp2Float}`); - - const pendingDivs1 = await getPendingRootAlphaDivs(api, netuid1); - log.info(`PendingRootAlphaDivs netuid1: ${pendingDivs1}`); - - // Check claimable - const claimable = await getRootClaimable(api, owner1HotkeyAddress); - const claimableStr = [...claimable.entries()].map(([k, v]) => `[${k}: ${v.toString()}]`).join(", "); - log.info(`RootClaimable entries for hotkey1: ${claimableStr || "(none)"}`); - - // Call claim_root - with Swap type, dividends are swapped to TAO and added to ROOT stake - await claimRoot(api, stakerColdkey, [netuid1]); - log.info("Called claim_root with Swap type"); - - // Get ROOT stake after claim - const rootStakeAfter = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, ROOT_NETUID); - log.info(`Root stake after claim: ${rootStakeAfter}`); - - // Check RootClaimed value - const rootClaimed = await getRootClaimed(api, netuid1, owner1HotkeyAddress, stakerColdkeyAddress); - log.info(`RootClaimed value: ${rootClaimed}`); - - // With Swap type, ROOT stake should increase (not dynamic subnet stake) - expect(rootStakeAfter, "ROOT stake should increase after claiming with Swap type").toBeGreaterThan(rootStakeBefore); - log.info(`✅ Root claim with Swap successful: ROOT stake increased from ${rootStakeBefore} to ${rootStakeAfter}`); - }); - - it("should handle claim_root when no dividends are available", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const coldkey = getRandomSubstrateKeypair(); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, coldkeyAddress); - - // Set root claim type to Keep - await setRootClaimType(api, coldkey, "Keep"); - - // Try to claim on a non-existent subnet (should succeed but be a no-op) - // According to Rust tests, claiming on unrelated subnets returns Ok but does nothing - await claimRoot(api, coldkey, [1]); - - log.info("✅ claim_root with no dividends executed successfully (no-op)."); - }); -}); diff --git a/e2e/staking/test/move-stake.test.ts b/e2e/staking/test/move-stake.test.ts deleted file mode 100644 index 292d8d8ded..0000000000 --- a/e2e/staking/test/move-stake.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - burnedRegister, - startCall, - addStake, - moveStake, - getStake, - getStakeRaw, - tao, - log, -} from "e2e-shared"; - -describe("▶ move_stake extrinsic", () => { - it("should move stake to another hotkey across subnets", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const originHotkey = getRandomSubstrateKeypair(); - const destinationHotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const originHotkeyAddress = convertPublicKeyToSs58(originHotkey.publicKey); - const destinationHotkeyAddress = convertPublicKeyToSs58(destinationHotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, originHotkeyAddress); - await forceSetBalance(api, destinationHotkeyAddress); - await forceSetBalance(api, coldkeyAddress); - - // Create first subnet with origin hotkey - const netuid1 = await addNewSubnetwork(api, originHotkey, coldkey); - await startCall(api, netuid1, coldkey); - - // Create second subnet with destination hotkey - const netuid2 = await addNewSubnetwork(api, destinationHotkey, coldkey); - await startCall(api, netuid2, coldkey); - - // Add stake to origin hotkey on first subnet - await addStake(api, coldkey, originHotkeyAddress, netuid1, tao(200)); - - // Get initial stakes (converted from U64F64 for display) - const originStakeBefore = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid1); - const destStakeBefore = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid2); - expect(originStakeBefore, "Origin hotkey should have stake before move").toBeGreaterThan(0n); - - log.info( - `Origin stake (netuid1) before: ${originStakeBefore}, Destination stake (netuid2) before: ${destStakeBefore}`, - ); - - // Move stake to destination hotkey on different subnet - // Use raw U64F64 value for the extrinsic - const originStakeRaw = await getStakeRaw(api, originHotkeyAddress, coldkeyAddress, netuid1); - const moveAmount = originStakeRaw / 2n; - await moveStake(api, coldkey, originHotkeyAddress, destinationHotkeyAddress, netuid1, netuid2, moveAmount); - - // Verify stakes changed - const originStakeAfter = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid1); - const destStakeAfter = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid2); - - log.info(`Origin stake (netuid1) after: ${originStakeAfter}, Destination stake (netuid2) after: ${destStakeAfter}`); - - expect(originStakeAfter, "Origin stake should decrease").toBeLessThan(originStakeBefore); - expect(destStakeAfter, "Destination stake should increase").toBeGreaterThan(destStakeBefore); - - log.info("✅ Successfully moved stake to another hotkey across subnets."); - }); - - it("should move stake to another hotkey on the same subnet", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const originHotkey = getRandomSubstrateKeypair(); - const destinationHotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const originHotkeyAddress = convertPublicKeyToSs58(originHotkey.publicKey); - const destinationHotkeyAddress = convertPublicKeyToSs58(destinationHotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, originHotkeyAddress); - await forceSetBalance(api, destinationHotkeyAddress); - await forceSetBalance(api, coldkeyAddress); - - // Create subnet with origin hotkey - const netuid = await addNewSubnetwork(api, originHotkey, coldkey); - await startCall(api, netuid, coldkey); - - // Register destination hotkey on the same subnet - await burnedRegister(api, netuid, destinationHotkeyAddress, coldkey); - - // Add stake to origin hotkey - await addStake(api, coldkey, originHotkeyAddress, netuid, tao(200)); - - // Get initial stakes (converted from U64F64 for display) - const originStakeBefore = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid); - const destStakeBefore = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid); - expect(originStakeBefore, "Origin hotkey should have stake before move").toBeGreaterThan(0n); - - log.info(`Origin stake before: ${originStakeBefore}, Destination stake before: ${destStakeBefore}`); - - // Move stake to destination hotkey on the same subnet - // Use raw U64F64 value for the extrinsic - const originStakeRaw = await getStakeRaw(api, originHotkeyAddress, coldkeyAddress, netuid); - const moveAmount = originStakeRaw / 2n; - await moveStake(api, coldkey, originHotkeyAddress, destinationHotkeyAddress, netuid, netuid, moveAmount); - - // Verify stakes changed - const originStakeAfter = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid); - const destStakeAfter = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid); - - log.info(`Origin stake after: ${originStakeAfter}, Destination stake after: ${destStakeAfter}`); - - expect(originStakeAfter, "Origin stake should decrease").toBeLessThan(originStakeBefore); - expect(destStakeAfter, "Destination stake should increase").toBeGreaterThan(destStakeBefore); - - log.info("✅ Successfully moved stake to another hotkey on the same subnet."); - }); -}); diff --git a/e2e/staking/test/remove-stake-full-limit.test.ts b/e2e/staking/test/remove-stake-full-limit.test.ts deleted file mode 100644 index 47af798512..0000000000 --- a/e2e/staking/test/remove-stake-full-limit.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { describe, it, expect, beforeAll } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - getBalance, - addNewSubnetwork, - burnedRegister, - startCall, - addStake, - removeStakeFullLimit, - getStake, - sudoSetTempo, - tao, - log, -} from "e2e-shared"; - -describe("▶ remove_stake_full_limit extrinsic", () => { - // Separate owner and staker hotkeys to avoid minimum owner stake retention - const ownerHotkey = getRandomSubstrateKeypair(); - const stakerHotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const ownerAddress = convertPublicKeyToSs58(ownerHotkey.publicKey); - const stakerAddress = convertPublicKeyToSs58(stakerHotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - let netuid: number; - - beforeAll(async () => { - const api = await getDevnetApi(); - await forceSetBalance(api, ownerAddress); - await forceSetBalance(api, stakerAddress); - await forceSetBalance(api, coldkeyAddress); - netuid = await addNewSubnetwork(api, ownerHotkey, coldkey); - await startCall(api, netuid, coldkey); - // Set high tempo to prevent emissions during test - await sudoSetTempo(api, netuid, 10000); - // Register staker hotkey (not the owner) - await burnedRegister(api, netuid, stakerAddress, coldkey); - }); - - it("should remove all stake with price limit", async () => { - const api = await getDevnetApi(); - - // Add stake first - await addStake(api, coldkey, stakerAddress, netuid, tao(100)); - - // Get initial stake and balance - const stakeBefore = await getStake(api, stakerAddress, coldkeyAddress, netuid); - const balanceBefore = await getBalance(api, coldkeyAddress); - log.info(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); - expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); - - // Remove all stake with a reasonable limit price (low limit to avoid slippage rejection) - // Using a low limit price (0.09 TAO per alpha) allows the transaction to succeed - const limitPrice = tao(1) / 10n; // 0.1 TAO - await removeStakeFullLimit(api, coldkey, stakerAddress, netuid, limitPrice); - - // Verify stake is zero (staker is not owner, so all stake can be removed) - const stakeAfter = await getStake(api, stakerAddress, coldkeyAddress, netuid); - const balanceAfter = await getBalance(api, coldkeyAddress); - log.info(`Stake after: ${stakeAfter}, Balance after: ${balanceAfter}`); - - expect(stakeAfter, "Stake should be zero after full removal").toBe(0n); - expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); - - log.info("✅ Successfully removed all stake with price limit."); - }); - - it("should remove all stake without price limit", async () => { - const api = await getDevnetApi(); - - // Add stake first - await addStake(api, coldkey, stakerAddress, netuid, tao(100)); - - // Get initial stake and balance - const stakeBefore = await getStake(api, stakerAddress, coldkeyAddress, netuid); - const balanceBefore = await getBalance(api, coldkeyAddress); - log.info(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); - expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); - - // Remove all stake without limit price (undefined = no slippage protection) - await removeStakeFullLimit(api, coldkey, stakerAddress, netuid, undefined); - - // Verify stake is zero (staker is not owner, so all stake can be removed) - const stakeAfter = await getStake(api, stakerAddress, coldkeyAddress, netuid); - const balanceAfter = await getBalance(api, coldkeyAddress); - log.info(`Stake after: ${stakeAfter}, Balance after: ${balanceAfter}`); - - expect(stakeAfter, "Stake should be zero after full removal").toBe(0n); - expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); - - log.info("✅ Successfully removed all stake without price limit."); - }); -}); diff --git a/e2e/staking/test/remove-stake-limit.test.ts b/e2e/staking/test/remove-stake-limit.test.ts deleted file mode 100644 index 9578fb8e3f..0000000000 --- a/e2e/staking/test/remove-stake-limit.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { describe, it, expect, beforeAll } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - getBalance, - addNewSubnetwork, - startCall, - addStake, - removeStakeLimit, - getStake, - tao, - log, -} from "e2e-shared"; - -describe("▶ remove_stake_limit extrinsic", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkeyAddress = convertPublicKeyToSs58(hotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - let netuid: number; - - beforeAll(async () => { - const api = await getDevnetApi(); - await forceSetBalance(api, hotkeyAddress); - await forceSetBalance(api, coldkeyAddress); - netuid = await addNewSubnetwork(api, hotkey, coldkey); - await startCall(api, netuid, coldkey); - }); - - it("should remove stake with price limit (allow partial)", async () => { - const api = await getDevnetApi(); - - // Add stake first (100 TAO like benchmark) - await addStake(api, coldkey, hotkeyAddress, netuid, tao(100)); - - // Get initial stake and balance - const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - const balanceBefore = await getBalance(api, coldkeyAddress); - log.info(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); - expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); - - // Remove stake with limit price and allow partial fills - const unstakeAmount = tao(30); - const limitPrice = tao(1); - await removeStakeLimit(api, coldkey, hotkeyAddress, netuid, unstakeAmount, limitPrice, true); - - // Verify balance increased (received TAO from unstaking) - const balanceAfter = await getBalance(api, coldkeyAddress); - expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); - - log.info("✅ Successfully removed stake with limit (allow partial)."); - }); - - it("should remove stake with price limit (fill or kill)", async () => { - const api = await getDevnetApi(); - - // Add stake first (100 TAO like benchmark) - await addStake(api, coldkey, hotkeyAddress, netuid, tao(100)); - - // Get initial stake and balance - const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - const balanceBefore = await getBalance(api, coldkeyAddress); - log.info(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); - expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); - - // Remove stake with limit price (fill or kill mode) - const unstakeAmount = tao(30); - const limitPrice = tao(1); - await removeStakeLimit(api, coldkey, hotkeyAddress, netuid, unstakeAmount, limitPrice, false); - - // Verify balance increased (received TAO from unstaking) - const balanceAfter = await getBalance(api, coldkeyAddress); - expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); - - log.info("✅ Successfully removed stake with limit (fill or kill)."); - }); -}); diff --git a/e2e/staking/test/remove-stake.test.ts b/e2e/staking/test/remove-stake.test.ts deleted file mode 100644 index db9f5aa150..0000000000 --- a/e2e/staking/test/remove-stake.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { describe, it, expect, beforeAll } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - getBalance, - addNewSubnetwork, - startCall, - addStake, - removeStake, - getStake, - getStakeRaw, - tao, - log, -} from "e2e-shared"; - -describe("▶ remove_stake extrinsic", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkeyAddress = convertPublicKeyToSs58(hotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - let netuid: number; - - beforeAll(async () => { - const api = await getDevnetApi(); - await forceSetBalance(api, hotkeyAddress); - await forceSetBalance(api, coldkeyAddress); - netuid = await addNewSubnetwork(api, hotkey, coldkey); - await startCall(api, netuid, coldkey); - }); - - it("should remove stake from a hotkey", async () => { - const api = await getDevnetApi(); - - // Add stake first - await addStake(api, coldkey, hotkeyAddress, netuid, tao(200)); - - // Get initial stake and balance (converted from U64F64 for display) - const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); - const balanceBefore = await getBalance(api, coldkeyAddress); - expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); - - // Remove stake (amount is in alpha units - use raw U64F64 value) - const stakeRaw = await getStakeRaw(api, hotkeyAddress, coldkeyAddress, netuid); - const unstakeAmount = stakeRaw / 2n; - await removeStake(api, coldkey, hotkeyAddress, netuid, unstakeAmount); - - // Verify balance increased (received TAO from unstaking) - const balanceAfter = await getBalance(api, coldkeyAddress); - expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); - - log.info("✅ Successfully removed stake."); - }); -}); diff --git a/e2e/staking/test/swap-stake-limit.test.ts b/e2e/staking/test/swap-stake-limit.test.ts deleted file mode 100644 index 316ddff051..0000000000 --- a/e2e/staking/test/swap-stake-limit.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - burnedRegister, - startCall, - addStake, - swapStakeLimit, - getStake, - getStakeRaw, - tao, - log, -} from "e2e-shared"; - -describe("▶ swap_stake_limit extrinsic", () => { - it("should swap stake with price limit (allow partial)", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const hotkey1 = getRandomSubstrateKeypair(); - const hotkey2 = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkey1Address = convertPublicKeyToSs58(hotkey1.publicKey); - const hotkey2Address = convertPublicKeyToSs58(hotkey2.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, hotkey1Address); - await forceSetBalance(api, hotkey2Address); - await forceSetBalance(api, coldkeyAddress); - - // Create first subnet - const netuid1 = await addNewSubnetwork(api, hotkey1, coldkey); - await startCall(api, netuid1, coldkey); - - // Create second subnet - const netuid2 = await addNewSubnetwork(api, hotkey2, coldkey); - await startCall(api, netuid2, coldkey); - - // Register hotkey1 on subnet2 so we can swap stake there - await burnedRegister(api, netuid2, hotkey1Address, coldkey); - - // Add stake to hotkey1 on subnet1 - await addStake(api, coldkey, hotkey1Address, netuid1, tao(100)); - - // Get initial stakes (converted from U64F64 for display) - const stake1Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); - const stake2Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); - expect(stake1Before, "Should have stake on subnet1 before swap").toBeGreaterThan(0n); - - log.info(`Stake on netuid1 before: ${stake1Before}, Stake on netuid2 before: ${stake2Before}`); - - // Swap stake with limit price (0.99 TAO relative price limit, allow partial fills) - // Use raw U64F64 value for the extrinsic - const stake1Raw = await getStakeRaw(api, hotkey1Address, coldkeyAddress, netuid1); - const swapAmount = stake1Raw / 2n; - const limitPrice = (tao(1) * 99n) / 100n; // 0.99 TAO - await swapStakeLimit(api, coldkey, hotkey1Address, netuid1, netuid2, swapAmount, limitPrice, true); - - // Verify stakes changed - const stake1After = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); - const stake2After = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); - - log.info(`Stake on netuid1 after: ${stake1After}, Stake on netuid2 after: ${stake2After}`); - - expect(stake1After, "Stake on subnet1 should decrease").toBeLessThan(stake1Before); - expect(stake2After, "Stake on subnet2 should increase").toBeGreaterThan(stake2Before); - - log.info("✅ Successfully swapped stake with price limit (allow partial)."); - }); - - it("should swap stake with price limit (fill or kill)", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const hotkey1 = getRandomSubstrateKeypair(); - const hotkey2 = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkey1Address = convertPublicKeyToSs58(hotkey1.publicKey); - const hotkey2Address = convertPublicKeyToSs58(hotkey2.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, hotkey1Address); - await forceSetBalance(api, hotkey2Address); - await forceSetBalance(api, coldkeyAddress); - - // Create first subnet - const netuid1 = await addNewSubnetwork(api, hotkey1, coldkey); - await startCall(api, netuid1, coldkey); - - // Create second subnet - const netuid2 = await addNewSubnetwork(api, hotkey2, coldkey); - await startCall(api, netuid2, coldkey); - - // Register hotkey1 on subnet2 so we can swap stake there - await burnedRegister(api, netuid2, hotkey1Address, coldkey); - - // Add stake to hotkey1 on subnet1 - await addStake(api, coldkey, hotkey1Address, netuid1, tao(100)); - - // Get initial stakes (converted from U64F64 for display) - const stake1Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); - const stake2Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); - expect(stake1Before, "Should have stake on subnet1 before swap").toBeGreaterThan(0n); - - log.info(`Stake on netuid1 before: ${stake1Before}, Stake on netuid2 before: ${stake2Before}`); - - // Swap stake with limit price (fill or kill mode - allow_partial = false) - // Use raw U64F64 value for the extrinsic - const stake1Raw = await getStakeRaw(api, hotkey1Address, coldkeyAddress, netuid1); - const swapAmount = stake1Raw / 2n; - const limitPrice = tao(1) / 10n; // 0.1 TAO - permissive limit to allow slippage - await swapStakeLimit(api, coldkey, hotkey1Address, netuid1, netuid2, swapAmount, limitPrice, false); - - // Verify stakes changed - const stake1After = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); - const stake2After = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); - - log.info(`Stake on netuid1 after: ${stake1After}, Stake on netuid2 after: ${stake2After}`); - - expect(stake1After, "Stake on subnet1 should decrease").toBeLessThan(stake1Before); - expect(stake2After, "Stake on subnet2 should increase").toBeGreaterThan(stake2Before); - - log.info("✅ Successfully swapped stake with price limit (fill or kill)."); - }); -}); diff --git a/e2e/staking/test/swap-stake.test.ts b/e2e/staking/test/swap-stake.test.ts deleted file mode 100644 index 44a818dd81..0000000000 --- a/e2e/staking/test/swap-stake.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - burnedRegister, - startCall, - addStake, - swapStake, - getStake, - getStakeRaw, - tao, - log, -} from "e2e-shared"; - -describe("▶ swap_stake extrinsic", () => { - it("should swap stake from one subnet to another", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const hotkey1 = getRandomSubstrateKeypair(); - const hotkey2 = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const hotkey1Address = convertPublicKeyToSs58(hotkey1.publicKey); - const hotkey2Address = convertPublicKeyToSs58(hotkey2.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, hotkey1Address); - await forceSetBalance(api, hotkey2Address); - await forceSetBalance(api, coldkeyAddress); - - // Create first subnet - const netuid1 = await addNewSubnetwork(api, hotkey1, coldkey); - await startCall(api, netuid1, coldkey); - - // Create second subnet - const netuid2 = await addNewSubnetwork(api, hotkey2, coldkey); - await startCall(api, netuid2, coldkey); - - // Register hotkey1 on subnet2 so we can swap stake there - await burnedRegister(api, netuid2, hotkey1Address, coldkey); - - // Add stake to hotkey1 on subnet1 - await addStake(api, coldkey, hotkey1Address, netuid1, tao(100)); - - // Get initial stakes - const stake1Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); - const stake2Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); - expect(stake1Before, "Should have stake on subnet1 before swap").toBeGreaterThan(0n); - - log.info(`Stake on netuid1 before: ${stake1Before}, Stake on netuid2 before: ${stake2Before}`); - - // Swap half the stake from subnet1 to subnet2 - // Use raw U64F64 value for the extrinsic - const stake1Raw = await getStakeRaw(api, hotkey1Address, coldkeyAddress, netuid1); - const swapAmount = stake1Raw / 2n; - await swapStake(api, coldkey, hotkey1Address, netuid1, netuid2, swapAmount); - - // Verify stakes changed - const stake1After = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); - const stake2After = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); - - log.info(`Stake on netuid1 after: ${stake1After}, Stake on netuid2 after: ${stake2After}`); - - // Note: hotkey1 is the owner of netuid1, so minimum owner stake may be retained - expect(stake1After, "Stake on subnet1 should decrease after swap").toBeLessThan(stake1Before); - expect(stake2After, "Stake on subnet2 should increase after swap").toBeGreaterThan(stake2Before); - - log.info("✅ Successfully swapped stake from one subnet to another."); - }); -}); diff --git a/e2e/staking/test/transfer-stake.test.ts b/e2e/staking/test/transfer-stake.test.ts deleted file mode 100644 index 330ca0b8d9..0000000000 --- a/e2e/staking/test/transfer-stake.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - startCall, - addStake, - transferStake, - getStake, - getStakeRaw, - tao, - log, -} from "e2e-shared"; - -describe("▶ transfer_stake extrinsic", () => { - it("should transfer stake to another coldkey across subnets", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const hotkey1 = getRandomSubstrateKeypair(); - const hotkey2 = getRandomSubstrateKeypair(); - const originColdkey = getRandomSubstrateKeypair(); - const destinationColdkey = getRandomSubstrateKeypair(); - const hotkey1Address = convertPublicKeyToSs58(hotkey1.publicKey); - const hotkey2Address = convertPublicKeyToSs58(hotkey2.publicKey); - const originColdkeyAddress = convertPublicKeyToSs58(originColdkey.publicKey); - const destinationColdkeyAddress = convertPublicKeyToSs58(destinationColdkey.publicKey); - - await forceSetBalance(api, hotkey1Address); - await forceSetBalance(api, hotkey2Address); - await forceSetBalance(api, originColdkeyAddress); - await forceSetBalance(api, destinationColdkeyAddress); - - // Create first subnet - const netuid1 = await addNewSubnetwork(api, hotkey1, originColdkey); - await startCall(api, netuid1, originColdkey); - - // Create second subnet - const netuid2 = await addNewSubnetwork(api, hotkey2, originColdkey); - await startCall(api, netuid2, originColdkey); - - // Add stake from origin coldkey on first subnet - await addStake(api, originColdkey, hotkey1Address, netuid1, tao(200)); - - // Get initial stakes (converted from U64F64 for display) - const originStakeBefore = await getStake(api, hotkey1Address, originColdkeyAddress, netuid1); - const destStakeBefore = await getStake(api, hotkey1Address, destinationColdkeyAddress, netuid2); - expect(originStakeBefore, "Origin should have stake before transfer").toBeGreaterThan(0n); - - log.info( - `Origin stake (netuid1) before: ${originStakeBefore}, Destination stake (netuid2) before: ${destStakeBefore}`, - ); - - // Transfer stake to destination coldkey on a different subnet - // Use raw U64F64 value for the extrinsic - const originStakeRaw = await getStakeRaw(api, hotkey1Address, originColdkeyAddress, netuid1); - const transferAmount = originStakeRaw / 2n; - await transferStake( - api, - originColdkey, - destinationColdkeyAddress, - hotkey1Address, - netuid1, - netuid2, - transferAmount, - ); - - // Verify stakes changed - const originStakeAfter = await getStake(api, hotkey1Address, originColdkeyAddress, netuid1); - const destStakeAfter = await getStake(api, hotkey1Address, destinationColdkeyAddress, netuid2); - - log.info(`Origin stake (netuid1) after: ${originStakeAfter}, Destination stake (netuid2) after: ${destStakeAfter}`); - - expect(originStakeAfter, "Origin stake should decrease").toBeLessThan(originStakeBefore); - expect(destStakeAfter, "Destination stake should increase").toBeGreaterThan(destStakeBefore); - - log.info("✅ Successfully transferred stake to another coldkey across subnets."); - }); - - it("should transfer stake to another coldkey", async () => { - const api = await getDevnetApi(); - - // Setup accounts - const hotkey = getRandomSubstrateKeypair(); - const originColdkey = getRandomSubstrateKeypair(); - const destinationColdkey = getRandomSubstrateKeypair(); - const hotkeyAddress = convertPublicKeyToSs58(hotkey.publicKey); - const originColdkeyAddress = convertPublicKeyToSs58(originColdkey.publicKey); - const destinationColdkeyAddress = convertPublicKeyToSs58(destinationColdkey.publicKey); - - await forceSetBalance(api, hotkeyAddress); - await forceSetBalance(api, originColdkeyAddress); - await forceSetBalance(api, destinationColdkeyAddress); - - // Create subnet - const netuid = await addNewSubnetwork(api, hotkey, originColdkey); - await startCall(api, netuid, originColdkey); - - // Add stake from origin coldkey - const stakeAmount = tao(100); - await addStake(api, originColdkey, hotkeyAddress, netuid, stakeAmount); - - // Get initial stake (converted from U64F64 for display) - const originStakeBefore = await getStake(api, hotkeyAddress, originColdkeyAddress, netuid); - expect(originStakeBefore, "Origin should have stake before transfer").toBeGreaterThan(0n); - - log.info(`Origin stake before: ${originStakeBefore}`); - - // Transfer stake to destination coldkey - // Use raw U64F64 value for the extrinsic, transfer half to avoid AmountTooLow error - const originStakeRaw = await getStakeRaw(api, hotkeyAddress, originColdkeyAddress, netuid); - const transferAmount = originStakeRaw / 2n; - await transferStake(api, originColdkey, destinationColdkeyAddress, hotkeyAddress, netuid, netuid, transferAmount); - - // Verify destination received stake - const originStakeAfter = await getStake(api, hotkeyAddress, originColdkeyAddress, netuid); - const destStakeAfter = await getStake(api, hotkeyAddress, destinationColdkeyAddress, netuid); - - log.info(`Origin stake after: ${originStakeAfter}, Destination stake after: ${destStakeAfter}`); - - expect(originStakeAfter, "Origin stake should decrease after transfer").toBeLessThan(originStakeBefore); - expect(destStakeAfter, "Destination stake should be non-zero after transfer").toBeGreaterThan(0n); - - log.info("✅ Successfully transferred stake to another coldkey."); - }); -}); diff --git a/e2e/staking/test/unstake-all-alpha.test.ts b/e2e/staking/test/unstake-all-alpha.test.ts deleted file mode 100644 index dd71a27192..0000000000 --- a/e2e/staking/test/unstake-all-alpha.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - addNewSubnetwork, - burnedRegister, - startCall, - addStake, - unstakeAllAlpha, - getStake, - sudoSetTempo, - tao, - log, -} from "e2e-shared"; - -describe("▶ unstake_all_alpha extrinsic", () => { - it("should unstake all alpha from multiple subnets and restake to root", async () => { - const api = await getDevnetApi(); - - // Setup accounts - // - owner1/coldkey: owns subnet 1 - // - owner2/coldkey: owns subnet 2 - // - stakerHotkey: staker (not owner) on both subnets - used for testing unstake_all_alpha - const owner1Hotkey = getRandomSubstrateKeypair(); - const owner2Hotkey = getRandomSubstrateKeypair(); - const stakerHotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const owner1Address = convertPublicKeyToSs58(owner1Hotkey.publicKey); - const owner2Address = convertPublicKeyToSs58(owner2Hotkey.publicKey); - const stakerAddress = convertPublicKeyToSs58(stakerHotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, owner1Address); - await forceSetBalance(api, owner2Address); - await forceSetBalance(api, stakerAddress); - await forceSetBalance(api, coldkeyAddress); - - // Create first subnet with owner1 - const netuid1 = await addNewSubnetwork(api, owner1Hotkey, coldkey); - await startCall(api, netuid1, coldkey); - - // Create second subnet with owner2 - const netuid2 = await addNewSubnetwork(api, owner2Hotkey, coldkey); - await startCall(api, netuid2, coldkey); - - // Set very high tempo to prevent emissions during test - await sudoSetTempo(api, netuid1, 10000); - await sudoSetTempo(api, netuid2, 10000); - - // Register stakerHotkey on both subnets (it's not the owner) - await burnedRegister(api, netuid1, stakerAddress, coldkey); - await burnedRegister(api, netuid2, stakerAddress, coldkey); - - // Add stake to both subnets using stakerHotkey (not the owner) - await addStake(api, coldkey, stakerAddress, netuid1, tao(100)); - await addStake(api, coldkey, stakerAddress, netuid2, tao(50)); - - // Verify stake was added to both subnets - const stake1Before = await getStake(api, stakerAddress, coldkeyAddress, netuid1); - const stake2Before = await getStake(api, stakerAddress, coldkeyAddress, netuid2); - - expect(stake1Before, "Should have stake in subnet 1 before unstake_all_alpha").toBeGreaterThan(0n); - expect(stake2Before, "Should have stake in subnet 2 before unstake_all_alpha").toBeGreaterThan(0n); - log.info(`Stake1 before: ${stake1Before}, Stake2 before: ${stake2Before}`); - - // Unstake all alpha - this removes stake from dynamic subnets and restakes to root - await unstakeAllAlpha(api, coldkey, stakerAddress); - - // Verify stakes are removed from both dynamic subnets - const stake1After = await getStake(api, stakerAddress, coldkeyAddress, netuid1); - const stake2After = await getStake(api, stakerAddress, coldkeyAddress, netuid2); - - log.info(`Stake1 after: ${stake1After}, Stake2 after: ${stake2After}`); - - // Since stakerHotkey is not the owner of either subnet, all stake should be removed - // High tempo prevents emissions during test, so expect exact zero - expect(stake1After, "Stake1 should be zero after unstake_all_alpha").toBe(0n); - expect(stake2After, "Stake2 should be zero after unstake_all_alpha").toBe(0n); - - log.info("✅ Successfully unstaked all alpha from multiple subnets to root."); - }); -}); diff --git a/e2e/staking/test/unstake-all.test.ts b/e2e/staking/test/unstake-all.test.ts deleted file mode 100644 index 146a2c3225..0000000000 --- a/e2e/staking/test/unstake-all.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - getDevnetApi, - getRandomSubstrateKeypair, - convertPublicKeyToSs58, - forceSetBalance, - getBalance, - addNewSubnetwork, - burnedRegister, - startCall, - addStake, - unstakeAll, - getStake, - sudoSetTempo, - tao, - log, -} from "e2e-shared"; - -describe("▶ unstake_all extrinsic", () => { - it("should unstake all from a hotkey across all subnets", async () => { - const api = await getDevnetApi(); - - // Setup accounts - // - owner1Hotkey/coldkey: owns subnet 1 - // - owner2Hotkey/coldkey: owns subnet 2 - // - stakerHotkey: staker (not owner) on both subnets - used for testing unstake_all - const owner1Hotkey = getRandomSubstrateKeypair(); - const owner2Hotkey = getRandomSubstrateKeypair(); - const stakerHotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const owner1Address = convertPublicKeyToSs58(owner1Hotkey.publicKey); - const owner2Address = convertPublicKeyToSs58(owner2Hotkey.publicKey); - const stakerAddress = convertPublicKeyToSs58(stakerHotkey.publicKey); - const coldkeyAddress = convertPublicKeyToSs58(coldkey.publicKey); - - await forceSetBalance(api, owner1Address); - await forceSetBalance(api, owner2Address); - await forceSetBalance(api, stakerAddress); - await forceSetBalance(api, coldkeyAddress); - - // Create first subnet with owner1 - const netuid1 = await addNewSubnetwork(api, owner1Hotkey, coldkey); - await startCall(api, netuid1, coldkey); - - // Create second subnet with owner2 - const netuid2 = await addNewSubnetwork(api, owner2Hotkey, coldkey); - await startCall(api, netuid2, coldkey); - - // Set high tempo to prevent emissions during test - await sudoSetTempo(api, netuid1, 10000); - await sudoSetTempo(api, netuid2, 10000); - - // Register stakerHotkey on both subnets (it's not the owner) - await burnedRegister(api, netuid1, stakerAddress, coldkey); - await burnedRegister(api, netuid2, stakerAddress, coldkey); - - // Add stake to both subnets using stakerHotkey (not the owner) - await addStake(api, coldkey, stakerAddress, netuid1, tao(100)); - await addStake(api, coldkey, stakerAddress, netuid2, tao(50)); - - // Verify stake was added to both subnets - const stake1Before = await getStake(api, stakerAddress, coldkeyAddress, netuid1); - const stake2Before = await getStake(api, stakerAddress, coldkeyAddress, netuid2); - const balanceBefore = await getBalance(api, coldkeyAddress); - - expect(stake1Before, "Should have stake in subnet 1 before unstake_all").toBeGreaterThan(0n); - expect(stake2Before, "Should have stake in subnet 2 before unstake_all").toBeGreaterThan(0n); - log.info(`Stake1 before: ${stake1Before}, Stake2 before: ${stake2Before}, Balance before: ${balanceBefore}`); - - // Unstake all - await unstakeAll(api, coldkey, stakerAddress); - - // Verify stakes are removed from both subnets and balance increased - const stake1After = await getStake(api, stakerAddress, coldkeyAddress, netuid1); - const stake2After = await getStake(api, stakerAddress, coldkeyAddress, netuid2); - const balanceAfter = await getBalance(api, coldkeyAddress); - - log.info(`Stake1 after: ${stake1After}, Stake2 after: ${stake2After}, Balance after: ${balanceAfter}`); - - // Since stakerHotkey is not the owner of either subnet, all stake should be removed - expect(stake1After, "Stake1 should be zero after unstake_all").toBe(0n); - expect(stake2After, "Stake2 should be zero after unstake_all").toBe(0n); - expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); - - log.info("✅ Successfully unstaked all from multiple subnets."); - }); -}); diff --git a/e2e/staking/tsconfig.json b/e2e/staking/tsconfig.json deleted file mode 100644 index c2f86d9e2c..0000000000 --- a/e2e/staking/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "types": ["node", "vitest/globals"] - } -} diff --git a/e2e/staking/vitest.config.ts b/e2e/staking/vitest.config.ts deleted file mode 100644 index c33905bdbe..0000000000 --- a/e2e/staking/vitest.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineConfig } from "vitest/config"; -import { BaseSequencer, type TestSpecification } from "vitest/node"; - -class AlphabeticalSequencer extends BaseSequencer { - async sort(files: TestSpecification[]): Promise<TestSpecification[]> { - return files.sort((a, b) => a.moduleId.localeCompare(b.moduleId)); - } -} - -export default defineConfig({ - test: { - globals: true, - testTimeout: 120_000, - hookTimeout: 300_000, - fileParallelism: false, - globalSetup: "./setup.ts", - include: ["test/**/*.test.ts"], - sequence: { - sequencer: AlphabeticalSequencer, - }, - }, -}); diff --git a/eco-tests/Cargo.toml b/eco-tests/Cargo.toml new file mode 100644 index 0000000000..c157ca281f --- /dev/null +++ b/eco-tests/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "eco-tests" +version = "0.1.0" +edition = "2024" +publish = false + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = ['cfg(substrate_runtime)'] } + +[lints.clippy] +arithmetic-side-effects = "deny" +expect-used = "deny" +indexing-slicing = "deny" +manual_inspect = "allow" +result_large_err = "allow" +type_complexity = "allow" +unwrap-used = "deny" +useless_conversion = "allow" + +[dependencies] +pallet-subtensor = { path = "../pallets/subtensor", default-features = false, features = ["std"] } +frame-support = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +frame-system = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +sp-core = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +sp-io = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +sp-runtime = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +sp-std = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +codec = { package = "parity-scale-codec", version = "3.7.5", default-features = false, features = ["derive", "std"] } +scale-info = { version = "2.11.2", default-features = false, features = ["derive", "std"] } +pallet-balances = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +pallet-scheduler = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +pallet-preimage = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +pallet-drand = { path = "../pallets/drand", default-features = false, features = ["std"] } +pallet-subtensor-swap = { path = "../pallets/swap", default-features = false, features = ["std"] } +pallet-crowdloan = { path = "../pallets/crowdloan", default-features = false, features = ["std"] } +pallet-subtensor-proxy = { path = "../pallets/proxy", default-features = false, features = ["std"] } +pallet-subtensor-utility = { path = "../pallets/utility", default-features = false, features = ["std"] } +pallet-shield = { path = "../pallets/shield", default-features = false, features = ["std"] } +subtensor-runtime-common = { path = "../common", default-features = false, features = ["std"] } +subtensor-swap-interface = { path = "../pallets/swap-interface", default-features = false, features = ["std"] } +share-pool = { path = "../primitives/share-pool", default-features = false, features = ["std"] } +safe-math = { path = "../primitives/safe-math", default-features = false, features = ["std"] } +log = { version = "0.4.21", default-features = false, features = ["std"] } +approx = "0.5" +sp-tracing = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "fb1dd20df37710800aa284ac49bb26193d5539ee", default-features = false, features = ["std"] } +tracing = "0.1" +tracing-log = "0.2" +tracing-subscriber = { version = "=0.3.18", features = ["fmt", "env-filter"] } +rand = { version = "0.10.0", default-features = false, features = ["std", "thread_rng"] } +hex-literal = "0.4.1" + +[patch.crates-io] +w3f-bls = { git = "https://github.com/opentensor/bls", branch = "fix-no-std" } diff --git a/eco-tests/src/helpers.rs b/eco-tests/src/helpers.rs new file mode 100644 index 0000000000..ca3ea4dc0b --- /dev/null +++ b/eco-tests/src/helpers.rs @@ -0,0 +1,422 @@ +#![allow( + dead_code, + clippy::arithmetic_side_effects, + clippy::expect_used, + clippy::unwrap_used +)] + +use frame_support::{assert_ok, pallet_prelude::Zero, traits::Hooks}; +use frame_system::RawOrigin; +use pallet_subtensor::utils::rate_limiting::TransactionType; +use pallet_subtensor::*; +use share_pool::SafeFloat; +use sp_core::{Get, H256, U256}; +use sp_runtime::{BuildStorage, Saturating}; +use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; +use subtensor_swap_interface::{Order, SwapHandler}; + +use super::mock::*; + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext(block_number: BlockNumber) -> sp_io::TestExternalities { + init_logs_for_tests(); + let t = frame_system::GenesisConfig::<Test>::default() + .build_storage() + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(block_number)); + ext +} + +pub fn test_ext_with_balances(balances: Vec<(U256, u128)>) -> sp_io::TestExternalities { + init_logs_for_tests(); + let mut t = frame_system::GenesisConfig::<Test>::default() + .build_storage() + .unwrap(); + + pallet_balances::GenesisConfig::<Test> { + balances: balances + .iter() + .map(|(a, b)| (*a, TaoBalance::from(*b as u64))) + .collect::<Vec<(U256, TaoBalance)>>(), + dev_accounts: None, + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() +} + +pub fn step_block(n: u16) { + for _ in 0..n { + Scheduler::on_finalize(System::block_number()); + Proxy::on_finalize(System::block_number()); + SubtensorModule::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + SubtensorModule::on_initialize(System::block_number()); + Scheduler::on_initialize(System::block_number()); + } +} + +pub fn run_to_block(n: u64) { + run_to_block_ext(n, false) +} + +pub fn run_to_block_ext(n: u64, enable_events: bool) { + while System::block_number() < n { + Scheduler::on_finalize(System::block_number()); + SubtensorModule::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + if !enable_events { + System::events().iter().for_each(|event| { + log::info!("Event: {:?}", event.event); + }); + System::reset_events(); + } + SubtensorModule::on_initialize(System::block_number()); + Scheduler::on_initialize(System::block_number()); + } +} + +pub fn next_block_no_epoch(netuid: NetUid) -> u64 { + // high tempo to skip automatic epochs in on_initialize + let high_tempo: u16 = u16::MAX - 1; + let old_tempo: u16 = SubtensorModule::get_tempo(netuid); + + SubtensorModule::set_tempo(netuid, high_tempo); + let new_block = next_block(); + SubtensorModule::set_tempo(netuid, old_tempo); + + new_block +} + +pub fn run_to_block_no_epoch(netuid: NetUid, n: u64) { + // high tempo to skip automatic epochs in on_initialize + let high_tempo: u16 = u16::MAX - 1; + let old_tempo: u16 = SubtensorModule::get_tempo(netuid); + + SubtensorModule::set_tempo(netuid, high_tempo); + run_to_block(n); + SubtensorModule::set_tempo(netuid, old_tempo); +} + +pub fn step_epochs(count: u16, netuid: NetUid) { + for _ in 0..count { + let blocks_to_next_epoch = SubtensorModule::blocks_until_next_epoch( + netuid, + SubtensorModule::get_tempo(netuid), + SubtensorModule::get_current_block_as_u64(), + ); + log::info!("Blocks to next epoch: {blocks_to_next_epoch:?}"); + step_block(blocks_to_next_epoch as u16); + + assert!(SubtensorModule::should_run_epoch( + netuid, + SubtensorModule::get_current_block_as_u64() + )); + step_block(1); + } +} + +/// Increments current block by 1, running all hooks associated with doing so, and asserts +/// that the block number was in fact incremented. +/// +/// Returns the new block number. +pub fn next_block() -> u64 { + let mut block = System::block_number(); + block += 1; + run_to_block(block); + assert_eq!(System::block_number(), block); + block +} + +pub fn register_ok_neuron( + netuid: NetUid, + hotkey_account_id: U256, + coldkey_account_id: U256, + start_nonce: u64, +) { + let block_number: u64 = SubtensorModule::get_current_block_as_u64(); + let (nonce, work): (u64, Vec<u8>) = SubtensorModule::create_work_for_block_number( + netuid, + block_number, + start_nonce, + &hotkey_account_id, + ); + let result = SubtensorModule::register( + <<Test as frame_system::Config>::RuntimeOrigin>::signed(hotkey_account_id), + netuid, + block_number, + nonce, + work, + hotkey_account_id, + coldkey_account_id, + ); + assert_ok!(result); + log::info!( + "Register ok neuron: netuid: {netuid:?}, coldkey: {hotkey_account_id:?}, hotkey: {coldkey_account_id:?}" + ); +} + +pub fn add_network(netuid: NetUid, tempo: u16, _modality: u16) { + SubtensorModule::init_new_network(netuid, tempo); + SubtensorModule::set_network_registration_allowed(netuid, true); + SubtensorModule::set_network_pow_registration_allowed(netuid, true); + FirstEmissionBlockNumber::<Test>::insert(netuid, 1); + SubtokenEnabled::<Test>::insert(netuid, true); +} + +pub fn add_network_without_emission_block(netuid: NetUid, tempo: u16, _modality: u16) { + SubtensorModule::init_new_network(netuid, tempo); + SubtensorModule::set_network_registration_allowed(netuid, true); + SubtensorModule::set_network_pow_registration_allowed(netuid, true); +} + +pub fn add_network_disable_subtoken(netuid: NetUid, tempo: u16, _modality: u16) { + SubtensorModule::init_new_network(netuid, tempo); + SubtensorModule::set_network_registration_allowed(netuid, true); + SubtensorModule::set_network_pow_registration_allowed(netuid, true); + SubtokenEnabled::<Test>::insert(netuid, false); +} + +pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> NetUid { + let netuid = SubtensorModule::get_next_netuid(); + let lock_cost = SubtensorModule::get_network_lock_cost(); + SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost.into()); + TotalIssuance::<Test>::mutate(|total_issuance| { + *total_issuance = total_issuance.saturating_add(lock_cost); + }); + + assert_ok!(SubtensorModule::register_network( + RawOrigin::Signed(*coldkey).into(), + *hotkey + )); + NetworkRegistrationAllowed::<Test>::insert(netuid, true); + NetworkPowRegistrationAllowed::<Test>::insert(netuid, true); + FirstEmissionBlockNumber::<Test>::insert(netuid, 0); + SubtokenEnabled::<Test>::insert(netuid, true); + netuid +} + +pub fn add_dynamic_network_without_emission_block(hotkey: &U256, coldkey: &U256) -> NetUid { + let netuid = SubtensorModule::get_next_netuid(); + let lock_cost = SubtensorModule::get_network_lock_cost(); + SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost.into()); + TotalIssuance::<Test>::mutate(|total_issuance| { + *total_issuance = total_issuance.saturating_add(lock_cost); + }); + + assert_ok!(SubtensorModule::register_network( + RawOrigin::Signed(*coldkey).into(), + *hotkey + )); + NetworkRegistrationAllowed::<Test>::insert(netuid, true); + NetworkPowRegistrationAllowed::<Test>::insert(netuid, true); + netuid +} + +pub fn add_dynamic_network_disable_commit_reveal(hotkey: &U256, coldkey: &U256) -> NetUid { + let netuid = add_dynamic_network(hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, false); + netuid +} + +pub fn add_network_disable_commit_reveal(netuid: NetUid, tempo: u16, _modality: u16) { + add_network(netuid, tempo, _modality); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, false); +} + +// Helper function to set up a neuron with stake +pub fn setup_neuron_with_stake(netuid: NetUid, hotkey: U256, coldkey: U256, stake: TaoBalance) { + register_ok_neuron(netuid, hotkey, coldkey, stake.into()); + increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake, netuid); +} + +pub fn wait_set_pending_children_cooldown(netuid: NetUid) { + let cooldown = DefaultPendingCooldown::<Test>::get(); + step_block(cooldown as u16); // Wait for cooldown to pass + step_epochs(1, netuid); // Run next epoch +} + +pub fn wait_and_set_pending_children(netuid: NetUid) { + let original_block = System::block_number(); + wait_set_pending_children_cooldown(netuid); + SubtensorModule::do_set_pending_children(netuid); + System::set_block_number(original_block); +} + +pub fn mock_schedule_children( + coldkey: &U256, + parent: &U256, + netuid: NetUid, + child_vec: &[(u64, U256)], +) { + // Set minimum stake for setting children + StakeThreshold::<Test>::put(0); + + // Set initial parent-child relationship + assert_ok!(SubtensorModule::do_schedule_children( + RuntimeOrigin::signed(*coldkey), + *parent, + netuid, + child_vec.to_vec() + )); +} + +pub fn mock_set_children(coldkey: &U256, parent: &U256, netuid: NetUid, child_vec: &[(u64, U256)]) { + mock_schedule_children(coldkey, parent, netuid, child_vec); + wait_and_set_pending_children(netuid); +} + +pub fn mock_set_children_no_epochs(netuid: NetUid, parent: &U256, child_vec: &[(u64, U256)]) { + let backup_block = SubtensorModule::get_current_block_as_u64(); + PendingChildKeys::<Test>::insert(netuid, parent, (child_vec, 0)); + System::set_block_number(1); + SubtensorModule::do_set_pending_children(netuid); + System::set_block_number(backup_block); +} + +// Helper function to wait for the rate limit +pub fn step_rate_limit(transaction_type: &TransactionType, netuid: NetUid) { + // Check rate limit + let limit = transaction_type.rate_limit_on_subnet::<Test>(netuid); + + // Step that many blocks + step_block(limit as u16); +} + +/// Helper function to increase stake on a coldkey-hotkey pair via the public add_stake extrinsic. +pub fn increase_stake_on_coldkey_hotkey_account( + coldkey: &U256, + hotkey: &U256, + tao_staked: TaoBalance, + netuid: NetUid, +) { + // Ensure the coldkey has enough balance + SubtensorModule::add_balance_to_coldkey_account(coldkey, tao_staked.into()); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(*coldkey), + *hotkey, + netuid, + tao_staked, + )); +} + +/// Increases the stake on the hotkey account under its owning coldkey. +/// +/// # Arguments +/// * `hotkey` - The hotkey account ID. +/// * `increment` - The amount to be incremented. +pub fn increase_stake_on_hotkey_account(hotkey: &U256, increment: TaoBalance, netuid: NetUid) { + increase_stake_on_coldkey_hotkey_account( + &SubtensorModule::get_owning_coldkey_for_hotkey(hotkey), + hotkey, + increment, + netuid, + ); +} + +pub fn remove_stake_rate_limit_for_tests(hotkey: &U256, coldkey: &U256, netuid: NetUid) { + StakingOperationRateLimiter::<Test>::remove((hotkey, coldkey, netuid)); +} + +pub fn setup_reserves(netuid: NetUid, tao: TaoBalance, alpha: AlphaBalance) { + SubnetTAO::<Test>::set(netuid, tao); + SubnetAlphaIn::<Test>::set(netuid, alpha); +} + +pub fn swap_tao_to_alpha(netuid: NetUid, tao: TaoBalance) -> (AlphaBalance, u64) { + if netuid.is_root() { + return (tao.to_u64().into(), 0); + } + + let order = GetAlphaForTao::<Test>::with_amount(tao); + let result = <Test as pallet_subtensor::Config>::SwapInterface::swap( + netuid.into(), + order, + <Test as pallet_subtensor::Config>::SwapInterface::max_price(), + false, + true, + ); + + assert_ok!(&result); + + let result = result.unwrap(); + + // we don't want to have silent 0 comparisons in tests + assert!(result.amount_paid_out > AlphaBalance::ZERO); + + (result.amount_paid_out, result.fee_paid.into()) +} + +pub fn swap_alpha_to_tao_ext( + netuid: NetUid, + alpha: AlphaBalance, + drop_fees: bool, +) -> (TaoBalance, u64) { + if netuid.is_root() { + return (alpha.to_u64().into(), 0); + } + + println!( + "<Test as pallet_subtensor::Config>::SwapInterface::min_price() = {:?}", + <Test as pallet_subtensor::Config>::SwapInterface::min_price::<TaoBalance>() + ); + + let order = GetTaoForAlpha::<Test>::with_amount(alpha); + let result = <Test as pallet_subtensor::Config>::SwapInterface::swap( + netuid.into(), + order, + <Test as pallet_subtensor::Config>::SwapInterface::min_price(), + drop_fees, + true, + ); + + assert_ok!(&result); + + let result = result.unwrap(); + + // we don't want to have silent 0 comparisons in tests + assert!(!result.amount_paid_out.is_zero()); + + (result.amount_paid_out, result.fee_paid.into()) +} + +pub fn swap_alpha_to_tao(netuid: NetUid, alpha: AlphaBalance) -> (TaoBalance, u64) { + swap_alpha_to_tao_ext(netuid, alpha, false) +} + +pub fn last_event() -> RuntimeEvent { + System::events().pop().expect("RuntimeEvent expected").event +} + +pub fn assert_last_event<T: frame_system::pallet::Config>( + generic_event: <T as frame_system::pallet::Config>::RuntimeEvent, +) { + frame_system::Pallet::<T>::assert_last_event(generic_event.into()); +} + +pub fn commit_dummy(who: U256, netuid: NetUid) { + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + + // any 32‑byte value is fine; hash is never opened + let hash = H256::from_low_u64_be(0xDEAD_BEEF); + assert_ok!(SubtensorModule::do_commit_weights( + RuntimeOrigin::signed(who), + netuid, + hash + )); +} + +pub fn sf_to_u128(sf: &SafeFloat) -> u128 { + let alpha_f64: f64 = sf.into(); + alpha_f64 as u128 +} + +pub fn sf_from_u64(val: u64) -> SafeFloat { + SafeFloat::from(val) +} diff --git a/eco-tests/src/lib.rs b/eco-tests/src/lib.rs new file mode 100644 index 0000000000..d7d60aca2b --- /dev/null +++ b/eco-tests/src/lib.rs @@ -0,0 +1,6 @@ +#[cfg(test)] +mod helpers; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; diff --git a/eco-tests/src/mock.rs b/eco-tests/src/mock.rs new file mode 100644 index 0000000000..b89378dbed --- /dev/null +++ b/eco-tests/src/mock.rs @@ -0,0 +1,587 @@ +#![allow( + dead_code, + clippy::arithmetic_side_effects, + clippy::expect_used, + clippy::unwrap_used +)] + +use core::num::NonZeroU64; + +use frame_support::dispatch::DispatchResult; +use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth, InstanceFilter}; +use frame_support::weights::Weight; +use frame_support::weights::constants::RocksDbWeight; +use frame_support::{PalletId, derive_impl}; +use frame_support::{parameter_types, traits::PrivilegeCmp}; +use frame_system as system; +use frame_system::{EnsureRoot, limits, offchain::CreateTransactionBase}; +use pallet_subtensor::*; +use pallet_subtensor_proxy as pallet_proxy; +use pallet_subtensor_utility as pallet_utility; +use sp_core::{ConstU64, H256, U256, offchain::KeyTypeId}; +use sp_runtime::Perbill; +use sp_runtime::{ + Percent, + traits::{BlakeTwo256, IdentityLookup}, +}; +use sp_std::{cell::RefCell, cmp::Ordering, sync::OnceLock}; +use sp_tracing::tracing_subscriber; +use subtensor_runtime_common::{AuthorshipInfo, NetUid, TaoBalance}; +use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; +type Block = frame_system::mocking::MockBlock<Test>; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system = 1, + Balances: pallet_balances = 2, + Shield: pallet_shield = 3, + SubtensorModule: pallet_subtensor::pallet = 4, + Utility: pallet_utility = 5, + Scheduler: pallet_scheduler = 6, + Preimage: pallet_preimage = 7, + Drand: pallet_drand = 8, + Swap: pallet_subtensor_swap = 9, + Crowdloan: pallet_crowdloan = 10, + Proxy: pallet_subtensor_proxy = 11, + } +); + +pub type SubtensorCall = pallet_subtensor::Call<Test>; + +pub type SubtensorEvent = pallet_subtensor::Event<Test>; + +pub type BalanceCall = pallet_balances::Call<Test>; + +pub type TestRuntimeCall = frame_system::Call<Test>; + +pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"test"); + +pub type AccountId = U256; + +// The address format for describing accounts. +pub type Address = AccountId; + +// Balance of an account. +pub type Balance = TaoBalance; + +// An index to a block. +pub type BlockNumber = u64; + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = (); +} + +impl pallet_shield::Config for Test { + type AuthorityId = sp_core::sr25519::Public; + type FindAuthors = (); +} + +pub struct NoNestingCallFilter; + +impl Contains<RuntimeCall> for NoNestingCallFilter { + fn contains(call: &RuntimeCall) -> bool { + match call { + RuntimeCall::Utility(inner) => { + let calls = match inner { + pallet_utility::Call::force_batch { calls } => calls, + pallet_utility::Call::batch { calls } => calls, + pallet_utility::Call::batch_all { calls } => calls, + _ => &Vec::new(), + }; + + !calls.iter().any(|call| { + matches!(call, RuntimeCall::Utility(inner) if matches!(inner, pallet_utility::Call::force_batch { .. } | pallet_utility::Call::batch_all { .. } | pallet_utility::Call::batch { .. })) + }) + } + _ => true, + } + } +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = InsideBoth<Everything, NoNestingCallFilter>; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = RocksDbWeight; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = U256; + type Lookup = IdentityLookup<Self::AccountId>; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData<TaoBalance>; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + type Nonce = u64; + type Block = Block; + type DispatchGuard = pallet_subtensor::CheckColdkeySwap<Test>; +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +pub const MOCK_BLOCK_BUILDER: u64 = 12345u64; + +pub struct MockAuthorshipProvider; + +impl AuthorshipInfo<U256> for MockAuthorshipProvider { + fn author() -> Option<U256> { + Some(U256::from(MOCK_BLOCK_BUILDER)) + } +} + +parameter_types! { + pub const InitialMinAllowedWeights: u16 = 0; + pub const InitialEmissionValue: u16 = 0; + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::with_sensible_defaults( + Weight::from_parts(2_000_000_000_000, u64::MAX), + Perbill::from_percent(75), + ); + pub const ExistentialDeposit: Balance = TaoBalance::new(1); + pub const TransactionByteFee: Balance = TaoBalance::new(100); + pub const SDebug:u64 = 1; + pub const InitialRho: u16 = 30; + pub const InitialAlphaSigmoidSteepness: i16 = 1000; + pub const InitialKappa: u16 = 32_767; + pub const InitialTempo: u16 = 360; + pub const SelfOwnership: u64 = 2; + pub const InitialImmunityPeriod: u16 = 2; + pub const InitialMinAllowedUids: u16 = 2; + pub const InitialMaxAllowedUids: u16 = 256; + pub const InitialBondsMovingAverage: u64 = 900_000; + pub const InitialBondsPenalty:u16 = u16::MAX; + pub const InitialBondsResetOn: bool = false; + pub const InitialStakePruningMin: u16 = 0; + pub const InitialFoundationDistribution: u64 = 0; + pub const InitialDefaultDelegateTake: u16 = 11_796; // 18%, same as in production + pub const InitialMinDelegateTake: u16 = 5_898; // 9%; + pub const InitialDefaultChildKeyTake: u16 = 0 ;// 0 % + pub const InitialMinChildKeyTake: u16 = 0; // 0 %; + pub const InitialMaxChildKeyTake: u16 = 11_796; // 18 %; + pub const InitialWeightsVersionKey: u16 = 0; + pub const InitialServingRateLimit: u64 = 0; // No limit. + pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing + pub const InitialTxDelegateTakeRateLimit: u64 = 1; // 1 block take rate limit for testing + pub const InitialTxChildKeyTakeRateLimit: u64 = 1; // 1 block take rate limit for testing + pub const InitialBurn: u64 = 0; + pub const InitialMinBurn: u64 = 500_000; + pub const InitialMaxBurn: u64 = 1_000_000_000; + pub const MinBurnUpperBound: TaoBalance = TaoBalance::new(1_000_000_000); // 1 TAO + pub const MaxBurnLowerBound: TaoBalance = TaoBalance::new(100_000_000); // 0.1 TAO + pub const InitialValidatorPruneLen: u64 = 0; + pub const InitialScalingLawPower: u16 = 50; + pub const InitialMaxAllowedValidators: u16 = 100; + pub const InitialIssuance: u64 = 0; + pub const InitialDifficulty: u64 = 10000; + pub const InitialActivityCutoff: u16 = 5000; + pub const InitialAdjustmentInterval: u16 = 100; + pub const InitialAdjustmentAlpha: u64 = 0; // no weight to previous value. + pub const InitialMaxRegistrationsPerBlock: u16 = 3; + pub const InitialTargetRegistrationsPerInterval: u16 = 2; + pub const InitialPruningScore : u16 = u16::MAX; + pub const InitialRegistrationRequirement: u16 = u16::MAX; // Top 100% + pub const InitialMinDifficulty: u64 = 1; + pub const InitialMaxDifficulty: u64 = u64::MAX; + pub const InitialRAORecycledForRegistration: u64 = 0; + pub const InitialNetworkImmunityPeriod: u64 = 1_296_000; + pub const InitialNetworkMinLockCost: u64 = 100_000_000_000; + pub const InitialSubnetOwnerCut: u16 = 0; // 0%. 100% of rewards go to validators + miners. + pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks. + pub const InitialNetworkRateLimit: u64 = 0; + pub const InitialKeySwapCost: u64 = 1_000_000_000; + pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default + pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default + pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn + pub const InitialYuma3On: bool = false; // Default value for Yuma3On + pub const InitialColdkeySwapAnnouncementDelay: u64 = 50; + pub const InitialColdkeySwapReannouncementDelay: u64 = 10; + pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days + pub const InitialTaoWeight: u64 = 0; // 100% global weight. + pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks + pub const InitialStartCallDelay: u64 = 0; // 0 days + pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; + pub const HotkeySwapOnSubnetInterval: u64 = 15; // 15 block, should be bigger than subnet number, then trigger clean up for all subnets + pub const MaxContributorsPerLeaseToRemove: u32 = 3; + pub const LeaseDividendsDistributionInterval: u32 = 100; + pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); + pub const EvmKeyAssociateRateLimit: u64 = 10; +} + +impl pallet_subtensor::Config for Test { + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type InitialIssuance = InitialIssuance; + type SudoRuntimeCall = TestRuntimeCall; + type Scheduler = Scheduler; + type InitialMinAllowedWeights = InitialMinAllowedWeights; + type InitialEmissionValue = InitialEmissionValue; + type InitialTempo = InitialTempo; + type InitialDifficulty = InitialDifficulty; + type InitialAdjustmentInterval = InitialAdjustmentInterval; + type InitialAdjustmentAlpha = InitialAdjustmentAlpha; + type InitialTargetRegistrationsPerInterval = InitialTargetRegistrationsPerInterval; + type InitialRho = InitialRho; + type InitialAlphaSigmoidSteepness = InitialAlphaSigmoidSteepness; + type InitialKappa = InitialKappa; + type InitialMinAllowedUids = InitialMinAllowedUids; + type InitialMaxAllowedUids = InitialMaxAllowedUids; + type InitialValidatorPruneLen = InitialValidatorPruneLen; + type InitialScalingLawPower = InitialScalingLawPower; + type InitialImmunityPeriod = InitialImmunityPeriod; + type InitialActivityCutoff = InitialActivityCutoff; + type InitialMaxRegistrationsPerBlock = InitialMaxRegistrationsPerBlock; + type InitialPruningScore = InitialPruningScore; + type InitialBondsMovingAverage = InitialBondsMovingAverage; + type InitialBondsPenalty = InitialBondsPenalty; + type InitialBondsResetOn = InitialBondsResetOn; + type InitialMaxAllowedValidators = InitialMaxAllowedValidators; + type InitialDefaultDelegateTake = InitialDefaultDelegateTake; + type InitialMinDelegateTake = InitialMinDelegateTake; + type InitialDefaultChildKeyTake = InitialDefaultChildKeyTake; + type InitialMinChildKeyTake = InitialMinChildKeyTake; + type InitialMaxChildKeyTake = InitialMaxChildKeyTake; + type InitialTxChildKeyTakeRateLimit = InitialTxChildKeyTakeRateLimit; + type InitialWeightsVersionKey = InitialWeightsVersionKey; + type InitialMaxDifficulty = InitialMaxDifficulty; + type InitialMinDifficulty = InitialMinDifficulty; + type InitialServingRateLimit = InitialServingRateLimit; + type InitialTxRateLimit = InitialTxRateLimit; + type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; + type InitialBurn = InitialBurn; + type InitialMaxBurn = InitialMaxBurn; + type InitialMinBurn = InitialMinBurn; + type MinBurnUpperBound = MinBurnUpperBound; + type MaxBurnLowerBound = MaxBurnLowerBound; + type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; + type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; + type InitialNetworkMinLockCost = InitialNetworkMinLockCost; + type InitialSubnetOwnerCut = InitialSubnetOwnerCut; + type InitialNetworkLockReductionInterval = InitialNetworkLockReductionInterval; + type InitialNetworkRateLimit = InitialNetworkRateLimit; + type KeySwapCost = InitialKeySwapCost; + type AlphaHigh = InitialAlphaHigh; + type AlphaLow = InitialAlphaLow; + type LiquidAlphaOn = InitialLiquidAlphaOn; + type Yuma3On = InitialYuma3On; + type Preimages = Preimage; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; + type InitialColdkeySwapReannouncementDelay = InitialColdkeySwapReannouncementDelay; + type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; + type InitialTaoWeight = InitialTaoWeight; + type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; + type InitialStartCallDelay = InitialStartCallDelay; + type SwapInterface = pallet_subtensor_swap::Pallet<Self>; + type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost; + type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval; + type ProxyInterface = FakeProxier; + type LeaseDividendsDistributionInterval = LeaseDividendsDistributionInterval; + type GetCommitments = (); + type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; + type CommitmentsInterface = CommitmentsI; + type EvmKeyAssociateRateLimit = EvmKeyAssociateRateLimit; + type AuthorshipProvider = MockAuthorshipProvider; +} + +// Swap-related parameter types +parameter_types! { + pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); + pub const SwapMaxFeeRate: u16 = 10000; // 15.26% + pub const SwapMaxPositions: u32 = 100; + pub const SwapMinimumLiquidity: u64 = 1_000; + pub const SwapMinimumReserve: NonZeroU64 = NonZeroU64::new(100).unwrap(); +} + +impl pallet_subtensor_swap::Config for Test { + type SubnetInfo = SubtensorModule; + type BalanceOps = SubtensorModule; + type ProtocolId = SwapProtocolId; + type TaoReserve = TaoBalanceReserve<Self>; + type AlphaReserve = AlphaBalanceReserve<Self>; + type MaxFeeRate = SwapMaxFeeRate; + type MaxPositions = SwapMaxPositions; + type MinimumLiquidity = SwapMinimumLiquidity; + type MinimumReserve = SwapMinimumReserve; + type WeightInfo = (); +} + +pub struct OriginPrivilegeCmp; + +impl PrivilegeCmp<OriginCaller> for OriginPrivilegeCmp { + fn cmp_privilege(_left: &OriginCaller, _right: &OriginCaller) -> Option<Ordering> { + Some(Ordering::Less) + } +} + +pub struct CommitmentsI; +impl CommitmentsInterface for CommitmentsI { + fn purge_netuid(_netuid: NetUid) {} +} + +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * + BlockWeights::get().max_block; + pub const MaxScheduledPerBlock: u32 = 50; + pub const NoPreimagePostponement: Option<u32> = Some(10); +} + +impl pallet_scheduler::Config for Test { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot<AccountId>; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Test>; + type OriginPrivilegeCmp = OriginPrivilegeCmp; + type Preimages = Preimage; + type BlockNumberProvider = System; +} + +impl pallet_utility::Config for Test { + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = pallet_utility::weights::SubstrateWeight<Test>; +} + +parameter_types! { + pub const PreimageMaxSize: u32 = 4096 * 1024; + pub const PreimageBaseDeposit: Balance = TaoBalance::new(1); + pub const PreimageByteDeposit: Balance = TaoBalance::new(1); +} + +impl pallet_preimage::Config for Test { + type WeightInfo = pallet_preimage::weights::SubstrateWeight<Test>; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type ManagerOrigin = EnsureRoot<AccountId>; + type Consideration = (); +} + +thread_local! { + pub static PROXIES: RefCell<FakeProxier> = const { RefCell::new(FakeProxier(vec![])) }; +} + +pub struct FakeProxier(pub Vec<(U256, U256)>); + +impl ProxyInterface<U256> for FakeProxier { + fn add_lease_beneficiary_proxy(beneficiary: &AccountId, lease: &AccountId) -> DispatchResult { + PROXIES.with_borrow_mut(|proxies| { + proxies.0.push((*beneficiary, *lease)); + }); + Ok(()) + } + + fn remove_lease_beneficiary_proxy( + beneficiary: &AccountId, + lease: &AccountId, + ) -> DispatchResult { + PROXIES.with_borrow_mut(|proxies| { + proxies.0.retain(|(b, l)| b != beneficiary && l != lease); + }); + Ok(()) + } +} + +parameter_types! { + pub const CrowdloanPalletId: PalletId = PalletId(*b"bt/cloan"); + pub const MinimumDeposit: u64 = 50; + pub const AbsoluteMinimumContribution: u64 = 10; + pub const MinimumBlockDuration: u64 = 20; + pub const MaximumBlockDuration: u64 = 100; + pub const RefundContributorsLimit: u32 = 5; + pub const MaxContributors: u32 = 10; +} + +impl pallet_crowdloan::Config for Test { + type PalletId = CrowdloanPalletId; + type Currency = Balances; + type RuntimeCall = RuntimeCall; + type WeightInfo = pallet_crowdloan::weights::SubstrateWeight<Test>; + type Preimages = Preimage; + type MinimumDeposit = MinimumDeposit; + type AbsoluteMinimumContribution = AbsoluteMinimumContribution; + type MinimumBlockDuration = MinimumBlockDuration; + type MaximumBlockDuration = MaximumBlockDuration; + type RefundContributorsLimit = RefundContributorsLimit; + type MaxContributors = MaxContributors; +} + +// Proxy Pallet config +parameter_types! { + // Set as 1 for testing purposes + pub const ProxyDepositBase: Balance = TaoBalance::new(1); + // Set as 1 for testing purposes + pub const ProxyDepositFactor: Balance = TaoBalance::new(1); + // Set as 20 for testing purposes + pub const MaxProxies: u32 = 20; // max num proxies per acct + // Set as 15 for testing purposes + pub const MaxPending: u32 = 15; // max blocks pending ~15min + // Set as 1 for testing purposes + pub const AnnouncementDepositBase: Balance = TaoBalance::new(1); + // Set as 1 for testing purposes + pub const AnnouncementDepositFactor: Balance = TaoBalance::new(1); +} + +impl pallet_proxy::Config for Test { + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = subtensor_runtime_common::ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = pallet_proxy::weights::SubstrateWeight<Test>; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; + type BlockNumberProvider = System; +} + +impl InstanceFilter<RuntimeCall> for subtensor_runtime_common::ProxyType { + fn filter(&self, _c: &RuntimeCall) -> bool { + // In tests, allow all proxy types to pass through + true + } + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (subtensor_runtime_common::ProxyType::Any, _) => true, + _ => false, + } + } +} + +mod test_crypto { + use super::KEY_TYPE; + use sp_core::{ + U256, + sr25519::{Public as Sr25519Public, Signature as Sr25519Signature}, + }; + use sp_runtime::{ + app_crypto::{app_crypto, sr25519}, + traits::IdentifyAccount, + }; + + app_crypto!(sr25519, KEY_TYPE); + + pub struct TestAuthId; + + impl frame_system::offchain::AppCrypto<Public, Signature> for TestAuthId { + type RuntimeAppPublic = Public; + type GenericSignature = Sr25519Signature; + type GenericPublic = Sr25519Public; + } + + impl IdentifyAccount for Public { + type AccountId = U256; + + fn into_account(self) -> U256 { + let mut bytes = [0u8; 32]; + bytes.copy_from_slice(self.as_ref()); + U256::from_big_endian(&bytes) + } + } +} + +pub type TestAuthId = test_crypto::TestAuthId; + +impl pallet_drand::Config for Test { + type AuthorityId = TestAuthId; + type Verifier = pallet_drand::verifier::QuicknetVerifier; + type UnsignedPriority = ConstU64<{ 1 << 20 }>; + type HttpFetchTimeout = ConstU64<1_000>; +} + +impl frame_system::offchain::SigningTypes for Test { + type Public = test_crypto::Public; + type Signature = test_crypto::Signature; +} + +pub type UncheckedExtrinsic = sp_runtime::testing::TestXt<RuntimeCall, ()>; + +impl<LocalCall> frame_system::offchain::CreateTransactionBase<LocalCall> for Test +where + RuntimeCall: From<LocalCall>, +{ + type Extrinsic = UncheckedExtrinsic; + type RuntimeCall = RuntimeCall; +} + +impl<LocalCall> frame_system::offchain::CreateInherent<LocalCall> for Test +where + RuntimeCall: From<LocalCall>, +{ + fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { + UncheckedExtrinsic::new_inherent(call) + } +} + +impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for Test +where + RuntimeCall: From<LocalCall>, +{ + fn create_signed_transaction< + C: frame_system::offchain::AppCrypto<Self::Public, Self::Signature>, + >( + call: <Self as CreateTransactionBase<LocalCall>>::RuntimeCall, + _public: Self::Public, + _account: Self::AccountId, + nonce: Self::Nonce, + ) -> Option<Self::Extrinsic> { + Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), ())) + } +} + +static TEST_LOGS_INIT: OnceLock<()> = OnceLock::new(); + +pub fn init_logs_for_tests() { + if TEST_LOGS_INIT.get().is_some() { + return; + } + + // RUST_LOG (full syntax) or "off" if unset + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("off")); + + // Bridge log -> tracing (ok if already set) + let _ = tracing_log::LogTracer::init(); + + // Simple formatter + let fmt_layer = tracing_subscriber::fmt::layer() + .with_ansi(false) + .with_target(true) + .with_level(true) + .without_time(); + + let _ = tracing_subscriber::registry() + .with(filter) + .with(fmt_layer) + .try_init(); + + let _ = TEST_LOGS_INIT.set(()); +} diff --git a/eco-tests/src/tests.rs b/eco-tests/src/tests.rs new file mode 100644 index 0000000000..65a5a0cd0f --- /dev/null +++ b/eco-tests/src/tests.rs @@ -0,0 +1,31 @@ +#![allow(clippy::unwrap_used)] +#![allow(clippy::arithmetic_side_effects)] + +use frame_support::assert_ok; +use pallet_subtensor::*; +use sp_core::{Get, U256}; + +use super::helpers::*; +use super::mock::*; + +#[test] +fn test_add_stake_ok_neuron_does_not_belong_to_coldkey() { + new_test_ext(1).execute_with(|| { + let coldkey_id = U256::from(544); + let hotkey_id = U256::from(54544); + let other_cold_key = U256::from(99498); + let netuid = add_dynamic_network(&hotkey_id, &coldkey_id); + let stake = DefaultMinStake::<Test>::get() * 10.into(); + + // Give it some $$$ in his coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&other_cold_key, stake.into()); + + // Perform the request which is signed by a different cold key + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(other_cold_key), + hotkey_id, + netuid, + stake, + )); + }); +} diff --git a/node/Cargo.toml b/node/Cargo.toml index 2766893452..d067eb19c8 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -26,7 +26,7 @@ clap = { workspace = true, features = ["derive"] } futures = { workspace = true, features = ["thread-pool"] } serde = { workspace = true, features = ["derive"] } hex.workspace = true -tokio = { workspace = true, features = ["time"] } +tokio = { workspace = true, features = ["time", "rt", "net"] } # Storage import memmap2.workspace = true @@ -68,6 +68,8 @@ sp-offchain.workspace = true sp-session.workspace = true frame-metadata-hash-extension.workspace = true frame-system.workspace = true +frame-support.workspace = true +pallet-balances.workspace = true pallet-transaction-payment.workspace = true pallet-commitments.workspace = true pallet-drand.workspace = true @@ -80,7 +82,7 @@ polkadot-sdk = { workspace = true, features = [ ] } # These dependencies are used for the subtensor's RPCs -jsonrpsee = { workspace = true, features = ["server"] } +jsonrpsee = { workspace = true, features = ["server", "http-client"] } sc-rpc.workspace = true sp-api.workspace = true sc-rpc-api.workspace = true @@ -156,6 +158,7 @@ runtime-benchmarks = [ "node-subtensor-runtime/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", + "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sc-service/runtime-benchmarks", "sp-runtime/runtime-benchmarks", @@ -165,6 +168,7 @@ runtime-benchmarks = [ "polkadot-sdk/runtime-benchmarks", "pallet-subtensor/runtime-benchmarks", "pallet-shield/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", "subtensor-runtime-common/runtime-benchmarks", ] @@ -174,6 +178,7 @@ pow-faucet = [] # in the near future. try-runtime = [ "node-subtensor-runtime/try-runtime", + "frame-support/try-runtime", "frame-system/try-runtime", "pallet-transaction-payment/try-runtime", "sp-runtime/try-runtime", @@ -182,6 +187,7 @@ try-runtime = [ "polkadot-sdk/try-runtime", "pallet-subtensor/try-runtime", "pallet-shield/try-runtime", + "pallet-balances/try-runtime", ] metadata-hash = ["node-subtensor-runtime/metadata-hash"] diff --git a/node/src/cli.rs b/node/src/cli.rs index e46c71857b..e7719b619c 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -8,6 +8,8 @@ use node_subtensor_runtime::opaque::Block; use sc_cli::RunCmd; use sc_consensus::BasicQueue; use sc_service::{Configuration, TaskManager}; +use std::fmt; +use std::path::PathBuf; use std::sync::Arc; #[derive(Debug, clap::Parser)] @@ -33,6 +35,14 @@ pub struct Cli { #[command(flatten)] pub eth: EthConfiguration, + + /// Control historical gap-backfill during initial/catch-up sync. + /// + /// `keep` preserves complete history (default for normal node runs). + /// `skip` is faster/lighter but historical block data may be incomplete. + /// For `build-patched-spec`, the implicit default is `skip` unless this flag is explicitly set. + #[arg(long, value_enum, default_value_t = HistoryBackfill::Keep)] + pub history_backfill: HistoryBackfill, } #[allow(clippy::large_enum_variant)] @@ -70,6 +80,93 @@ pub enum Subcommand { // Db meta columns information. ChainInfo(sc_cli::ChainInfoCmd), + + // Build a patched test chainspec from synced network state. + #[command(name = "build-patched-spec")] + CloneState(CloneStateCmd), +} + +/// Build a patched clone chainspec by syncing state, exporting raw state, and applying test patch. +#[derive(Debug, Clone, clap::Args)] +pub struct CloneStateCmd { + /// Chain spec identifier or path (same semantics as `--chain`). + #[arg(long, value_name = "CHAIN")] + pub chain: String, + + /// Base path used for syncing and state export. + #[arg(long, value_name = "PATH")] + pub base_path: PathBuf, + + /// Output file path for the final patched chainspec JSON. + #[arg(long, value_name = "FILE")] + pub output: PathBuf, + + /// Sync mode for the temporary sync node. + #[arg(long, value_enum, default_value_t = sc_cli::SyncMode::Warp)] + pub sync: sc_cli::SyncMode, + + /// Database backend for the temporary sync/export node. + #[arg(long, value_enum, default_value_t = sc_cli::Database::ParityDb)] + pub database: sc_cli::Database, + + /// RPC port used by the temporary sync node. + #[arg(long, default_value_t = 9966)] + pub rpc_port: u16, + + /// P2P port used by the temporary sync node. + #[arg(long, default_value_t = 30466)] + pub port: u16, + + /// Maximum time to wait for sync completion. + #[arg(long, default_value_t = 7200)] + pub sync_timeout_sec: u64, + + /// Accept sync completion when current is within this many blocks of highest. + #[arg(long, default_value_t = 8)] + pub sync_lag_blocks: u64, + + /// Optional bootnodes for the sync step. Repeatable. + #[arg(long, value_name = "BOOTNODE")] + pub bootnodes: Vec<String>, + + /// Include Alice in patched validator authorities (default if no validator flags are passed; + /// Sudo is assigned to the first selected validator in Alice->Bob->Charlie order). + #[arg(long, default_value_t = false)] + pub alice: bool, + + /// Include Bob in patched validator authorities (if any validator flag is set, only selected + /// validators are used; Sudo is assigned to the first selected validator in Alice->Bob->Charlie + /// order). + #[arg(long, default_value_t = false)] + pub bob: bool, + + /// Include Charlie in patched validator authorities (if any validator flag is set, only + /// selected validators are used; Sudo is assigned to the first selected validator in + /// Alice->Bob->Charlie order). + #[arg(long, default_value_t = false)] + pub charlie: bool, +} + +#[derive(Debug, Clone, Copy, clap::ValueEnum, Default)] +pub enum HistoryBackfill { + #[default] + Keep, + Skip, +} + +impl AsRef<str> for HistoryBackfill { + fn as_ref(&self) -> &str { + match self { + HistoryBackfill::Keep => "keep", + HistoryBackfill::Skip => "skip", + } + } +} + +impl fmt::Display for HistoryBackfill { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_ref()) + } } /// Available Sealing methods. @@ -99,6 +196,7 @@ impl SupportedConsensusMechanism { &self, config: &mut Configuration, eth_config: &EthConfiguration, + skip_history_backfill: bool, ) -> Result< ( Arc<FullClient>, @@ -110,8 +208,12 @@ impl SupportedConsensusMechanism { sc_service::Error, > { match self { - SupportedConsensusMechanism::Aura => new_chain_ops::<AuraConsensus>(config, eth_config), - SupportedConsensusMechanism::Babe => new_chain_ops::<BabeConsensus>(config, eth_config), + SupportedConsensusMechanism::Aura => { + new_chain_ops::<AuraConsensus>(config, eth_config, skip_history_backfill) + } + SupportedConsensusMechanism::Babe => { + new_chain_ops::<BabeConsensus>(config, eth_config, skip_history_backfill) + } } } } diff --git a/node/src/clone_spec.rs b/node/src/clone_spec.rs new file mode 100644 index 0000000000..c18ad3dfef --- /dev/null +++ b/node/src/clone_spec.rs @@ -0,0 +1,655 @@ +//! Build-and-patch workflow for producing a local test chainspec from live network state. +//! +//! This module implements the `build-patched-spec` subcommand scenario: +//! +//! 1. Start a temporary node and sync it to the requested chain. +//! 2. Wait until sync is considered stable (RPC-reported near-head status). +//! 3. Stop the temporary node and run `export-state` from the synced database. +//! 4. Apply patching to the raw chainspec: +//! - replace validator/authority sets with selected dev authorities, +//! - set Sudo to the first selected validator, +//! - clear session-derived keys and localize top-level chain fields. +//! 5. Write the final patched chainspec JSON to the requested output path. +//! +//! The result is intended for local/mainnet-clone style testing where runtime state is taken from a +//! live network, but governance/validator control is reassigned to test authorities. + +use std::collections::VecDeque; +use std::fs::{self, File}; +use std::io::{BufReader, BufWriter}; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command, Stdio}; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +use jsonrpsee::{ + core::client::ClientT, + http_client::{HttpClient, HttpClientBuilder}, + rpc_params, +}; +use serde_json::{Value, json}; +use sp_runtime::codec::Encode; + +use crate::cli::CloneStateCmd; + +type CloneResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>; + +const RPC_POLL_INTERVAL: Duration = Duration::from_secs(2); +const GRANDPA_AUTHORITIES_WELL_KNOWN_KEY: &[u8] = b":grandpa_authorities"; + +/// Execute `build-patched-spec`: sync network state, export raw chainspec, apply clone patch. +pub fn run(cmd: &CloneStateCmd, skip_history_backfill: bool) -> sc_cli::Result<()> { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_io() + .enable_time() + .build() + .map_err(|err| sc_cli::Error::Application(Box::new(err)))?; + + runtime + .block_on(async_run(cmd, skip_history_backfill)) + .map_err(sc_cli::Error::Application) +} + +async fn async_run(cmd: &CloneStateCmd, skip_history_backfill: bool) -> CloneResult<()> { + let validators = selected_validators(cmd); + let selected_names = validators + .iter() + .map(|seed| seed.to_ascii_lowercase()) + .collect::<Vec<_>>() + .join(","); + + fs::create_dir_all(&cmd.base_path)?; + + if let Some(parent) = cmd.output.parent() { + fs::create_dir_all(parent)?; + } + + let current_exe = std::env::current_exe()?; + let database_arg = database_arg(cmd.database); + let sync_arg = sync_arg(cmd.sync); + + log::info!( + "build-patched-spec: validators={} history_backfill={}", + selected_names, + if skip_history_backfill { + "skip" + } else { + "keep" + } + ); + + let mut sync_args = vec![ + "--base-path".to_string(), + cmd.base_path.display().to_string(), + "--chain".to_string(), + cmd.chain.clone(), + "--sync".to_string(), + sync_arg.to_string(), + "--database".to_string(), + database_arg.to_string(), + "--rpc-port".to_string(), + cmd.rpc_port.to_string(), + "--port".to_string(), + cmd.port.to_string(), + "--rpc-methods".to_string(), + "unsafe".to_string(), + "--no-telemetry".to_string(), + "--no-prometheus".to_string(), + "--no-mdns".to_string(), + "--name".to_string(), + "build-patched-spec-sync".to_string(), + "--history-backfill".to_string(), + if skip_history_backfill { + "skip".to_string() + } else { + "keep".to_string() + }, + ]; + + for bootnode in &cmd.bootnodes { + sync_args.push("--bootnodes".to_string()); + sync_args.push(bootnode.clone()); + } + + log::info!("build-patched-spec: starting sync node"); + + let mut sync_child = Command::new(¤t_exe) + .args(&sync_args) + .stdin(Stdio::null()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn()?; + + let sync_wait_result = wait_for_sync_completion(&mut sync_child, cmd).await; + let stop_result = stop_child_gracefully(&mut sync_child).await; + + sync_wait_result?; + stop_result?; + + let raw_tmp = temp_raw_path()?; + + log::info!("build-patched-spec: exporting raw state"); + + export_raw_state(¤t_exe, cmd, database_arg, &raw_tmp)?; + + log::info!("build-patched-spec: applying clone patch"); + + patch_raw_chainspec_file(&raw_tmp, &cmd.output, &validators)?; + + if let Err(err) = fs::remove_file(&raw_tmp) { + log::warn!( + "build-patched-spec: warning: failed to remove temp file {}: {err}", + raw_tmp.display() + ); + } + + log::info!("build-patched-spec: wrote {}", cmd.output.display()); + + Ok(()) +} + +async fn wait_for_sync_completion(sync_child: &mut Child, cmd: &CloneStateCmd) -> CloneResult<()> { + let timeout = Duration::from_secs(cmd.sync_timeout_sec); + let start = Instant::now(); + let mut stable_ready_checks = 0u8; + let rpc_url = format!("http://127.0.0.1:{}", cmd.rpc_port); + let rpc_client = HttpClientBuilder::default() + .request_timeout(Duration::from_secs(10)) + .build(rpc_url)?; + + log::info!( + "build-patched-spec: waiting for sync completion (timeout={}s)", + cmd.sync_timeout_sec + ); + + while sync_child + .try_wait() + .map_err(|err| std::io::Error::other(format!("Failed to poll sync node process: {err}")))? + .is_none() + { + if start.elapsed() > timeout { + return Err(format!( + "Timed out waiting for sync completion after {} seconds", + cmd.sync_timeout_sec + ) + .into()); + } + + match query_sync_status(&rpc_client).await { + Ok(status) => { + let is_ready = !status.is_syncing + && status.peers > 0 + && status.current > 0 + && status.highest > 0 + && status.current.saturating_add(cmd.sync_lag_blocks) >= status.highest; + + if is_ready { + stable_ready_checks = stable_ready_checks.saturating_add(1); + if stable_ready_checks >= 3 { + log::info!("build-patched-spec: sync target reached"); + return Ok(()); + } + } else { + stable_ready_checks = 0; + } + } + Err(_) => { + // RPC may not be ready yet. + stable_ready_checks = 0; + } + } + + tokio::time::sleep(RPC_POLL_INTERVAL).await; + } + + let status = sync_child + .try_wait() + .map_err(|err| std::io::Error::other(format!("Failed to poll sync node process: {err}")))? + .ok_or_else(|| std::io::Error::other("Sync node status became unavailable"))?; + + Err(format!("Sync node exited unexpectedly: {status}").into()) +} + +async fn stop_child_gracefully(child: &mut Child) -> CloneResult<()> { + if child.try_wait()?.is_some() { + return Ok(()); + } + + Command::new("kill") + .arg("-INT") + .arg(child.id().to_string()) + .status()?; + + for _ in 0..30 { + if child.try_wait()?.is_some() { + return Ok(()); + } + tokio::time::sleep(Duration::from_secs(1)).await; + } + + child.kill()?; + + child.wait()?; + + Ok(()) +} + +fn export_raw_state( + current_exe: &Path, + cmd: &CloneStateCmd, + database_arg: &str, + raw_tmp: &Path, +) -> CloneResult<()> { + let stdout = File::create(raw_tmp)?; + let status = Command::new(current_exe) + .args([ + "export-state", + "--chain", + &cmd.chain, + "--base-path", + &cmd.base_path.display().to_string(), + "--database", + database_arg, + ]) + .stdin(Stdio::null()) + .stdout(Stdio::from(stdout)) + .stderr(Stdio::inherit()) + .status()?; + + if !status.success() { + return Err(format!("export-state failed with status {status}").into()); + } + + Ok(()) +} + +struct SyncStatus { + current: u64, + highest: u64, + peers: u64, + is_syncing: bool, +} + +async fn query_sync_status(rpc_client: &HttpClient) -> CloneResult<SyncStatus> { + let sync = rpc_call(rpc_client, "system_syncState").await?; + let health = rpc_call(rpc_client, "system_health").await?; + + let current = parse_u64_field(&sync, "currentBlock") + .ok_or_else(|| "system_syncState.currentBlock missing".to_string())?; + let highest = parse_u64_field(&sync, "highestBlock") + .ok_or_else(|| "system_syncState.highestBlock missing".to_string())?; + let peers = parse_u64_field(&health, "peers") + .ok_or_else(|| "system_health.peers missing".to_string())?; + let is_syncing = health + .get("isSyncing") + .and_then(Value::as_bool) + .ok_or_else(|| "system_health.isSyncing missing".to_string())?; + + Ok(SyncStatus { + current, + highest, + peers, + is_syncing, + }) +} + +async fn rpc_call(rpc_client: &HttpClient, method: &str) -> CloneResult<Value> { + rpc_client + .request(method, rpc_params![]) + .await + .map_err(Into::into) +} + +fn parse_u64_field(value: &Value, field: &str) -> Option<u64> { + let field_value = value.get(field)?; + + if let Value::Number(number) = field_value { + return number.to_string().parse::<u64>().ok(); + } + + let s = field_value.as_str()?; + + s.parse::<u64>() + .ok() + .or_else(|| u64::from_str_radix(s.trim_start_matches("0x"), 16).ok()) +} + +fn temp_raw_path() -> CloneResult<PathBuf> { + let epoch = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); + Ok(std::env::temp_dir().join(format!("subtensor-clone-export-{epoch}.json"))) +} + +fn sync_arg(mode: sc_cli::SyncMode) -> &'static str { + match mode { + sc_cli::SyncMode::Full => "full", + sc_cli::SyncMode::Fast => "fast", + sc_cli::SyncMode::FastUnsafe => "fast-unsafe", + sc_cli::SyncMode::Warp => "warp", + } +} + +fn database_arg(database: sc_cli::Database) -> &'static str { + match database { + #[cfg(feature = "rocksdb")] + sc_cli::Database::RocksDb => "rocksdb", + sc_cli::Database::ParityDb => "paritydb", + sc_cli::Database::Auto => "auto", + sc_cli::Database::ParityDbDeprecated => "paritydb-experimental", + } +} + +fn selected_validators(cmd: &CloneStateCmd) -> Vec<&'static str> { + let explicit = cmd.alice || cmd.bob || cmd.charlie; + let mut selected = Vec::new(); + + if explicit { + if cmd.alice { + selected.push("Alice"); + } + if cmd.bob { + selected.push("Bob"); + } + if cmd.charlie { + selected.push("Charlie"); + } + } else { + selected.push("Alice"); // only alice by default + } + + selected +} + +fn patch_raw_chainspec_file( + input: &Path, + output: &Path, + validators: &[&'static str], +) -> CloneResult<()> { + let file = File::open(input)?; + let reader = BufReader::with_capacity(64 * 1024 * 1024, file); + let mut spec: Value = serde_json::from_reader(reader)?; + patch_raw_spec(&mut spec, validators)?; + + let out = File::create(output)?; + let writer = BufWriter::with_capacity(64 * 1024 * 1024, out); + serde_json::to_writer(writer, &spec)?; + Ok(()) +} + +fn patch_raw_spec(spec: &mut Value, validators: &[&'static str]) -> CloneResult<()> { + let sudo = validators + .first() + .ok_or_else(|| "at least one validator must be selected".to_string())?; + + let top = spec + .pointer_mut("/genesis/raw/top") + .and_then(Value::as_object_mut) + .ok_or_else(|| "missing or invalid genesis.raw.top".to_string())?; + + let aura_keys = validators + .iter() + .map(|seed| crate::chain_spec::authority_keys_from_seed(seed).0) + .collect::<Vec<_>>(); + top.insert( + storage_key("Aura", "Authorities"), + Value::String(to_hex(&aura_keys.encode())), + ); + + let grandpa_entries = validators + .iter() + .map(|seed| (crate::chain_spec::authority_keys_from_seed(seed).1, 1u64)) + .collect::<Vec<_>>(); + let grandpa_encoded = grandpa_entries.encode(); + + top.insert( + storage_key("Grandpa", "Authorities"), + Value::String(to_hex(&grandpa_encoded)), + ); + + let mut well_known = vec![0x01u8]; + well_known.extend_from_slice(&grandpa_encoded); + top.insert( + to_hex(GRANDPA_AUTHORITIES_WELL_KNOWN_KEY), + Value::String(to_hex(&well_known)), + ); + + top.insert( + storage_key("Grandpa", "CurrentSetId"), + Value::String(to_hex(&0u64.to_le_bytes())), + ); + top.insert( + storage_key("Grandpa", "State"), + Value::String("0x00".into()), + ); + top.remove(&storage_key("Grandpa", "PendingChange")); + top.remove(&storage_key("Grandpa", "NextForced")); + top.remove(&storage_key("Grandpa", "Stalled")); + remove_by_prefix(top, &storage_key("Grandpa", "SetIdSession")); + + top.insert( + storage_key("Sudo", "Key"), + Value::String(to_hex( + &crate::chain_spec::get_account_id_from_seed::<sp_core::sr25519::Public>(sudo).encode(), + )), + ); + + remove_by_prefix(top, &storage_prefix("Session")); + + set_validator_balances(top, validators); + + clear_top_level(spec); + Ok(()) +} + +/// Insert a `System::Account` entry for each validator seed so that dev authorities +/// have enough free balance to produce transactions on the cloned chain. +fn set_validator_balances(top: &mut serde_json::Map<String, Value>, validators: &[&'static str]) { + const FREE_BALANCE: u64 = 1_000_000_000_000_000; // 1M TAO (9 decimals) + + let prefix = frame_support::storage::storage_prefix(b"System", b"Account"); + + for seed in validators { + let account_id = + crate::chain_spec::get_account_id_from_seed::<sp_core::sr25519::Public>(seed); + let encoded_id = account_id.encode(); + + // Blake2_128Concat = blake2_128(encoded) ++ encoded + let hash = sp_io::hashing::blake2_128(&encoded_id); + let mut full_key = prefix.to_vec(); + full_key.extend_from_slice(&hash); + full_key.extend_from_slice(&encoded_id); + + let account_info = frame_system::AccountInfo { + nonce: 0u32, + consumers: 0u32, + providers: 1u32, // >=1 to keep account alive + sufficients: 0u32, + data: pallet_balances::AccountData { + free: FREE_BALANCE, + ..Default::default() + }, + }; + + top.insert( + to_hex(&full_key), + Value::String(to_hex(&account_info.encode())), + ); + } +} + +fn remove_by_prefix(map: &mut serde_json::Map<String, Value>, prefix: &str) { + let mut keys_to_remove = VecDeque::new(); + for key in map.keys() { + if key.starts_with(prefix) { + keys_to_remove.push_back(key.clone()); + } + } + while let Some(key) = keys_to_remove.pop_front() { + map.remove(&key); + } +} + +fn clear_top_level(spec: &mut Value) { + if let Some(object) = spec.as_object_mut() { + object.insert("bootNodes".into(), json!([])); + object.insert("codeSubstitutes".into(), json!({})); + object.insert("chainType".into(), json!("Local")); + } +} + +fn storage_key(pallet: &str, item: &str) -> String { + let key = frame_support::storage::storage_prefix(pallet.as_bytes(), item.as_bytes()); + to_hex(&key) +} + +fn storage_prefix(pallet: &str) -> String { + format!( + "0x{}", + hex::encode(sp_io::hashing::twox_128(pallet.as_bytes())) + ) +} + +fn to_hex(data: &[u8]) -> String { + format!("0x{}", hex::encode(data)) +} + +#[cfg(test)] +#[allow(clippy::expect_used)] +mod tests { + use super::*; + + fn target_artifact_path(name: &str) -> PathBuf { + let target_dir = std::env::var_os("CARGO_TARGET_DIR") + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from("target")); + target_dir.join("clone-spec-tests").join(name) + } + + fn default_cmd() -> CloneStateCmd { + CloneStateCmd { + chain: "finney".to_string(), + base_path: target_artifact_path("base"), + output: target_artifact_path("out.json"), + sync: sc_cli::SyncMode::Warp, + database: sc_cli::Database::ParityDb, + rpc_port: 9966, + port: 30466, + sync_timeout_sec: 10, + sync_lag_blocks: 8, + bootnodes: Vec::new(), + alice: false, + bob: false, + charlie: false, + } + } + + fn make_minimal_spec() -> Value { + let mut top = serde_json::Map::new(); + top.insert(storage_key("Grandpa", "PendingChange"), json!("0x01")); + top.insert(storage_key("Grandpa", "NextForced"), json!("0x02")); + top.insert(storage_key("Grandpa", "Stalled"), json!("0x03")); + top.insert( + format!("{}{}", storage_key("Grandpa", "SetIdSession"), "deadbeef"), + json!("0x04"), + ); + top.insert(format!("{}abcd", storage_prefix("Session")), json!("0x05")); + top.insert(storage_key("Balances", "TotalIssuance"), json!("0x06")); + + json!({ + "genesis": { "raw": { "top": top } }, + "bootNodes": ["/dns4/example.com/tcp/30333/p2p/12D3KooW..."], + "codeSubstitutes": { "0x01": "0x02" }, + "chainType": "Live" + }) + } + + #[test] + fn selected_validators_defaults_to_alice() { + let cmd = default_cmd(); + let selected = selected_validators(&cmd); + assert_eq!(selected.len(), 1); + assert_eq!(selected.first(), Some(&"Alice")); + } + + #[test] + fn selected_validators_respects_explicit_flags() { + let mut cmd = default_cmd(); + cmd.bob = true; + cmd.charlie = true; + + let selected = selected_validators(&cmd); + assert_eq!(selected, vec!["Bob", "Charlie"]); + } + + #[test] + fn parse_u64_field_supports_u64_decimal_and_hex_string() { + let value = json!({ + "a": 42, + "b": "123", + "c": "0x2a" + }); + + assert_eq!(parse_u64_field(&value, "a"), Some(42)); + assert_eq!(parse_u64_field(&value, "b"), Some(123)); + assert_eq!(parse_u64_field(&value, "c"), Some(42)); + assert_eq!(parse_u64_field(&value, "missing"), None); + } + + #[test] + fn patch_raw_spec_updates_authorities_sudo_and_top_level() { + let mut spec = make_minimal_spec(); + let validators = vec!["Alice", "Bob"]; + patch_raw_spec(&mut spec, &validators).expect("patch should succeed"); + + let top = spec + .pointer("/genesis/raw/top") + .and_then(Value::as_object) + .expect("top should be object"); + + let aura_hex = top + .get(&storage_key("Aura", "Authorities")) + .and_then(Value::as_str) + .expect("aura authorities key should exist"); + let aura_raw = hex::decode(aura_hex.trim_start_matches("0x")).expect("hex decode aura"); + let expected_aura = vec![ + crate::chain_spec::authority_keys_from_seed("Alice").0, + crate::chain_spec::authority_keys_from_seed("Bob").0, + ] + .encode(); + assert_eq!(aura_raw, expected_aura); + + let sudo_hex = top + .get(&storage_key("Sudo", "Key")) + .and_then(Value::as_str) + .expect("sudo key should exist"); + assert_eq!( + sudo_hex, + to_hex( + &crate::chain_spec::get_account_id_from_seed::<sp_core::sr25519::Public>("Alice") + .encode() + ) + .as_str() + ); + + assert!(!top.contains_key(&storage_key("Grandpa", "PendingChange"))); + assert!(!top.contains_key(&storage_key("Grandpa", "NextForced"))); + assert!(!top.contains_key(&storage_key("Grandpa", "Stalled"))); + assert!( + top.keys() + .all(|k| !k.starts_with(&storage_prefix("Session"))) + ); + + assert_eq!(spec.get("chainType"), Some(&json!("Local"))); + assert_eq!(spec.get("bootNodes"), Some(&json!([]))); + assert_eq!(spec.get("codeSubstitutes"), Some(&json!({}))); + } + + #[test] + fn patch_raw_spec_fails_when_top_missing() { + let mut spec = json!({}); + let err = patch_raw_spec(&mut spec, &["Alice"]).expect_err("must fail"); + assert!( + err.to_string() + .contains("missing or invalid genesis.raw.top"), + "unexpected error: {err}" + ); + } +} diff --git a/node/src/command.rs b/node/src/command.rs index 670ae6f4e4..fd3a122ec5 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -2,7 +2,7 @@ use std::sync::{Arc, atomic::AtomicBool}; use crate::{ chain_spec, - cli::{Cli, Subcommand, SupportedConsensusMechanism}, + cli::{Cli, HistoryBackfill, Subcommand, SupportedConsensusMechanism}, consensus::BabeConsensus, ethereum::db_config_dir, service, @@ -62,6 +62,7 @@ pub fn run() -> sc_cli::Result<()> { let cmd = Cli::command(); let arg_matches = cmd.get_matches(); let cli = Cli::from_arg_matches(&arg_matches)?; + let skip_history_backfill = resolve_skip_history_backfill(&cli, &arg_matches); match &cli.subcommand { Some(Subcommand::Key(cmd)) => cmd.run(&cli), @@ -72,32 +73,40 @@ pub fn run() -> sc_cli::Result<()> { Some(Subcommand::CheckBlock(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let (client, _, import_queue, task_manager, _) = - cli.initial_consensus.new_chain_ops(&mut config, &cli.eth)?; + let (client, _, import_queue, task_manager, _) = cli + .initial_consensus + .new_chain_ops(&mut config, &cli.eth, skip_history_backfill)?; Ok((cmd.run(client, import_queue), task_manager)) }) } Some(Subcommand::ExportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let (client, _, _, task_manager, _) = - cli.initial_consensus.new_chain_ops(&mut config, &cli.eth)?; + let (client, _, _, task_manager, _) = cli.initial_consensus.new_chain_ops( + &mut config, + &cli.eth, + skip_history_backfill, + )?; Ok((cmd.run(client, config.database), task_manager)) }) } Some(Subcommand::ExportState(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let (client, _, _, task_manager, _) = - cli.initial_consensus.new_chain_ops(&mut config, &cli.eth)?; + let (client, _, _, task_manager, _) = cli.initial_consensus.new_chain_ops( + &mut config, + &cli.eth, + skip_history_backfill, + )?; Ok((cmd.run(client, config.chain_spec), task_manager)) }) } Some(Subcommand::ImportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let (client, _, import_queue, task_manager, _) = - cli.initial_consensus.new_chain_ops(&mut config, &cli.eth)?; + let (client, _, import_queue, task_manager, _) = cli + .initial_consensus + .new_chain_ops(&mut config, &cli.eth, skip_history_backfill)?; Ok((cmd.run(client, import_queue), task_manager)) }) } @@ -150,8 +159,11 @@ pub fn run() -> sc_cli::Result<()> { Some(Subcommand::Revert(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let (client, backend, _, task_manager, _) = - cli.initial_consensus.new_chain_ops(&mut config, &cli.eth)?; + let (client, backend, _, task_manager, _) = cli.initial_consensus.new_chain_ops( + &mut config, + &cli.eth, + skip_history_backfill, + )?; let aux_revert = Box::new(move |client, _, blocks| { sc_consensus_grandpa::revert(client, blocks)?; Ok(()) @@ -233,25 +245,54 @@ pub fn run() -> sc_cli::Result<()> { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run::<Block>(&config)) } + Some(Subcommand::CloneState(cmd)) => { + let runner = cli.create_runner(&cli.run)?; + let cmd = cmd.clone(); + runner.sync_run(move |_| crate::clone_spec::run(&cmd, skip_history_backfill)) + } // Start with the initial consensus type asked. - None => { - let arg_matches = Cli::command().get_matches(); - let cli = Cli::from_args(); - match cli.initial_consensus { - SupportedConsensusMechanism::Babe => start_babe_service(&arg_matches), - SupportedConsensusMechanism::Aura => start_aura_service(&arg_matches), + None => match cli.initial_consensus { + SupportedConsensusMechanism::Babe => { + start_babe_service(&arg_matches, skip_history_backfill) } - } + SupportedConsensusMechanism::Aura => { + start_aura_service(&arg_matches, skip_history_backfill) + } + }, } } +fn resolve_skip_history_backfill(cli: &Cli, arg_matches: &ArgMatches) -> bool { + // We keep a single global `--history-backfill` flag, but `build-patched-spec` should default to + // `skip` when the operator didn't set the flag explicitly. This preserves `keep` as the default + // for normal node runs. + if matches!( + arg_matches.value_source("history_backfill"), + Some(ValueSource::CommandLine) + ) { + return matches!(cli.history_backfill, HistoryBackfill::Skip); + } + + matches!(&cli.subcommand, Some(Subcommand::CloneState(_))) +} + #[allow(clippy::expect_used)] -fn start_babe_service(arg_matches: &ArgMatches) -> Result<(), sc_cli::Error> { +fn start_babe_service( + arg_matches: &ArgMatches, + skip_history_backfill: bool, +) -> Result<(), sc_cli::Error> { let cli = Cli::from_arg_matches(arg_matches).expect("Bad arg_matches"); let runner = cli.create_runner(&cli.run)?; match runner.run_node_until_exit(|config| async move { let config = customise_config(arg_matches, config); - service::build_full::<BabeConsensus>(config, cli.eth, cli.sealing, None).await + service::build_full::<BabeConsensus>( + config, + cli.eth, + cli.sealing, + None, + skip_history_backfill, + ) + .await }) { Ok(_) => Ok(()), Err(e) => { @@ -264,7 +305,7 @@ fn start_babe_service(arg_matches: &ArgMatches) -> Result<(), sc_cli::Error> { log::info!( "💡 Chain is using Aura consensus. Switching to Aura service until Babe block is detected.", ); - start_aura_service(arg_matches) + start_aura_service(arg_matches, skip_history_backfill) // Handle Aura service still has DB lock. This never has been observed to take more // than 1s to drop. } else if matches!(e, sc_service::Error::Client(sp_blockchain::Error::Backend(ref msg)) @@ -272,7 +313,7 @@ fn start_babe_service(arg_matches: &ArgMatches) -> Result<(), sc_cli::Error> { { log::info!("Failed to aquire DB lock, trying again in 1s..."); std::thread::sleep(std::time::Duration::from_secs(1)); - start_babe_service(arg_matches) + start_babe_service(arg_matches, skip_history_backfill) // Unknown error, return it. } else { log::error!("Failed to start Babe service: {e:?}"); @@ -283,7 +324,10 @@ fn start_babe_service(arg_matches: &ArgMatches) -> Result<(), sc_cli::Error> { } #[allow(clippy::expect_used)] -fn start_aura_service(arg_matches: &ArgMatches) -> Result<(), sc_cli::Error> { +fn start_aura_service( + arg_matches: &ArgMatches, + skip_history_backfill: bool, +) -> Result<(), sc_cli::Error> { let cli = Cli::from_arg_matches(arg_matches).expect("Bad arg_matches"); let runner = cli.create_runner(&cli.run)?; @@ -301,13 +345,14 @@ fn start_aura_service(arg_matches: &ArgMatches) -> Result<(), sc_cli::Error> { cli.eth, cli.sealing, Some(custom_service_signal_clone), + skip_history_backfill, ) .await }) { Ok(()) => Ok(()), Err(e) => { if custom_service_signal.load(std::sync::atomic::Ordering::Relaxed) { - start_babe_service(arg_matches) + start_babe_service(arg_matches, skip_history_backfill) } else { Err(e.into()) } diff --git a/node/src/conditional_evm_block_import.rs b/node/src/conditional_evm_block_import.rs index b6ba445c1f..0a69bdc090 100644 --- a/node/src/conditional_evm_block_import.rs +++ b/node/src/conditional_evm_block_import.rs @@ -1,11 +1,12 @@ use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult}; -use sp_consensus::Error as ConsensusError; +use sp_consensus::{BlockOrigin, Error as ConsensusError}; use sp_runtime::traits::{Block as BlockT, Header}; use std::marker::PhantomData; pub struct ConditionalEVMBlockImport<B: BlockT, I, F> { inner: I, frontier_block_import: F, + skip_history_backfill: bool, _marker: PhantomData<B>, } @@ -19,6 +20,7 @@ where ConditionalEVMBlockImport { inner: self.inner.clone(), frontier_block_import: self.frontier_block_import.clone(), + skip_history_backfill: self.skip_history_backfill, _marker: PhantomData, } } @@ -32,10 +34,11 @@ where F: BlockImport<B>, F::Error: Into<ConsensusError>, { - pub fn new(inner: I, frontier_block_import: F) -> Self { + pub fn new(inner: I, frontier_block_import: F, skip_history_backfill: bool) -> Self { Self { inner, frontier_block_import, + skip_history_backfill, _marker: PhantomData, } } @@ -56,7 +59,17 @@ where self.inner.check_block(block).await.map_err(Into::into) } - async fn import_block(&self, block: BlockImportParams<B>) -> Result<ImportResult, Self::Error> { + async fn import_block( + &self, + mut block: BlockImportParams<B>, + ) -> Result<ImportResult, Self::Error> { + if self.skip_history_backfill && matches!(block.origin, BlockOrigin::NetworkInitialSync) { + // During initial network sync, Substrate can mark missing historical ranges as "gaps" + // (`create_gap = true`) and then backfill them later. When history backfill is set to + // `skip`, we disable gap creation so no history reconstruction work is scheduled. + // `build-patched-spec` just defaults this setting to `skip`. + block.create_gap = false; + } // 4345556 - mainnet runtime upgrade block with Frontier if *block.header.number() < 4345557u32.into() { self.inner.import_block(block).await.map_err(Into::into) diff --git a/node/src/consensus/aura_consensus.rs b/node/src/consensus/aura_consensus.rs index ce34e8125a..74ec8fea1e 100644 --- a/node/src/consensus/aura_consensus.rs +++ b/node/src/consensus/aura_consensus.rs @@ -139,7 +139,7 @@ impl ConsensusMechanism for AuraConsensus { Self {} } - fn build_biq(&mut self) -> Result<BIQ<'_>, sc_service::Error> + fn build_biq(&mut self, skip_history_backfill: bool) -> Result<BIQ<'_>, sc_service::Error> where NumberFor<Block>: BlockNumberOps, { @@ -157,6 +157,7 @@ impl ConsensusMechanism for AuraConsensus { client.clone(), grandpa_block_import.clone(), expected_babe_config.clone(), + skip_history_backfill, ); let slot_duration = sc_consensus_aura::slot_duration(&*client)?; diff --git a/node/src/consensus/babe_consensus.rs b/node/src/consensus/babe_consensus.rs index 4f84cbb87b..fad204fb48 100644 --- a/node/src/consensus/babe_consensus.rs +++ b/node/src/consensus/babe_consensus.rs @@ -152,7 +152,7 @@ impl ConsensusMechanism for BabeConsensus { } } - fn build_biq(&mut self) -> Result<BIQ<'_>, sc_service::Error> + fn build_biq(&mut self, skip_history_backfill: bool) -> Result<BIQ<'_>, sc_service::Error> where NumberFor<Block>: BlockNumberOps, { @@ -188,6 +188,7 @@ impl ConsensusMechanism for BabeConsensus { let conditional_block_import = ConditionalEVMBlockImport::new( babe_import.clone(), FrontierBlockImport::new(babe_import.clone(), client.clone()), + skip_history_backfill, ); let slot_duration = babe_link.config().slot_duration(); diff --git a/node/src/consensus/consensus_mechanism.rs b/node/src/consensus/consensus_mechanism.rs index 9fd8cad63b..41cb2fb4a8 100644 --- a/node/src/consensus/consensus_mechanism.rs +++ b/node/src/consensus/consensus_mechanism.rs @@ -78,7 +78,7 @@ pub trait ConsensusMechanism { fn new() -> Self; /// Builds a `BIQ` that uses the ConsensusMechanism. - fn build_biq(&mut self) -> Result<BIQ<'_>, sc_service::Error>; + fn build_biq(&mut self, skip_history_backfill: bool) -> Result<BIQ<'_>, sc_service::Error>; /// Returns the slot duration. fn slot_duration(&self, client: &FullClient) -> Result<SlotDuration, ServiceError>; diff --git a/node/src/consensus/hybrid_import_queue.rs b/node/src/consensus/hybrid_import_queue.rs index 30d8ff4065..342d67dbc1 100644 --- a/node/src/consensus/hybrid_import_queue.rs +++ b/node/src/consensus/hybrid_import_queue.rs @@ -71,10 +71,12 @@ impl HybridBlockImport { client: Arc<FullClient>, grandpa_block_import: GrandpaBlockImport, babe_config: BabeConfiguration, + skip_history_backfill: bool, ) -> Self { let inner_aura = ConditionalEVMBlockImport::new( grandpa_block_import.clone(), FrontierBlockImport::new(grandpa_block_import.clone(), client.clone()), + skip_history_backfill, ); #[allow(clippy::expect_used)] @@ -88,6 +90,7 @@ impl HybridBlockImport { let inner_babe = ConditionalEVMBlockImport::new( babe_import.clone(), FrontierBlockImport::new(babe_import.clone(), client.clone()), + skip_history_backfill, ); HybridBlockImport { diff --git a/node/src/lib.rs b/node/src/lib.rs index c447a07309..4740155f5e 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -1,6 +1,7 @@ pub mod chain_spec; pub mod cli; pub mod client; +pub mod clone_spec; pub mod conditional_evm_block_import; pub mod consensus; pub mod ethereum; diff --git a/node/src/main.rs b/node/src/main.rs index 64f25acc67..2766b93054 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -6,6 +6,7 @@ mod benchmarking; mod chain_spec; mod cli; mod client; +mod clone_spec; mod command; mod conditional_evm_block_import; mod consensus; diff --git a/node/src/service.rs b/node/src/service.rs index f33931d210..067fc3ff91 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -241,6 +241,7 @@ pub fn build_manual_seal_import_queue( crate::conditional_evm_block_import::ConditionalEVMBlockImport::new( grandpa_block_import.clone(), fc_consensus::FrontierBlockImport::new(grandpa_block_import.clone(), client.clone()), + false, ); Ok(( sc_consensus_manual_seal::import_queue( @@ -259,6 +260,7 @@ pub async fn new_full<NB, CM>( eth_config: EthConfiguration, sealing: Option<Sealing>, custom_service_signal: Option<Arc<AtomicBool>>, + skip_history_backfill: bool, ) -> Result<TaskManager, ServiceError> where NumberFor<Block>: BlockNumberOps, @@ -275,7 +277,7 @@ where } let mut consensus_mechanism = CM::new(); - let build_import_queue = consensus_mechanism.build_biq()?; + let build_import_queue = consensus_mechanism.build_biq(skip_history_backfill)?; let PartialComponents { client, @@ -660,6 +662,7 @@ pub async fn build_full<CM: ConsensusMechanism>( eth_config: EthConfiguration, sealing: Option<Sealing>, custom_service_signal: Option<Arc<AtomicBool>>, + skip_history_backfill: bool, ) -> Result<TaskManager, ServiceError> { match config.network.network_backend { sc_network::config::NetworkBackendType::Libp2p => { @@ -668,6 +671,7 @@ pub async fn build_full<CM: ConsensusMechanism>( eth_config, sealing, custom_service_signal, + skip_history_backfill, ) .await } @@ -677,6 +681,7 @@ pub async fn build_full<CM: ConsensusMechanism>( eth_config, sealing, custom_service_signal, + skip_history_backfill, ) .await } @@ -686,6 +691,7 @@ pub async fn build_full<CM: ConsensusMechanism>( pub fn new_chain_ops<CM: ConsensusMechanism>( config: &mut Configuration, eth_config: &EthConfiguration, + skip_history_backfill: bool, ) -> Result< ( Arc<FullClient>, @@ -705,7 +711,11 @@ pub fn new_chain_ops<CM: ConsensusMechanism>( task_manager, other, .. - } = new_partial(config, eth_config, consensus_mechanism.build_biq()?)?; + } = new_partial( + config, + eth_config, + consensus_mechanism.build_biq(skip_history_backfill)?, + )?; Ok((client, backend, import_queue, task_manager, other.3)) } @@ -748,7 +758,7 @@ fn run_manual_seal_authorship( TIMESTAMP.with(|x| { let mut x_ref = x.borrow_mut(); *x_ref = x_ref.saturating_add(subtensor_runtime_common::time::SLOT_DURATION); - inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x.borrow()) + inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x_ref) }) } @@ -765,6 +775,9 @@ fn run_manual_seal_authorship( let create_inherent_data_providers = move |_, ()| async move { Ok(MockTimestampInherentDataProvider) }; + let aura_data_provider = + sc_consensus_manual_seal::consensus::aura::AuraConsensusDataProvider::new(client.clone()); + let manual_seal = match sealing { Sealing::Manual => future::Either::Left(sc_consensus_manual_seal::run_manual_seal( sc_consensus_manual_seal::ManualSealParams { @@ -774,7 +787,7 @@ fn run_manual_seal_authorship( pool: transaction_pool, commands_stream, select_chain, - consensus_data_provider: None, + consensus_data_provider: Some(Box::new(aura_data_provider)), create_inherent_data_providers, }, )), diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 7f882fcf9c..a0c094e564 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1149,7 +1149,7 @@ pub mod pallet { /// The extrinsic will call the Subtensor pallet to set the minimum stake required for nominators. #[pallet::call_index(43)] #[pallet::weight(Weight::from_parts(28_050_000, 6792) - .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)))] pub fn sudo_set_nominator_min_required_stake( origin: OriginFor<T>, @@ -1595,7 +1595,7 @@ pub mod pallet { /// # Weight /// Weight is handled by the `#[pallet::weight]` attribute. #[pallet::call_index(65)] - #[pallet::weight(Weight::from_parts(6_201_000, 0) + #[pallet::weight(Weight::from_parts(3_415_000, 0) .saturating_add(T::DbWeight::get().reads(0_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)))] pub fn sudo_set_ema_price_halving_period( diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 35f8f6f784..f744a4ea39 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -340,8 +340,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = pallet_subtensor::TaoCurrencyReserve<Self>; - type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve<Self>; + type TaoReserve = pallet_subtensor::TaoBalanceReserve<Self>; + type AlphaReserve = pallet_subtensor::AlphaBalanceReserve<Self>; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index a030607995..a7d41b7f3a 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -33,6 +33,7 @@ impl<T: Config + pallet_drand::Config> Pallet<T> { Self::run_auto_claim_root_divs(last_block_hash); // --- 9. Populate root coldkey maps. Self::populate_root_coldkey_staking_maps(); + Self::populate_root_coldkey_staking_maps_v2(); // Return ok. Ok(()) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d25f7ce170..460a754d45 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -515,7 +515,7 @@ impl<T: Config> Pallet<T> { log::debug!( "owner_hotkey: {owner_hotkey:?} owner_coldkey: {owner_coldkey:?}, owner_cut: {owner_cut:?}" ); - let real_owner_cut = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &owner_hotkey, &owner_coldkey, netuid, @@ -523,7 +523,7 @@ impl<T: Config> Pallet<T> { ); // If the subnet is leased, notify the lease logic that owner cut has been distributed. if let Some(lease_id) = SubnetUidToLeaseId::<T>::get(netuid) { - Self::distribute_leased_network_dividends(lease_id, real_owner_cut); + Self::distribute_leased_network_dividends(lease_id, owner_cut); } } @@ -617,7 +617,7 @@ impl<T: Config> Pallet<T> { root_alpha = root_alpha.saturating_sub(alpha_take); // Give the validator their take. log::debug!("hotkey: {hotkey:?} alpha_take: {alpha_take:?}"); - let _validator_stake = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &Owner::<T>::get(hotkey.clone()), netuid, diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index a5640e1736..e8cb541863 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -256,7 +256,8 @@ where *protocol, *placeholder1, *placeholder2, - ), + ) + .map(|_| ()), 0u64, ) .map(|validity| (validity, (), origin.clone())) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 556b941565..54ccadc1a1 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -94,6 +94,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use pallet_drand::types::RoundNumber; use runtime_common::prod_or_fast; + use share_pool::SafeFloat; use sp_core::{ConstU32, H160, H256}; use sp_runtime::traits::{Dispatchable, TrailingZeroInput}; use sp_std::collections::btree_map::BTreeMap; @@ -1435,11 +1436,41 @@ pub mod pallet { ValueQuery, >; + /// DMAP ( hot, netuid ) --> total_alpha_shares | Returns the number of alpha shares for a hotkey on a subnet, stores SafeFloat. + #[pallet::storage] + pub type TotalHotkeySharesV2<T: Config> = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, // hot + Identity, + NetUid, // subnet + SafeFloat, // Hotkey shares in unlimited precision + ValueQuery, + >; + + /// --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet, stores SafeFloat. + #[pallet::storage] + pub type AlphaV2<T: Config> = StorageNMap< + _, + ( + NMapKey<Blake2_128Concat, T::AccountId>, // hot + NMapKey<Blake2_128Concat, T::AccountId>, // cold + NMapKey<Identity, NetUid>, // subnet + ), + SafeFloat, // Shares in unlimited precision + ValueQuery, + >; + /// Contains last Alpha storage map key to iterate (check first) #[pallet::storage] pub type AlphaMapLastKey<T: Config> = StorageValue<_, Option<Vec<u8>>, ValueQuery, DefaultAlphaIterationLastKey<T>>; + /// Contains last AlphaV2 storage map key to iterate (check first) + #[pallet::storage] + pub type AlphaV2MapLastKey<T: Config> = + StorageValue<_, Option<Vec<u8>>, ValueQuery, DefaultAlphaIterationLastKey<T>>; + /// --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. #[pallet::storage] pub type TokenSymbol<T: Config> = @@ -2495,9 +2526,9 @@ use sp_std::vec::Vec; use subtensor_macros::freeze_struct; #[derive(Clone)] -pub struct TaoCurrencyReserve<T: Config>(PhantomData<T>); +pub struct TaoBalanceReserve<T: Config>(PhantomData<T>); -impl<T: Config> TokenReserve<TaoBalance> for TaoCurrencyReserve<T> { +impl<T: Config> TokenReserve<TaoBalance> for TaoBalanceReserve<T> { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> TaoBalance { SubnetTAO::<T>::get(netuid).saturating_add(SubnetTaoProvided::<T>::get(netuid)) @@ -2513,9 +2544,9 @@ impl<T: Config> TokenReserve<TaoBalance> for TaoCurrencyReserve<T> { } #[derive(Clone)] -pub struct AlphaCurrencyReserve<T: Config>(PhantomData<T>); +pub struct AlphaBalanceReserve<T: Config>(PhantomData<T>); -impl<T: Config> TokenReserve<AlphaBalance> for AlphaCurrencyReserve<T> { +impl<T: Config> TokenReserve<AlphaBalance> for AlphaBalanceReserve<T> { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> AlphaBalance { SubnetAlphaIn::<T>::get(netuid).saturating_add(SubnetAlphaInProvided::<T>::get(netuid)) @@ -2531,9 +2562,9 @@ impl<T: Config> TokenReserve<AlphaBalance> for AlphaCurrencyReserve<T> { } pub type GetAlphaForTao<T> = - subtensor_swap_interface::GetAlphaForTao<TaoCurrencyReserve<T>, AlphaCurrencyReserve<T>>; + subtensor_swap_interface::GetAlphaForTao<TaoBalanceReserve<T>, AlphaBalanceReserve<T>>; pub type GetTaoForAlpha<T> = - subtensor_swap_interface::GetTaoForAlpha<AlphaCurrencyReserve<T>, TaoCurrencyReserve<T>>; + subtensor_swap_interface::GetTaoForAlpha<AlphaBalanceReserve<T>, TaoBalanceReserve<T>>; impl<T: Config + pallet_balances::Config<Balance = TaoBalance>> subtensor_runtime_common::SubnetInfo<T::AccountId> for Pallet<T> @@ -2621,20 +2652,25 @@ impl<T: Config + pallet_balances::Config<Balance = TaoBalance>> hotkey: &T::AccountId, netuid: NetUid, alpha: AlphaBalance, - ) -> Result<AlphaBalance, DispatchError> { + ) -> Result<(), DispatchError> { ensure!( Self::hotkey_account_exists(hotkey), Error::<T>::HotKeyAccountNotExists ); + ensure!( + Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) >= alpha, + Error::<T>::InsufficientBalance + ); + // Decrese alpha out counter SubnetAlphaOut::<T>::mutate(netuid, |total| { *total = total.saturating_sub(alpha); }); - Ok(Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, coldkey, netuid, alpha, - )) + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); + + Ok(()) } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 6d5eb257b3..f70b83f52d 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -711,7 +711,7 @@ mod dispatches { /// #[pallet::call_index(2)] #[pallet::weight((Weight::from_parts(340_800_000, 0) - .saturating_add(T::DbWeight::get().reads(25_u64)) + .saturating_add(T::DbWeight::get().reads(27_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake( origin: OriginFor<T>, @@ -1064,8 +1064,8 @@ mod dispatches { )] #[pallet::call_index(70)] #[pallet::weight((Weight::from_parts(275_300_000, 0) - .saturating_add(T::DbWeight::get().reads(52_u64)) - .saturating_add(T::DbWeight::get().writes(35_u64)), DispatchClass::Normal, Pays::No))] + .saturating_add(T::DbWeight::get().reads(57_u64)) + .saturating_add(T::DbWeight::get().writes(39_u64)), DispatchClass::Normal, Pays::No))] pub fn swap_hotkey( origin: OriginFor<T>, hotkey: T::AccountId, @@ -1526,7 +1526,7 @@ mod dispatches { /// - Thrown if key has hit transaction rate limit #[pallet::call_index(84)] #[pallet::weight((Weight::from_parts(358_500_000, 0) - .saturating_add(T::DbWeight::get().reads(40_u64)) + .saturating_add(T::DbWeight::get().reads(44_u64)) .saturating_add(T::DbWeight::get().writes(24_u64)), DispatchClass::Normal, Pays::Yes))] pub fn unstake_all_alpha(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) @@ -1555,7 +1555,7 @@ mod dispatches { /// #[pallet::call_index(85)] #[pallet::weight((Weight::from_parts(164_300_000, 0) - .saturating_add(T::DbWeight::get().reads(15_u64)) + .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)), DispatchClass::Normal, Pays::Yes))] pub fn move_stake( origin: OriginFor<T>, @@ -1598,7 +1598,7 @@ mod dispatches { /// May emit a `StakeTransferred` event on success. #[pallet::call_index(86)] #[pallet::weight((Weight::from_parts(160_300_000, 0) - .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)), DispatchClass::Normal, Pays::Yes))] pub fn transfer_stake( origin: OriginFor<T>, @@ -1640,7 +1640,7 @@ mod dispatches { #[pallet::call_index(87)] #[pallet::weight(( Weight::from_parts(351_300_000, 0) - .saturating_add(T::DbWeight::get().reads(36_u64)) + .saturating_add(T::DbWeight::get().reads(40_u64)) .saturating_add(T::DbWeight::get().writes(22_u64)), DispatchClass::Normal, Pays::Yes @@ -1705,7 +1705,7 @@ mod dispatches { /// #[pallet::call_index(88)] #[pallet::weight((Weight::from_parts(402_900_000, 0) - .saturating_add(T::DbWeight::get().reads(25_u64)) + .saturating_add(T::DbWeight::get().reads(27_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)), DispatchClass::Normal, Pays::Yes))] pub fn add_stake_limit( origin: OriginFor<T>, @@ -1770,7 +1770,7 @@ mod dispatches { /// #[pallet::call_index(89)] #[pallet::weight((Weight::from_parts(377_400_000, 0) - .saturating_add(T::DbWeight::get().reads(28_u64)) + .saturating_add(T::DbWeight::get().reads(30_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_limit( origin: OriginFor<T>, @@ -1814,7 +1814,7 @@ mod dispatches { #[pallet::call_index(90)] #[pallet::weight(( Weight::from_parts(411_500_000, 0) - .saturating_add(T::DbWeight::get().reads(36_u64)) + .saturating_add(T::DbWeight::get().reads(40_u64)) .saturating_add(T::DbWeight::get().writes(22_u64)), DispatchClass::Normal, Pays::Yes @@ -1933,7 +1933,7 @@ mod dispatches { /// Emits a `TokensRecycled` event on success. #[pallet::call_index(101)] #[pallet::weight(( - Weight::from_parts(113_400_000, 0).saturating_add(T::DbWeight::get().reads_writes(7, 4)), + Weight::from_parts(113_400_000, 0).saturating_add(T::DbWeight::get().reads_writes(9, 4)), DispatchClass::Normal, Pays::Yes ))] @@ -1958,7 +1958,7 @@ mod dispatches { /// Emits a `TokensBurned` event on success. #[pallet::call_index(102)] #[pallet::weight(( - Weight::from_parts(112_200_000, 0).saturating_add(T::DbWeight::get().reads_writes(7, 3)), + Weight::from_parts(112_200_000, 0).saturating_add(T::DbWeight::get().reads_writes(9, 3)), DispatchClass::Normal, Pays::Yes ))] @@ -1989,7 +1989,7 @@ mod dispatches { /// Without limit_price it remove all the stake similar to `remove_stake` extrinsic #[pallet::call_index(103)] #[pallet::weight((Weight::from_parts(395_300_000, 10142) - .saturating_add(T::DbWeight::get().reads(28_u64)) + .saturating_add(T::DbWeight::get().reads(30_u64)) .saturating_add(T::DbWeight::get().writes(13_u64)), DispatchClass::Normal, Pays::Yes))] pub fn remove_stake_full_limit( origin: OriginFor<T>, @@ -2265,7 +2265,7 @@ mod dispatches { #[pallet::call_index(121)] #[pallet::weight(( Weight::from_parts(117_000_000, 7767) - .saturating_add(T::DbWeight::get().reads(12_u64)) + .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)), DispatchClass::Normal, Pays::Yes @@ -2458,7 +2458,7 @@ mod dispatches { /// #[pallet::call_index(127)] #[pallet::weight( - Weight::from_parts(20_750_000, 0) + Weight::from_parts(30_810_000, 0) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) )] @@ -2587,7 +2587,7 @@ mod dispatches { #[pallet::call_index(132)] #[pallet::weight(( Weight::from_parts(368_000_000, 8556) - .saturating_add(T::DbWeight::get().reads(28_u64)) + .saturating_add(T::DbWeight::get().reads(30_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)), DispatchClass::Normal, Pays::Yes diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index cad73081c6..637704f970 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -276,8 +276,6 @@ mod errors { VotingPowerTrackingNotEnabled, /// Invalid voting power EMA alpha value (must be <= 10^18). InvalidVotingPowerEmaAlpha, - /// Unintended precision loss when unstaking alpha - PrecisionLoss, /// Deprecated call. Deprecated, /// "Add stake and burn" exceeded the operation rate limit diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 02c84edb25..0370ad2e06 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -528,10 +528,24 @@ mod events { /// Alpha burned alpha: AlphaBalance, }, + /// A coldkey swap announcement has been cleared. ColdkeySwapCleared { /// The account ID of the coldkey that cleared the announcement. who: T::AccountId, }, + + /// Transaction fee was paid in Alpha. + /// + /// Emitted in addition to `TransactionFeePaid` when the fee payment path is Alpha. + /// `alpha_fee` is the exact Alpha amount deducted. + TransactionFeePaidWithAlpha { + /// Account that paid the transaction fee. + who: T::AccountId, + /// Exact fee deducted in Alpha units. + alpha_fee: AlphaBalance, + /// Resulting swapped TAO amount + tao_amount: TaoBalance, + }, } } diff --git a/pallets/subtensor/src/macros/genesis.rs b/pallets/subtensor/src/macros/genesis.rs index d4e269391e..a074bdcef1 100644 --- a/pallets/subtensor/src/macros/genesis.rs +++ b/pallets/subtensor/src/macros/genesis.rs @@ -91,20 +91,20 @@ mod genesis { SubnetOwner::<T>::insert(netuid, hotkey.clone()); SubnetLocked::<T>::insert(netuid, TaoBalance::from(1)); LargestLocked::<T>::insert(netuid, 1); - Alpha::<T>::insert( + AlphaV2::<T>::insert( // Lock the initial funds making this key the owner. (hotkey.clone(), hotkey.clone(), netuid), - U64F64::saturating_from_num(1_000_000_000), + SafeFloat::from(1_000_000_000), ); TotalHotkeyAlpha::<T>::insert( hotkey.clone(), netuid, AlphaBalance::from(1_000_000_000), ); - TotalHotkeyShares::<T>::insert( + TotalHotkeySharesV2::<T>::insert( hotkey.clone(), netuid, - U64F64::saturating_from_num(1_000_000_000), + SafeFloat::from(1_000_000_000), ); SubnetAlphaOut::<T>::insert(netuid, AlphaBalance::from(1_000_000_000)); let mut staking_hotkeys = StakingHotkeys::<T>::get(hotkey.clone()); diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 899e8d32f2..8b6ead17c0 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -166,7 +166,10 @@ mod hooks { // Fix staking hot keys .saturating_add(migrations::migrate_fix_staking_hot_keys::migrate_fix_staking_hot_keys::<T>()) // Migrate coldkey swap scheduled to announcements - .saturating_add(migrations::migrate_coldkey_swap_scheduled_to_announcements::migrate_coldkey_swap_scheduled_to_announcements::<T>()); + .saturating_add(migrations::migrate_coldkey_swap_scheduled_to_announcements::migrate_coldkey_swap_scheduled_to_announcements::<T>()) + // Remove orphaned axon/prometheus/certificate entries (follow-up to v1, + // accumulated while serve_axon checked registration on any network) + .saturating_add(migrations::migrate_remove_orphan_axon_prom_cert_v2::migrate_remove_orphan_axon_prom_cert_v2::<T>()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_remove_orphan_axon_prom_cert_v2.rs b/pallets/subtensor/src/migrations/migrate_remove_orphan_axon_prom_cert_v2.rs new file mode 100644 index 0000000000..f4f4f23c0d --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_remove_orphan_axon_prom_cert_v2.rs @@ -0,0 +1,93 @@ +use super::*; +use crate::HasMigrationRun; +use frame_support::{traits::Get, weights::Weight}; +use scale_info::prelude::string::String; +use sp_std::collections::btree_set::BTreeSet; + +/// Remove Axon, Prometheus, and NeuronCertificate entries for hotkeys that are not +/// registered on the respective subnet. +/// +/// This is a follow-up to `migrate_remove_neuron_axon_cert_prom`. The bug in +/// `serve_axon` / `serve_prometheus` (checking registration on *any* network instead +/// of the target netuid) allowed new orphaned entries to accumulate after that first +/// migration ran. This migration clears those entries. +pub fn migrate_remove_orphan_axon_prom_cert_v2<T: Config>() -> Weight { + let migration_name = b"migrate_remove_orphan_axon_prom_cert_v2".to_vec(); + let mut weight: Weight = T::DbWeight::get().reads(1); + + // Skip if already executed. + if HasMigrationRun::<T>::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{}' already run - skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + log::info!( + target: "runtime", + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + for network in NetworksAdded::<T>::iter_keys() { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + let hotkeys = BTreeSet::from_iter(Uids::<T>::iter_key_prefix(network)); + weight.saturating_accrue(T::DbWeight::get().reads(hotkeys.len() as u64)); + + // Axons + let axons = Axons::<T>::iter_key_prefix(network).collect::<Vec<_>>(); + weight.saturating_accrue(T::DbWeight::get().reads(axons.len() as u64)); + let mut cleaned_axons: u32 = 0; + for axon_hotkey in axons { + if !hotkeys.contains(&axon_hotkey) { + Axons::<T>::remove(network, &axon_hotkey); + cleaned_axons = cleaned_axons.saturating_add(1); + } + } + weight.saturating_accrue(T::DbWeight::get().writes(cleaned_axons as u64)); + + // Prometheus + let prometheus = Prometheus::<T>::iter_key_prefix(network).collect::<Vec<_>>(); + weight.saturating_accrue(T::DbWeight::get().reads(prometheus.len() as u64)); + let mut cleaned_prometheus: u32 = 0; + for prometheus_hotkey in prometheus { + if !hotkeys.contains(&prometheus_hotkey) { + Prometheus::<T>::remove(network, &prometheus_hotkey); + cleaned_prometheus = cleaned_prometheus.saturating_add(1); + } + } + weight.saturating_accrue(T::DbWeight::get().writes(cleaned_prometheus as u64)); + + // NeuronCertificates + let certificates = NeuronCertificates::<T>::iter_key_prefix(network).collect::<Vec<_>>(); + weight.saturating_accrue(T::DbWeight::get().reads(certificates.len() as u64)); + let mut cleaned_certificates: u32 = 0; + for certificate_hotkey in certificates { + if !hotkeys.contains(&certificate_hotkey) { + NeuronCertificates::<T>::remove(network, &certificate_hotkey); + cleaned_certificates = cleaned_certificates.saturating_add(1); + } + } + weight.saturating_accrue(T::DbWeight::get().writes(cleaned_certificates as u64)); + + if cleaned_axons > 0 || cleaned_prometheus > 0 || cleaned_certificates > 0 { + log::info!( + target: "runtime", + "Cleaned {cleaned_axons} axons, {cleaned_prometheus} prometheus, \ + {cleaned_certificates} neuron certificates for network {network}" + ); + } + } + + HasMigrationRun::<T>::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 23a2899b94..29415b6670 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -36,6 +36,7 @@ pub mod migrate_rate_limiting_last_blocks; pub mod migrate_remove_commitments_rate_limit; pub mod migrate_remove_network_modality; pub mod migrate_remove_old_identity_maps; +pub mod migrate_remove_orphan_axon_prom_cert_v2; pub mod migrate_remove_stake_map; pub mod migrate_remove_tao_dividends; pub mod migrate_remove_total_hotkey_coldkey_stakes_this_interval; diff --git a/pallets/subtensor/src/rpc_info/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index 61c0286e7a..b69ea6f778 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -65,8 +65,8 @@ impl<T: Config> Pallet<T> { alpha_share_pools.push(alpha_share_pool); } - for ((nominator, netuid), alpha_stake) in Alpha::<T>::iter_prefix((delegate.clone(),)) { - if alpha_stake == 0 { + for (nominator, netuid, alpha_stake) in Self::alpha_iter_single_prefix(&delegate) { + if alpha_stake.is_zero() { continue; } @@ -166,7 +166,7 @@ impl<T: Config> Pallet<T> { )> = Vec::new(); for delegate in <Delegates<T> as IterableStorageMap<T::AccountId, u16>>::iter_keys() { // Staked to this delegate, so add to list - for (netuid, _) in Alpha::<T>::iter_prefix((delegate.clone(), delegatee.clone())) { + for (netuid, _) in Self::alpha_iter_prefix((&delegate, &delegatee)) { let delegate_info = Self::get_delegate_by_existing_account(delegate.clone(), true); delegates.push(( delegate_info, diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index bfe34b2ab0..5c785f199b 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -1,3 +1,4 @@ +use alloc::collections::BTreeMap; use frame_support::traits::{ Imbalance, tokens::{ @@ -6,6 +7,7 @@ use frame_support::traits::{ }, }; use safe_math::*; +use share_pool::SafeFloat; use substrate_fixed::types::U96F32; use subtensor_runtime_common::{NetUid, TaoBalance}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -68,7 +70,7 @@ impl<T: Config> Pallet<T> { hotkeys .iter() .map(|hotkey| { - Alpha::<T>::iter_prefix((hotkey, coldkey)) + Self::alpha_iter_prefix((hotkey, coldkey)) .map(|(netuid, _)| { let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, netuid, @@ -101,7 +103,7 @@ impl<T: Config> Pallet<T> { hotkeys .iter() .map(|hotkey| { - Alpha::<T>::iter_prefix((hotkey, coldkey)) + Self::alpha_iter_prefix((hotkey, coldkey)) .map(|(netuid_on_storage, _)| { if netuid_on_storage == netuid { let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -261,7 +263,7 @@ impl<T: Config> Pallet<T> { /// used with caution. pub fn clear_small_nominations() { // Loop through all staking accounts to identify and clear nominations below the minimum stake. - for ((hotkey, coldkey, netuid), _) in Alpha::<T>::iter() { + for ((hotkey, coldkey, netuid), _) in Self::alpha_iter() { Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid); } } @@ -413,7 +415,149 @@ impl<T: Config> Pallet<T> { } } + // Same thing as populate_root_coldkey_staking_maps, but for AlphaV2 + // TODO: Remove this function and AlphaV2MapLastKey when slow migration is finished + pub fn populate_root_coldkey_staking_maps_v2() { + // Get starting key for the batch. Get the first key if we restart the process. + let mut new_starting_raw_key = AlphaV2MapLastKey::<T>::get(); + let mut starting_key = None; + if new_starting_raw_key.is_none() { + starting_key = AlphaV2::<T>::iter_keys().next(); + new_starting_raw_key = starting_key.as_ref().map(AlphaV2::<T>::hashed_key_for); + } + + if let Some(starting_raw_key) = new_starting_raw_key { + // Get the key batch + let mut keys = AlphaV2::<T>::iter_keys_from(starting_raw_key) + .take(ALPHA_MAP_BATCH_SIZE) + .collect::<Vec<_>>(); + + // New iteration: insert the starting key in the batch if it's a new iteration + // iter_keys_from() skips the starting key + if let Some(starting_key) = starting_key { + if keys.len() == ALPHA_MAP_BATCH_SIZE { + keys.remove(keys.len().saturating_sub(1)); + } + keys.insert(0, starting_key); + } + + let mut new_starting_key = None; + let new_iteration = keys.len() < ALPHA_MAP_BATCH_SIZE; + + // Check and remove alphas if necessary + for key in keys { + let (_, coldkey, netuid) = key.clone(); + + if netuid == NetUid::ROOT { + Self::maybe_add_coldkey_index(&coldkey); + } + + new_starting_key = Some(AlphaV2::<T>::hashed_key_for(key)); + } + + // Restart the process if it's the last batch + if new_iteration { + new_starting_key = None; + } + + AlphaV2MapLastKey::<T>::put(new_starting_key); + } + } + pub fn burn_subnet_alpha(_netuid: NetUid, _amount: AlphaBalance) { // Do nothing; TODO: record burned alpha in a tracker } + + /// Several alpha iteration helpers that merge key space from Alpha and AlphaV2 maps + pub fn alpha_iter() -> impl Iterator<Item = ((T::AccountId, T::AccountId, NetUid), SafeFloat)> { + // Old Alpha shares format: U64F64 -> SafeFloat + let legacy = Alpha::<T>::iter().map(|(key, val_u64f64)| { + let sf: SafeFloat = val_u64f64.into(); + (key, sf) + }); + + // New Alpha shares format + let v2 = AlphaV2::<T>::iter(); + + // Merge and prefer v2 on duplicates + let merged: BTreeMap<_, SafeFloat> = + legacy + .chain(v2) + .fold(BTreeMap::new(), |mut acc, (key, val)| { + acc.entry(key) + .and_modify(|existing| { + *existing = val.clone(); + }) + .or_insert(val); + acc + }); + + merged.into_iter() + } + + pub fn alpha_iter_prefix( + prefix: (&T::AccountId, &T::AccountId), + ) -> impl Iterator<Item = (NetUid, SafeFloat)> + where + T::AccountId: Clone, + { + // Old Alpha shares format: U64F64 -> SafeFloat + let legacy = Alpha::<T>::iter_prefix(prefix).map(|(netuid, val_u64f64)| { + let sf: SafeFloat = val_u64f64.into(); + (netuid, sf) + }); + + // New Alpha shares format + let v2 = AlphaV2::<T>::iter_prefix(prefix); + + // Merge by netuid and sum SafeFloat values + let merged: BTreeMap<NetUid, SafeFloat> = + legacy + .chain(v2) + .fold(BTreeMap::new(), |mut acc, (netuid, sf)| { + acc.entry(netuid) + .and_modify(|existing| { + *existing = sf.clone(); + }) + .or_insert(sf); + acc + }); + + merged + .into_iter() + .filter(|(_, alpha_share)| !alpha_share.is_zero()) + } + + pub fn alpha_iter_single_prefix( + prefix: &T::AccountId, + ) -> impl Iterator<Item = (T::AccountId, NetUid, SafeFloat)> + where + T::AccountId: Clone, + { + // Old Alpha shares format: U64F64 -> SafeFloat + let legacy = + Alpha::<T>::iter_prefix((prefix.clone(),)).map(|((coldkey, netuid), val_u64f64)| { + let sf: SafeFloat = val_u64f64.into(); + ((coldkey, netuid), sf) + }); + + // New Alpha shares format + let v2 = AlphaV2::<T>::iter_prefix((prefix,)); + + let merged: BTreeMap<(T::AccountId, NetUid), SafeFloat> = + legacy + .chain(v2) + .fold(BTreeMap::new(), |mut acc, (key, sf)| { + acc.entry(key) + .and_modify(|existing| { + *existing = sf.clone(); + }) + .or_insert(sf); + acc + }); + + merged + .into_iter() + .map(|((coldkey, netuid), sf)| (coldkey, netuid, sf)) + } } diff --git a/pallets/subtensor/src/staking/recycle_alpha.rs b/pallets/subtensor/src/staking/recycle_alpha.rs index 07bf2c41bd..bb93c12818 100644 --- a/pallets/subtensor/src/staking/recycle_alpha.rs +++ b/pallets/subtensor/src/staking/recycle_alpha.rs @@ -51,21 +51,12 @@ impl<T: Config> Pallet<T> { ); // Deduct from the coldkey's stake. - let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, &coldkey, netuid, amount, - ); - - ensure!(actual_alpha_decrease <= amount, Error::<T>::PrecisionLoss); + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); // Recycle means we should decrease the alpha issuance tracker. - Self::recycle_subnet_alpha(netuid, actual_alpha_decrease); + Self::recycle_subnet_alpha(netuid, amount); - Self::deposit_event(Event::AlphaRecycled( - coldkey, - hotkey, - actual_alpha_decrease, - netuid, - )); + Self::deposit_event(Event::AlphaRecycled(coldkey, hotkey, amount, netuid)); Ok(()) } @@ -118,21 +109,12 @@ impl<T: Config> Pallet<T> { ); // Deduct from the coldkey's stake. - let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, &coldkey, netuid, amount, - ); - - ensure!(actual_alpha_decrease <= amount, Error::<T>::PrecisionLoss); + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount); - Self::burn_subnet_alpha(netuid, actual_alpha_decrease); + Self::burn_subnet_alpha(netuid, amount); // Deposit event - Self::deposit_event(Event::AlphaBurned( - coldkey, - hotkey, - actual_alpha_decrease, - netuid, - )); + Self::deposit_event(Event::AlphaBurned(coldkey, hotkey, amount, netuid)); Ok(()) } diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 3ee42dbdfd..0750456106 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -480,13 +480,13 @@ impl<T: Config> Pallet<T> { let mut stakers: Vec<(T::AccountId, T::AccountId, u128)> = Vec::new(); let mut total_alpha_value_u128: u128 = 0; - let hotkeys_in_subnet: Vec<T::AccountId> = TotalHotkeyAlpha::<T>::iter() - .filter(|(_, this_netuid, _)| *this_netuid == netuid) - .map(|(hot, _, _)| hot.clone()) + let hotkeys_in_subnet: Vec<T::AccountId> = TotalHotkeyAlpha::<T>::iter_keys() + .filter(|(_, this_netuid)| *this_netuid == netuid) + .map(|(hot, _)| hot.clone()) .collect::<Vec<_>>(); for hot in hotkeys_in_subnet.iter() { - for ((cold, this_netuid), share_u64f64) in Alpha::<T>::iter_prefix((hot,)) { + for (cold, this_netuid, share_u64f64) in Self::alpha_iter_single_prefix(hot) { if this_netuid != netuid { continue; } @@ -498,7 +498,7 @@ impl<T: Config> Pallet<T> { // Fallback: if pool uninitialized, treat raw Alpha share as value. let val_u64 = if actual_val_u64 == 0 { - share_u64f64.saturating_to_num::<u64>() + u64::from(share_u64f64) } else { actual_val_u64 }; @@ -569,12 +569,14 @@ impl<T: Config> Pallet<T> { // 7) Destroy all α-in/α-out state for this subnet. // 7.a) Remove every (hot, cold, netuid) α entry. for (hot, cold) in keys_to_remove { - Alpha::<T>::remove((hot, cold, netuid)); + Alpha::<T>::remove((hot.clone(), cold.clone(), netuid)); + AlphaV2::<T>::remove((hot, cold, netuid)); } // 7.b) Clear share‑pool totals for each hotkey on this subnet. for hot in hotkeys_in_subnet { TotalHotkeyAlpha::<T>::remove(&hot, netuid); TotalHotkeyShares::<T>::remove(&hot, netuid); + TotalHotkeySharesV2::<T>::remove(&hot, netuid); } // 7.c) Remove α‑in/α‑out counters (fully destroyed). SubnetAlphaIn::<T>::remove(netuid); diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index a4987bbd74..cb2d46cef5 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,8 +1,8 @@ use super::*; use safe_math::*; -use share_pool::{SharePool, SharePoolDataOperations}; +use share_pool::{SafeFloat, SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; -use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32}; +use substrate_fixed::types::{I64F64, I96F32, U96F32}; use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, TaoBalance, Token}; use subtensor_swap_interface::{Order, SwapHandler, SwapResult}; @@ -508,7 +508,7 @@ impl<T: Config> Pallet<T> { coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, - ) -> AlphaBalance { + ) { if !amount.is_zero() { let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey); if !staking_hotkeys.contains(hotkey) { @@ -520,11 +520,7 @@ impl<T: Config> Pallet<T> { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); // We expect to add a positive amount here. let amount = amount.to_u64() as i64; - let actual_alpha = alpha_share_pool.update_value_for_one(coldkey, amount); - - // We should return a positive amount, or 0 if the operation failed. - // e.g. the stake was removed due to precision issues. - actual_alpha.max(0).unsigned_abs().into() + alpha_share_pool.update_value_for_one(coldkey, amount); } pub fn try_increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -552,22 +548,16 @@ impl<T: Config> Pallet<T> { coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance, - ) -> AlphaBalance { + ) { let mut alpha_share_pool = Self::get_alpha_share_pool(hotkey.clone(), netuid); let amount = amount.to_u64(); // We expect a negative value here - let mut actual_alpha = 0; if let Ok(value) = alpha_share_pool.try_get_value(coldkey) && value >= amount { - actual_alpha = alpha_share_pool.update_value_for_one(coldkey, (amount as i64).neg()); + alpha_share_pool.update_value_for_one(coldkey, (amount as i64).neg()); } - - // Get the negation of the removed alpha, and clamp at 0. - // This ensures we return a positive value, but only if - // `actual_alpha` was negative (i.e. a decrease in stake). - actual_alpha.neg().max(0).unsigned_abs().into() } /// Swaps TAO for the alpha token on the subnet. @@ -687,15 +677,13 @@ impl<T: Config> Pallet<T> { drop_fees: bool, ) -> Result<TaoBalance, DispatchError> { // Decrease alpha on subnet - let actual_alpha_decrease = - Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); // Swap the alpha for TAO. - let swap_result = - Self::swap_alpha_for_tao(netuid, actual_alpha_decrease, price_limit, drop_fees)?; + let swap_result = Self::swap_alpha_for_tao(netuid, alpha, price_limit, drop_fees)?; // Refund the unused alpha (in case if limit price is hit) - let refund = actual_alpha_decrease.saturating_sub( + let refund = alpha.saturating_sub( swap_result .amount_paid_in .saturating_add(swap_result.fee_paid) @@ -754,7 +742,7 @@ impl<T: Config> Pallet<T> { coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out.into(), - actual_alpha_decrease, + swap_result.amount_paid_in.into(), netuid, swap_result.fee_paid.to_u64(), )); @@ -764,7 +752,7 @@ impl<T: Config> Pallet<T> { coldkey.clone(), hotkey.clone(), swap_result.amount_paid_out, - actual_alpha_decrease, + swap_result.amount_paid_in, netuid, swap_result.fee_paid ); @@ -802,17 +790,12 @@ impl<T: Config> Pallet<T> { ); // Increase the alpha on the hotkey account. - if Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, netuid, swap_result.amount_paid_out.into(), - ) - .is_zero() - || swap_result.amount_paid_out.is_zero() - { - return Ok(AlphaBalance::ZERO); - } + ); // Step 4: Update the list of hotkeys staking for this coldkey let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey); @@ -889,7 +872,7 @@ impl<T: Config> Pallet<T> { alpha: AlphaBalance, ) -> Result<TaoBalance, DispatchError> { // Decrease alpha on origin keys - let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( origin_hotkey, origin_coldkey, netuid, @@ -904,17 +887,17 @@ impl<T: Config> Pallet<T> { } // Increase alpha on destination keys - let actual_alpha_moved = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( destination_hotkey, destination_coldkey, netuid, - actual_alpha_decrease, + alpha, ); if netuid == NetUid::ROOT { Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey( destination_hotkey, destination_coldkey, - actual_alpha_decrease.into(), + u64::from(alpha).into(), ); } @@ -923,7 +906,7 @@ impl<T: Config> Pallet<T> { let current_price = <T as pallet::Config>::SwapInterface::current_alpha_price(netuid.into()); let tao_equivalent: TaoBalance = current_price - .saturating_mul(U96F32::saturating_from_num(actual_alpha_moved)) + .saturating_mul(U96F32::saturating_from_num(alpha)) .saturating_to_num::<u64>() .into(); @@ -952,7 +935,7 @@ impl<T: Config> Pallet<T> { origin_coldkey.clone(), origin_hotkey.clone(), tao_equivalent, - actual_alpha_decrease, + alpha, netuid, 0_u64, // 0 fee )); @@ -960,7 +943,7 @@ impl<T: Config> Pallet<T> { destination_coldkey.clone(), destination_hotkey.clone(), tao_equivalent, - actual_alpha_moved, + alpha, netuid, 0_u64, // 0 fee )); @@ -1331,47 +1314,80 @@ type AlphaShareKey<T> = <T as frame_system::Config>::AccountId; impl<T: Config> SharePoolDataOperations<AlphaShareKey<T>> for HotkeyAlphaSharePoolDataOperations<T> { - fn get_shared_value(&self) -> U64F64 { - U64F64::saturating_from_num(crate::TotalHotkeyAlpha::<T>::get(&self.hotkey, self.netuid)) + fn get_shared_value(&self) -> u64 { + u64::from(TotalHotkeyAlpha::<T>::get(&self.hotkey, self.netuid)) } - fn get_share(&self, key: &AlphaShareKey<T>) -> U64F64 { - crate::Alpha::<T>::get((&(self.hotkey), key, self.netuid)) + fn get_share(&self, key: &AlphaShareKey<T>) -> SafeFloat { + // Read the deprecated Alpha map first and, if value is not available, try new AlphaV2 + let maybe_share_v1 = Alpha::<T>::try_get((&(self.hotkey), key, self.netuid)); + if let Ok(share_v1) = maybe_share_v1 { + return SafeFloat::from(share_v1); + } + + AlphaV2::<T>::get((&(self.hotkey), key, self.netuid)) } - fn try_get_share(&self, key: &AlphaShareKey<T>) -> Result<U64F64, ()> { - crate::Alpha::<T>::try_get((&(self.hotkey), key, self.netuid)) + fn try_get_share(&self, key: &AlphaShareKey<T>) -> Result<SafeFloat, ()> { + // Read the deprecated Alpha map first and, if value is not available, try new AlphaV2 + let maybe_share_v1 = Alpha::<T>::try_get((&(self.hotkey), key, self.netuid)); + if let Ok(share_v1) = maybe_share_v1 { + return Ok(SafeFloat::from(share_v1)); + } + + let maybe_share = AlphaV2::<T>::try_get((&(self.hotkey), key, self.netuid)); + if let Ok(share) = maybe_share { + Ok(share) + } else { + Err(()) + } } - fn get_denominator(&self) -> U64F64 { - crate::TotalHotkeyShares::<T>::get(&(self.hotkey), self.netuid) + fn get_denominator(&self) -> SafeFloat { + // Read the deprecated TotalHotkeyShares map first and, if value is not available, try new TotalHotkeySharesV2 + let maybe_denomnator_v1 = TotalHotkeyShares::<T>::try_get(&(self.hotkey), self.netuid); + if let Ok(denomnator_v1) = maybe_denomnator_v1 { + return SafeFloat::from(denomnator_v1); + } + + TotalHotkeySharesV2::<T>::get(&(self.hotkey), self.netuid) } - fn set_shared_value(&mut self, value: U64F64) { + fn set_shared_value(&mut self, value: u64) { if value != 0 { - crate::TotalHotkeyAlpha::<T>::insert( - &(self.hotkey), - self.netuid, - AlphaBalance::from(value.saturating_to_num::<u64>()), - ); + TotalHotkeyAlpha::<T>::insert(&(self.hotkey), self.netuid, AlphaBalance::from(value)); } else { - crate::TotalHotkeyAlpha::<T>::remove(&(self.hotkey), self.netuid); + TotalHotkeyAlpha::<T>::remove(&(self.hotkey), self.netuid); } } - fn set_share(&mut self, key: &AlphaShareKey<T>, share: U64F64) { - if share != 0 { - crate::Alpha::<T>::insert((&self.hotkey, key, self.netuid), share); + fn set_share(&mut self, key: &AlphaShareKey<T>, share: SafeFloat) { + // Lazy Alpha -> AlphaV2 migration happens right here + // Delete the Alpha entry, insert into AlphaV2 + let maybe_share_v1 = Alpha::<T>::try_get((&(self.hotkey), key, self.netuid)); + if maybe_share_v1.is_ok() { + Alpha::<T>::remove((&self.hotkey, key, self.netuid)); + } + + if !share.is_zero() { + AlphaV2::<T>::insert((&self.hotkey, key, self.netuid), share); } else { - crate::Alpha::<T>::remove((&self.hotkey, key, self.netuid)); + AlphaV2::<T>::remove((&self.hotkey, key, self.netuid)); } } - fn set_denominator(&mut self, update: U64F64) { - if update != 0 { - crate::TotalHotkeyShares::<T>::insert(&self.hotkey, self.netuid, update); + fn set_denominator(&mut self, update: SafeFloat) { + // Lazy TotalHotkeyShares -> TotalHotkeySharesV2 migration happens right here + // Delete the TotalHotkeyShares entry, insert into TotalHotkeySharesV2 + let maybe_denominator_v1 = TotalHotkeyShares::<T>::try_get(&(self.hotkey), self.netuid); + if maybe_denominator_v1.is_ok() { + TotalHotkeyShares::<T>::remove(&self.hotkey, self.netuid); + } + + if !update.is_zero() { + TotalHotkeySharesV2::<T>::insert(&self.hotkey, self.netuid, update); } else { - crate::TotalHotkeyShares::<T>::remove(&self.hotkey, self.netuid); + TotalHotkeySharesV2::<T>::remove(&self.hotkey, self.netuid); } } } diff --git a/pallets/subtensor/src/subnets/serving.rs b/pallets/subtensor/src/subnets/serving.rs index 29923f5ade..ae2d4112a9 100644 --- a/pallets/subtensor/src/subnets/serving.rs +++ b/pallets/subtensor/src/subnets/serving.rs @@ -70,8 +70,8 @@ impl<T: Config> Pallet<T> { // We check the callers (hotkey) signature. let hotkey_id = ensure_signed(origin)?; - // Validate user input - Self::validate_serve_axon( + // Validate user input and build the axon struct. + let mut prev_axon = Self::validate_serve_axon( &hotkey_id, netuid, version, @@ -90,24 +90,8 @@ impl<T: Config> Pallet<T> { NeuronCertificates::<T>::insert(netuid, hotkey_id.clone(), certificate) } - // We insert the axon meta. - let mut prev_axon = Self::get_axon_info(netuid, &hotkey_id); + // Record the block at insert time. prev_axon.block = Self::get_current_block_as_u64(); - prev_axon.version = version; - prev_axon.ip = ip; - prev_axon.port = port; - prev_axon.ip_type = ip_type; - prev_axon.protocol = protocol; - prev_axon.placeholder1 = placeholder1; - prev_axon.placeholder2 = placeholder2; - - // Validate axon data with delegate func - let axon_validated = Self::validate_axon_data(&prev_axon); - ensure!( - axon_validated.is_ok(), - axon_validated.err().unwrap_or(Error::<T>::InvalidPort) - ); - Axons::<T>::insert(netuid, hotkey_id.clone(), prev_axon); // We deposit axon served event. @@ -144,11 +128,8 @@ impl<T: Config> Pallet<T> { /// - On successfully serving the axon info. /// /// # Raises: - /// * 'MechanismDoesNotExist': - /// - Attempting to set weights on a non-existent network. - /// - /// * 'NotRegistered': - /// - Attempting to set weights from a non registered account. + /// * 'HotKeyNotRegisteredInNetwork': + /// - Attempting to serve prometheus from a hotkey not registered on the target network. /// /// * 'InvalidIpType': /// - The ip type is not 4 or 6. @@ -170,6 +151,12 @@ impl<T: Config> Pallet<T> { // We check the callers (hotkey) signature. let hotkey_id = ensure_signed(origin)?; + // Ensure the hotkey is registered on this specific network. + ensure!( + Self::is_hotkey_registered_on_network(netuid, &hotkey_id), + Error::<T>::HotKeyNotRegisteredInNetwork + ); + // Check the ip signature validity. ensure!(Self::is_valid_ip_type(ip_type), Error::<T>::InvalidIpType); ensure!( @@ -177,12 +164,6 @@ impl<T: Config> Pallet<T> { Error::<T>::InvalidIpAddress ); - // Ensure the hotkey is registered somewhere. - ensure!( - Self::is_hotkey_registered_on_any_network(&hotkey_id), - Error::<T>::HotKeyNotRegisteredInNetwork - ); - // We get the previous axon info assoicated with this ( netuid, uid ) let mut prev_prometheus = Self::get_prometheus_info(netuid, &hotkey_id); let current_block: u64 = Self::get_current_block_as_u64(); @@ -328,10 +309,10 @@ impl<T: Config> Pallet<T> { protocol: u8, placeholder1: u8, placeholder2: u8, - ) -> Result<(), Error<T>> { - // Ensure the hotkey is registered somewhere. + ) -> Result<AxonInfoOf, Error<T>> { + // Ensure the hotkey is registered on this specific network. ensure!( - Self::is_hotkey_registered_on_any_network(hotkey_id), + Self::is_hotkey_registered_on_network(netuid, hotkey_id), Error::<T>::HotKeyNotRegisteredInNetwork ); @@ -351,8 +332,8 @@ impl<T: Config> Pallet<T> { Error::<T>::ServingRateLimitExceeded ); - // Validate axon data with delegate func - prev_axon.block = Self::get_current_block_as_u64(); + // Assemble and validate the updated axon state. + prev_axon.block = current_block; prev_axon.version = version; prev_axon.ip = ip; prev_axon.port = port; @@ -367,6 +348,6 @@ impl<T: Config> Pallet<T> { axon_validated.err().unwrap_or(Error::<T>::InvalidPort) ); - Ok(()) + Ok(prev_axon) } } diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 401b5989ec..27fef995b2 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -1,5 +1,5 @@ use super::*; -use substrate_fixed::types::U64F64; +use share_pool::SafeFloat; impl<T: Config> Pallet<T> { /// Transfer all assets, stakes, subnet ownerships, and hotkey associations from `old_coldkey` to @@ -98,19 +98,40 @@ impl<T: Config> Pallet<T> { new_coldkey: &T::AccountId, ) { for hotkey in StakingHotkeys::<T>::get(old_coldkey) { - // Get the stake on the old (hot,coldkey) account. - let old_alpha: U64F64 = Alpha::<T>::get((&hotkey, old_coldkey, netuid)); - // Get the stake on the new (hot,coldkey) account. - let new_alpha: U64F64 = Alpha::<T>::get((&hotkey, new_coldkey, netuid)); - // Add the stake to new account. - Alpha::<T>::insert( - (&hotkey, new_coldkey, netuid), - new_alpha.saturating_add(old_alpha), - ); - // Remove the value from the old account. + // Swap and lazy-migrate Alpha to AlphaV2 + // TotalHotkeyShares does not have to be migrated here, these migrations can be independent + + // Get the v1 alpha shares on the old (hot,coldkey) account. + let orig_alpha_v1: SafeFloat = + SafeFloat::from(Alpha::<T>::get((&hotkey, old_coldkey, netuid))); + // Get the v1 alpha shares on the new (hot,coldkey) account. + let dest_alpha_v1: SafeFloat = + SafeFloat::from(Alpha::<T>::get((&hotkey, new_coldkey, netuid))); + // Get the v2 alpha shares on the old (hot,coldkey) account. + let orig_alpha_v2: SafeFloat = AlphaV2::<T>::get((&hotkey, old_coldkey, netuid)); + // Get the v2 alpha shares on the new (hot,coldkey) account. + let dest_alpha_v2: SafeFloat = AlphaV2::<T>::get((&hotkey, new_coldkey, netuid)); + + // Calculate and save new alpha shares on the destination new_coldkey + let new_dest_alpha = orig_alpha_v1 + .add(&dest_alpha_v1) + .unwrap_or_default() + .add(&orig_alpha_v2) + .unwrap_or_default() + .add(&dest_alpha_v2) + .unwrap_or_default(); + if !new_dest_alpha.is_zero() { + AlphaV2::<T>::insert((&hotkey, new_coldkey, netuid), new_dest_alpha.clone()); + } + + // Remove shares on the origin old_coldkey in both Alpha and AlphaV2 maps Alpha::<T>::remove((&hotkey, old_coldkey, netuid)); + AlphaV2::<T>::remove((&hotkey, old_coldkey, netuid)); + + // Remove shares on the destination new_coldkey in Alpha map + Alpha::<T>::remove((&hotkey, new_coldkey, netuid)); - if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { + if !new_dest_alpha.is_zero() { Self::transfer_root_claimed_for_new_keys( netuid, &hotkey, diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index ba50c78bca..1138ed1cde 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -1,5 +1,6 @@ use super::*; use frame_support::weights::Weight; +use share_pool::SafeFloat; use sp_core::Get; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{MechId, NetUid, Token}; @@ -184,8 +185,8 @@ impl<T: Config> Pallet<T> { keep_stake: bool, ) -> DispatchResult { // 1. keep the old hotkey alpha values for the case where hotkey staked by multiple coldkeys. - let old_alpha_values: Vec<((T::AccountId, NetUid), U64F64)> = - Alpha::<T>::iter_prefix((old_hotkey,)).collect(); + let old_alpha_values: Vec<(T::AccountId, NetUid, SafeFloat)> = + Self::alpha_iter_single_prefix(old_hotkey).collect(); weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values.len() as u64)); // 2. Swap owner. @@ -239,25 +240,26 @@ impl<T: Config> Pallet<T> { weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } - // 10. Alpha already update in perform_hotkey_swap_on_one_subnet + // 10. Alphas already update in perform_hotkey_swap_on_one_subnet // Update the StakingHotkeys for the case where hotkey staked by multiple coldkeys. if !keep_stake { - for ((coldkey, _netuid), _alpha) in old_alpha_values { + for (coldkey, _netuid, alpha_share) in old_alpha_values { // Swap StakingHotkeys. // StakingHotkeys( coldkey ) --> Vec<hotkey> -- the hotkeys that the coldkey stakes. - let mut staking_hotkeys = StakingHotkeys::<T>::get(&coldkey); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - if staking_hotkeys.contains(old_hotkey) { - staking_hotkeys.retain(|hk| *hk != *old_hotkey && *hk != *new_hotkey); - if !staking_hotkeys.contains(new_hotkey) { - staking_hotkeys.push(new_hotkey.clone()); + if !alpha_share.is_zero() { + let mut staking_hotkeys = StakingHotkeys::<T>::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if staking_hotkeys.contains(old_hotkey) { + staking_hotkeys.retain(|hk| *hk != *old_hotkey && *hk != *new_hotkey); + if !staking_hotkeys.contains(new_hotkey) { + staking_hotkeys.push(new_hotkey.clone()); + } + StakingHotkeys::<T>::insert(&coldkey, staking_hotkeys); + weight.saturating_accrue(T::DbWeight::get().writes(1)); } - StakingHotkeys::<T>::insert(&coldkey, staking_hotkeys); - weight.saturating_accrue(T::DbWeight::get().writes(1)); } } } - // Return successful after swapping all the relevant terms. Ok(()) } @@ -358,10 +360,9 @@ impl<T: Config> Pallet<T> { netuid: NetUid, keep_stake: bool, ) -> DispatchResult { - // 1. Swap total hotkey alpha for all subnets it exists on. - // TotalHotkeyAlpha( hotkey, netuid ) -> alpha -- the total alpha that the hotkey has on a specific subnet. - // Only transfer stake when keep_stake is false. if !keep_stake { + // 1. Swap total hotkey alpha for all subnets it exists on. + // TotalHotkeyAlpha( hotkey, netuid ) -> alpha -- the total alpha that the hotkey has on a specific subnet. let alpha = TotalHotkeyAlpha::<T>::take(old_hotkey, netuid); TotalHotkeyAlpha::<T>::mutate(new_hotkey, netuid, |value| { @@ -370,12 +371,24 @@ impl<T: Config> Pallet<T> { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); // 2. Swap total hotkey shares on all subnets it exists on. - // TotalHotkeyShares( hotkey, netuid ) -> alpha -- the total alpha that the hotkey has on a specific subnet. - let share = TotalHotkeyShares::<T>::take(old_hotkey, netuid); - TotalHotkeyShares::<T>::mutate(new_hotkey, netuid, |value| { - *value = value.saturating_add(share) - }); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + // TotalHotkeyShares( hotkey, netuid ) -> share pool denominator for this hotkey on this subnet. + // Merge v1 and v2 TotalHotkeyShares because TotalHotkeyShares v1 is deprecated + weight.saturating_accrue(T::DbWeight::get().reads(4)); + let old_share_v1 = SafeFloat::from(TotalHotkeyShares::<T>::take(old_hotkey, netuid)); + let old_share_v2 = TotalHotkeySharesV2::<T>::take(old_hotkey, netuid); + let total_old_shares = old_share_v1.add(&old_share_v2).unwrap_or_default(); + + let new_share_v1 = SafeFloat::from(TotalHotkeyShares::<T>::take(new_hotkey, netuid)); + let new_share_v2 = TotalHotkeySharesV2::<T>::take(new_hotkey, netuid); + let total_new_shares = new_share_v1.add(&new_share_v2).unwrap_or_default(); + + TotalHotkeyShares::<T>::remove(old_hotkey, netuid); + TotalHotkeyShares::<T>::remove(new_hotkey, netuid); + + let total_old_plus_new_shares = + total_new_shares.add(&total_old_shares).unwrap_or_default(); + TotalHotkeySharesV2::<T>::insert(new_hotkey, netuid, total_old_plus_new_shares); + weight.saturating_accrue(T::DbWeight::get().writes(3)); } // 3. Swap all subnet specific info. @@ -543,6 +556,11 @@ impl<T: Config> Pallet<T> { weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values.len() as u64)); weight.saturating_accrue(T::DbWeight::get().writes(old_alpha_values.len() as u64)); + let old_alpha_values_v2: Vec<((T::AccountId, NetUid), SafeFloat)> = + AlphaV2::<T>::iter_prefix((old_hotkey,)).collect(); + weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values_v2.len() as u64)); + weight.saturating_accrue(T::DbWeight::get().writes(old_alpha_values_v2.len() as u64)); + // 9.1. Transfer root claimable Self::transfer_root_claimable_for_new_hotkey(old_hotkey, new_hotkey); @@ -555,9 +573,38 @@ impl<T: Config> Pallet<T> { let new_alpha = Alpha::<T>::take((new_hotkey, &coldkey, netuid)); Alpha::<T>::remove((old_hotkey, &coldkey, netuid)); - Alpha::<T>::insert( + + // Insert into AlphaV2 because Alpha is deprecated + AlphaV2::<T>::insert( + (new_hotkey, &coldkey, netuid), + SafeFloat::from(alpha.saturating_add(new_alpha)), + ); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + + // Swap StakingHotkeys. + // StakingHotkeys( coldkey ) --> Vec<hotkey> -- the hotkeys that the coldkey stakes. + let mut staking_hotkeys = StakingHotkeys::<T>::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if staking_hotkeys.contains(old_hotkey) && !staking_hotkeys.contains(new_hotkey) + { + staking_hotkeys.push(new_hotkey.clone()); + StakingHotkeys::<T>::insert(&coldkey, staking_hotkeys); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } + } + } + + for ((coldkey, netuid_alpha), alpha) in old_alpha_values_v2 { + if netuid == netuid_alpha { + Self::transfer_root_claimed_for_new_keys( + netuid, old_hotkey, new_hotkey, &coldkey, &coldkey, + ); + + let new_alpha_v2 = AlphaV2::<T>::take((new_hotkey, &coldkey, netuid)); + AlphaV2::<T>::remove((old_hotkey, &coldkey, netuid)); + AlphaV2::<T>::insert( (new_hotkey, &coldkey, netuid), - alpha.saturating_add(new_alpha), + alpha.add(&new_alpha_v2).unwrap_or_default(), ); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 7f19c96b3d..e4662342c6 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -3122,3 +3122,68 @@ fn test_migrate_coldkey_swap_scheduled_to_announcements() { ); }); } + +#[test] +fn test_migrate_remove_orphan_axon_prom_cert_v2() { + use crate::migrations::migrate_remove_orphan_axon_prom_cert_v2::*; + const MIGRATION_NAME: &[u8] = b"migrate_remove_orphan_axon_prom_cert_v2"; + + new_test_ext(1).execute_with(|| { + setup_for(NetUid::from(10), 4, 7); + setup_for(NetUid::from(20), 8, 10); + assert!(!HasMigrationRun::<Test>::get(MIGRATION_NAME)); + + let w = migrate_remove_orphan_axon_prom_cert_v2::<Test>(); + assert!(!w.is_zero(), "Weight must be non-zero"); + + assert!(HasMigrationRun::<Test>::get(MIGRATION_NAME)); + // Only uids entries remain; extra orphan entries (uid+1..items) are gone. + assert_for(NetUid::from(10), 4, 7); + assert_for(NetUid::from(20), 8, 10); + + // Running again is a no-op (returns early). + let w2 = migrate_remove_orphan_axon_prom_cert_v2::<Test>(); + assert!(!w2.is_zero()); + }); + + fn setup_for(netuid: NetUid, uids: u32, items: u32) { + NetworksAdded::<Test>::insert(netuid, true); + for i in 1u32..=uids { + let hk = U256::from(netuid.inner() as u32 * 1000 + i); + Uids::<Test>::insert(netuid, hk, i as u16); + } + for i in 1u32..=items { + let hk = U256::from(netuid.inner() as u32 * 1000 + i); + Axons::<Test>::insert(netuid, hk, AxonInfo::default()); + NeuronCertificates::<Test>::insert(netuid, hk, NeuronCertificate::default()); + Prometheus::<Test>::insert(netuid, hk, PrometheusInfo::default()); + } + } + + fn assert_for(netuid: NetUid, uids: u32, items: u32) { + assert_eq!( + Axons::<Test>::iter_key_prefix(netuid).count(), + uids as usize + ); + assert_eq!( + NeuronCertificates::<Test>::iter_key_prefix(netuid).count(), + uids as usize + ); + assert_eq!( + Prometheus::<Test>::iter_key_prefix(netuid).count(), + uids as usize + ); + for i in 1u32..=uids { + let hk = U256::from(netuid.inner() as u32 * 1000 + i); + assert!(Axons::<Test>::contains_key(netuid, hk)); + assert!(NeuronCertificates::<Test>::contains_key(netuid, hk)); + assert!(Prometheus::<Test>::contains_key(netuid, hk)); + } + for i in (uids + 1)..=items { + let hk = U256::from(netuid.inner() as u32 * 1000 + i); + assert!(!Axons::<Test>::contains_key(netuid, hk)); + assert!(!NeuronCertificates::<Test>::contains_key(netuid, hk)); + assert!(!Prometheus::<Test>::contains_key(netuid, hk)); + } + } +} diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index deb4cd7fc5..772909638b 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -20,6 +20,7 @@ use frame_system as system; use frame_system::{EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase}; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_utility as pallet_utility; +use share_pool::SafeFloat; use sp_core::{ConstU64, Get, H256, U256, offchain::KeyTypeId}; use sp_runtime::Perbill; use sp_runtime::{ @@ -331,8 +332,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = TaoCurrencyReserve<Self>; - type AlphaReserve = AlphaCurrencyReserve<Self>; + type TaoReserve = TaoBalanceReserve<Self>; + type AlphaReserve = AlphaBalanceReserve<Self>; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; @@ -1023,3 +1024,14 @@ pub fn commit_dummy(who: U256, netuid: NetUid) { hash )); } + +#[allow(dead_code)] +pub fn sf_to_u128(sf: &SafeFloat) -> u128 { + let alpha_f64: f64 = sf.into(); + alpha_f64 as u128 +} + +#[allow(dead_code)] +pub fn sf_from_u64(val: u64) -> SafeFloat { + SafeFloat::from(val) +} diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index f6a50cf4ff..859c325c1f 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -1,4 +1,4 @@ -#![allow(clippy::expect_used)] +#![allow(clippy::expect_used, clippy::indexing_slicing)] use super::mock::*; use crate::migrations::migrate_network_immunity_period; @@ -7,7 +7,7 @@ use frame_support::{assert_err, assert_ok}; use frame_system::Config; use sp_core::U256; use sp_std::collections::{btree_map::BTreeMap, vec_deque::VecDeque}; -use substrate_fixed::types::{I96F32, U64F64, U96F32}; +use substrate_fixed::types::{I96F32, U96F32}; use subtensor_runtime_common::{MechId, NetUidStorageIndex, TaoBalance}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -100,7 +100,7 @@ fn dissolve_single_alpha_out_staker_gets_all_tao() { // 2. Single α-out staker let (s_hot, s_cold) = (U256::from(100), U256::from(200)); - Alpha::<Test>::insert((s_hot, s_cold, net), U64F64::from_num(5_000u128)); + AlphaV2::<Test>::insert((s_hot, s_cold, net), sf_from_u64(5_000u64)); // Entire TAO pot should be paid to staker's cold-key let pot: u64 = 99_999; @@ -119,7 +119,7 @@ fn dissolve_single_alpha_out_staker_gets_all_tao() { assert_eq!(after, before + pot.into()); // No α entries left for dissolved subnet - assert!(Alpha::<Test>::iter().all(|((_h, _c, n), _)| n != net)); + assert!(AlphaV2::<Test>::iter().all(|((_h, _c, n), _)| n != net)); assert!(!SubnetTAO::<Test>::contains_key(net)); }); } @@ -137,14 +137,14 @@ fn dissolve_two_stakers_pro_rata_distribution() { let reg_at = NetworkRegisteredAt::<Test>::get(net); NetworkRegistrationStartBlock::<Test>::put(reg_at.saturating_add(1)); - let (s1_hot, s1_cold, a1) = (U256::from(201), U256::from(301), 300u128); - let (s2_hot, s2_cold, a2) = (U256::from(202), U256::from(302), 700u128); + let (s1_hot, s1_cold, a1) = (U256::from(201), U256::from(301), 300u64); + let (s2_hot, s2_cold, a2) = (U256::from(202), U256::from(302), 700u64); - Alpha::<Test>::insert((s1_hot, s1_cold, net), U64F64::from_num(a1)); - Alpha::<Test>::insert((s2_hot, s2_cold, net), U64F64::from_num(a2)); + AlphaV2::<Test>::insert((s1_hot, s1_cold, net), sf_from_u64(a1)); + AlphaV2::<Test>::insert((s2_hot, s2_cold, net), sf_from_u64(a2)); - TotalHotkeyAlpha::<Test>::insert(s1_hot, net, AlphaBalance::from(a1 as u64)); - TotalHotkeyAlpha::<Test>::insert(s2_hot, net, AlphaBalance::from(a2 as u64)); + TotalHotkeyAlpha::<Test>::insert(s1_hot, net, AlphaBalance::from(a1)); + TotalHotkeyAlpha::<Test>::insert(s2_hot, net, AlphaBalance::from(a2)); let pot: u64 = 10_000; SubnetTAO::<Test>::insert(net, TaoBalance::from(pot)); @@ -156,9 +156,9 @@ fn dissolve_two_stakers_pro_rata_distribution() { let owner_before = SubtensorModule::get_coldkey_balance(&oc); // Expected τ shares with largest remainder - let total = a1 + a2; - let prod1 = a1 * (pot as u128); - let prod2 = a2 * (pot as u128); + let total = (a1 + a2) as u128; + let prod1 = (a1 as u128) * (pot as u128); + let prod2 = (a2 as u128) * (pot as u128); let share1 = (prod1 / total) as u64; let share2 = (prod2 / total) as u64; let mut distributed = share1 + share2; @@ -202,7 +202,7 @@ fn dissolve_two_stakers_pro_rata_distribution() { ); // α entries for dissolved subnet gone - assert!(Alpha::<Test>::iter().all(|((_h, _c, n), _)| n != net)); + assert!(AlphaV2::<Test>::iter().all(|((_h, _c, n), _)| n != net)); }); } @@ -635,7 +635,7 @@ fn dissolve_alpha_out_but_zero_tao_no_rewards() { let sh = U256::from(23); let sc = U256::from(24); - Alpha::<Test>::insert((sh, sc, net), U64F64::from_num(1_000u64)); + AlphaV2::<Test>::insert((sh, sc, net), sf_from_u64(1_000u64)); SubnetTAO::<Test>::insert(net, TaoBalance::from(0)); // zero TAO SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0)); Emission::<Test>::insert(net, Vec::<AlphaBalance>::new()); @@ -647,7 +647,7 @@ fn dissolve_alpha_out_but_zero_tao_no_rewards() { // No reward distributed, α-out cleared. assert_eq!(after, before); - assert!(Alpha::<Test>::iter().next().is_none()); + assert!(AlphaV2::<Test>::iter().next().is_none()); }); } @@ -679,8 +679,8 @@ fn dissolve_rounding_remainder_distribution() { let (s1h, s1c) = (U256::from(63), U256::from(64)); let (s2h, s2c) = (U256::from(65), U256::from(66)); - Alpha::<Test>::insert((s1h, s1c, net), U64F64::from_num(3u128)); - Alpha::<Test>::insert((s2h, s2c, net), U64F64::from_num(2u128)); + AlphaV2::<Test>::insert((s1h, s1c, net), sf_from_u64(3u64)); + AlphaV2::<Test>::insert((s2h, s2c, net), sf_from_u64(2u64)); SubnetTAO::<Test>::insert(net, TaoBalance::from(1)); // TAO pot = 1 SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0)); @@ -703,7 +703,7 @@ fn dissolve_rounding_remainder_distribution() { assert_eq!(c2_after, c2_before); // α records for subnet gone; TAO key gone - assert!(Alpha::<Test>::iter().all(|((_h, _c, n), _)| n != net)); + assert!(AlphaV2::<Test>::iter().all(|((_h, _c, n), _)| n != net)); assert!(!SubnetTAO::<Test>::contains_key(net)); }); } @@ -748,8 +748,8 @@ fn destroy_alpha_out_multiple_stakers_pro_rata() { )); // 4. α-out snapshot - let a1: u128 = Alpha::<Test>::get((h1, c1, netuid)).saturating_to_num(); - let a2: u128 = Alpha::<Test>::get((h2, c2, netuid)).saturating_to_num(); + let a1: u128 = sf_to_u128(&AlphaV2::<Test>::get((h1, c1, netuid))); + let a2: u128 = sf_to_u128(&AlphaV2::<Test>::get((h2, c2, netuid))); let atotal = a1 + a2; // 5. TAO pot & lock @@ -799,8 +799,8 @@ fn destroy_alpha_out_multiple_stakers_pro_rata() { ); // 11. α entries cleared for the subnet - assert!(!Alpha::<Test>::contains_key((h1, c1, netuid))); - assert!(!Alpha::<Test>::contains_key((h2, c2, netuid))); + assert!(!AlphaV2::<Test>::contains_key((h1, c1, netuid))); + assert!(!AlphaV2::<Test>::contains_key((h2, c2, netuid))); }); } @@ -856,7 +856,7 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { let mut alpha = [0u128; N]; let mut alpha_sum: u128 = 0; for i in 0..N { - alpha[i] = Alpha::<Test>::get((hot[i], cold[i], netuid)).saturating_to_num(); + alpha[i] = sf_to_u128(&AlphaV2::<Test>::get((hot[i], cold[i], netuid))); alpha_sum += alpha[i]; } @@ -937,7 +937,7 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { ); // α cleared for dissolved subnet & related counters reset - assert!(Alpha::<Test>::iter().all(|((_h, _c, n), _)| n != netuid)); + assert!(AlphaV2::<Test>::iter().all(|((_h, _c, n), _)| n != netuid)); assert_eq!(SubnetAlphaIn::<Test>::get(netuid), 0.into()); assert_eq!(SubnetAlphaOut::<Test>::get(netuid), 0.into()); assert_eq!(SubtensorModule::get_subnet_locked_balance(netuid), 0.into()); @@ -1778,7 +1778,6 @@ fn test_tempo_greater_than_weight_set_rate_limit() { }) } -#[allow(clippy::indexing_slicing)] #[test] fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state() { new_test_ext(0).execute_with(|| { @@ -1816,7 +1815,7 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( ]; // ──────────────────────────────────────────────────────────────────── - // 1) Create many subnets, enable V3, fix price at tick=0 (sqrt≈1) + // 1) Create many subnets, fix price at tick=0 // ──────────────────────────────────────────────────────────────────── let mut nets: Vec<NetUid> = Vec::new(); for i in 0..NUM_NETS { @@ -1828,20 +1827,6 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( Emission::<Test>::insert(net, Vec::<AlphaBalance>::new()); SubtensorModule::set_subnet_locked_balance(net, TaoBalance::from(0)); - assert_ok!( - pallet_subtensor_swap::Pallet::<Test>::toggle_user_liquidity( - RuntimeOrigin::root(), - net, - true - ) - ); - - // Price/tick pinned so LP math stays stable (sqrt(1)). - let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1) price"); - pallet_subtensor_swap::CurrentTick::<Test>::set(net, ct0); - pallet_subtensor_swap::AlphaSqrtPrice::<Test>::set(net, sqrt1); - nets.push(net); } @@ -1922,10 +1907,10 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( } // Capture **pair‑level** α snapshot per net (pre‑LP). - for ((hot, cold, net), amt) in Alpha::<Test>::iter() { + for ((hot, cold, net), amt) in AlphaV2::<Test>::iter() { if let Some(&ni) = net_index.get(&net) && lp_sets_per_net[ni].contains(&cold) { - let a: u128 = amt.saturating_to_num(); + let a: u128 = sf_to_u128(&amt); if a > 0 { alpha_pairs_per_net .entry(net) @@ -2050,53 +2035,17 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( // For each dissolved net, check α ledgers gone, network removed, and swap state clean. for &net in nets.iter() { assert!( - Alpha::<Test>::iter().all(|((_h, _c, n), _)| n != net), + AlphaV2::<Test>::iter().all(|((_h, _c, n), _)| n != net), "alpha ledger not fully cleared for net {net:?}" ); assert!( !SubtensorModule::if_subnet_exist(net), "subnet {net:?} still exists" ); - assert!( - pallet_subtensor_swap::Ticks::<Test>::iter_prefix(net) - .next() - .is_none(), - "ticks not cleared for net {net:?}" - ); - assert!( - !pallet_subtensor_swap::Positions::<Test>::iter() - .any(|((n, _owner, _pid), _)| n == net), - "swap positions not fully cleared for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::FeeGlobalTao::<Test>::get(net).saturating_to_num::<u64>(), - 0, - "FeeGlobalTao nonzero for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::FeeGlobalAlpha::<Test>::get(net).saturating_to_num::<u64>(), - 0, - "FeeGlobalAlpha nonzero for net {net:?}" - ); - assert_eq!( - pallet_subtensor_swap::CurrentLiquidity::<Test>::get(net), - 0, - "CurrentLiquidity not zero for net {net:?}" - ); assert!( !pallet_subtensor_swap::SwapV3Initialized::<Test>::get(net), "SwapV3Initialized still set" ); - assert!( - !pallet_subtensor_swap::EnabledUserLiquidity::<Test>::get(net), - "EnabledUserLiquidity still set" - ); - assert!( - pallet_subtensor_swap::TickIndexBitmapWords::<Test>::iter_prefix((net,)) - .next() - .is_none(), - "TickIndexBitmapWords not cleared for net {net:?}" - ); } // ──────────────────────────────────────────────────────────────────── @@ -2111,18 +2060,6 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( Emission::<Test>::insert(net_new, Vec::<AlphaBalance>::new()); SubtensorModule::set_subnet_locked_balance(net_new, TaoBalance::from(0)); - assert_ok!( - pallet_subtensor_swap::Pallet::<Test>::toggle_user_liquidity( - RuntimeOrigin::root(), - net_new, - true - ) - ); - let ct0 = pallet_subtensor_swap::tick::TickIndex::new_unchecked(0); - let sqrt1 = ct0.try_to_sqrt_price().expect("sqrt(1)"); - pallet_subtensor_swap::CurrentTick::<Test>::set(net_new, ct0); - pallet_subtensor_swap::AlphaSqrtPrice::<Test>::set(net_new, sqrt1); - // Compute the exact min stake per the pallet rule: DefaultMinStake + fee(DefaultMinStake). let min_stake = DefaultMinStake::<Test>::get(); let order = GetAlphaForTao::<Test>::with_amount(min_stake); @@ -2142,7 +2079,7 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( register_ok_neuron(net_new, hot1, cold, 7777); let before_tao = SubtensorModule::get_coldkey_balance(&cold); - let a_prev: u64 = Alpha::<Test>::get((hot1, cold, net_new)).saturating_to_num(); + let a_prev: u64 = sf_to_u128(&AlphaV2::<Test>::get((hot1, cold, net_new))) as u64; // Expected α for this exact τ, using the same sim path as the pallet. let order = GetAlphaForTao::<Test>::with_amount(min_amount_required); @@ -2161,7 +2098,7 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( )); let after_tao = SubtensorModule::get_coldkey_balance(&cold); - let a_new: u64 = Alpha::<Test>::get((hot1, cold, net_new)).saturating_to_num(); + let a_new: u64 = sf_to_u128(&AlphaV2::<Test>::get((hot1, cold, net_new))) as u64; let a_delta = a_new.saturating_sub(a_prev); // τ decreased by exactly the amount we sent. diff --git a/pallets/subtensor/src/tests/recycle_alpha.rs b/pallets/subtensor/src/tests/recycle_alpha.rs index 9ae3975744..404967dc74 100644 --- a/pallets/subtensor/src/tests/recycle_alpha.rs +++ b/pallets/subtensor/src/tests/recycle_alpha.rs @@ -3,8 +3,9 @@ use super::mock::*; use crate::*; use approx::assert_abs_diff_eq; use frame_support::{assert_noop, assert_ok, traits::Currency}; +use share_pool::SafeFloat; use sp_core::U256; -use substrate_fixed::types::{U64F64, U96F32}; +use substrate_fixed::types::U96F32; use subtensor_runtime_common::{AlphaBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -546,75 +547,93 @@ fn test_burn_errors() { } #[test] -fn test_recycle_precision_loss() { +fn test_recycle_precision() { new_test_ext(1).execute_with(|| { let coldkey = U256::from(1); let hotkey = U256::from(2); let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + let tao_reserve = TaoBalance::from(1_000_000_000_u64); + let alpha_reserve = AlphaBalance::from(1_000_000_000_u64); + SubnetAlphaIn::<Test>::insert(netuid, alpha_reserve); + SubnetTAO::<Test>::insert(netuid, tao_reserve); Balances::make_free_balance_be(&coldkey, 1_000_000_000.into()); // sanity check assert!(SubtensorModule::if_subnet_exist(netuid)); // add stake to coldkey-hotkey pair so we can recycle it - let stake = 200_000; increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake.into(), netuid); // amount to recycle let recycle_amount = AlphaBalance::from(stake / 2); - // Modify the alpha pool denominator so it's low-precision - let denominator = U64F64::from_num(0.00000001); - TotalHotkeyShares::<Test>::insert(hotkey, netuid, denominator); - Alpha::<Test>::insert((&hotkey, &coldkey, netuid), denominator); + // Modify the alpha pool denominator so it's low-precision (denominator = share = 1e-9) + let denominator = SafeFloat::from(1) + .div(&SafeFloat::from(1_000_000_000)) + .unwrap_or_default(); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, denominator.clone()); + AlphaV2::<Test>::insert((&hotkey, &coldkey, netuid), denominator); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); // recycle, expect error due to precision loss - assert_noop!( - SubtensorModule::recycle_alpha( - RuntimeOrigin::signed(coldkey), - hotkey, - recycle_amount, - netuid - ), - Error::<Test>::PrecisionLoss + assert_ok!(SubtensorModule::recycle_alpha( + RuntimeOrigin::signed(coldkey), + hotkey, + recycle_amount, + netuid + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake / 2) ); }); } #[test] -fn test_burn_precision_loss() { +fn test_burn_precision() { new_test_ext(1).execute_with(|| { let coldkey = U256::from(1); let hotkey = U256::from(2); let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + let tao_reserve = TaoBalance::from(1_000_000_000_u64); + let alpha_reserve = AlphaBalance::from(1_000_000_000_u64); + SubnetAlphaIn::<Test>::insert(netuid, alpha_reserve); + SubnetTAO::<Test>::insert(netuid, tao_reserve); Balances::make_free_balance_be(&coldkey, 1_000_000_000.into()); // sanity check assert!(SubtensorModule::if_subnet_exist(netuid)); // add stake to coldkey-hotkey pair so we can recycle it - let stake = 200_000; increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake.into(), netuid); // amount to recycle let burn_amount = AlphaBalance::from(stake / 2); - // Modify the alpha pool denominator so it's low-precision - let denominator = U64F64::from_num(0.00000001); - TotalHotkeyShares::<Test>::insert(hotkey, netuid, denominator); - Alpha::<Test>::insert((&hotkey, &coldkey, netuid), denominator); + // Modify the alpha pool denominator so it's low-precision (denominator = share = 1e-9) + let denominator = SafeFloat::from(1) + .div(&SafeFloat::from(1_000_000_000)) + .unwrap_or_default(); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, denominator.clone()); + AlphaV2::<Test>::insert((&hotkey, &coldkey, netuid), denominator); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); // burn, expect error due to precision loss - assert_noop!( - SubtensorModule::burn_alpha( - RuntimeOrigin::signed(coldkey), - hotkey, - burn_amount, - netuid - ), - Error::<Test>::PrecisionLoss + assert_ok!(SubtensorModule::burn_alpha( + RuntimeOrigin::signed(coldkey), + hotkey, + burn_amount, + netuid + )); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake / 2) ); }); } diff --git a/pallets/subtensor/src/tests/serving.rs b/pallets/subtensor/src/tests/serving.rs index 2979d4438c..4832a2542a 100644 --- a/pallets/subtensor/src/tests/serving.rs +++ b/pallets/subtensor/src/tests/serving.rs @@ -1242,3 +1242,118 @@ fn test_serve_axon_validate() { assert_ok!(result_ok); }); } + +/// serve_axon must reject a hotkey registered on a different subnet, not the target one. +#[test] +fn test_serve_axon_rejects_hotkey_registered_on_other_network() { + new_test_ext(1).execute_with(|| { + let hotkey = U256::from(42); + let netuid_registered = NetUid::from(1); + let netuid_target = NetUid::from(2); + let tempo: u16 = 13; + + // Register the hotkey on netuid 1 only. + add_network(netuid_registered, tempo, 0); + add_network(netuid_target, tempo, 0); + register_ok_neuron(netuid_registered, hotkey, U256::from(99), 0); + + // Trying to serve on netuid 2 (not registered there) must fail. + assert_noop!( + SubtensorModule::serve_axon( + <<Test as Config>::RuntimeOrigin>::signed(hotkey), + netuid_target, + 2, + 1676056785, + 128, + 4, + 0, + 0, + 0, + ), + Error::<Test>::HotKeyNotRegisteredInNetwork + ); + }); +} + +/// serve_axon succeeds only when the hotkey is registered on the exact target subnet. +#[test] +fn test_serve_axon_requires_registration_on_target_network() { + new_test_ext(1).execute_with(|| { + let hotkey = U256::from(7); + let netuid = NetUid::from(3); + + add_network(netuid, 13, 0); + register_ok_neuron(netuid, hotkey, U256::from(8), 0); + + assert_ok!(SubtensorModule::serve_axon( + <<Test as Config>::RuntimeOrigin>::signed(hotkey), + netuid, + 2, + 1676056785, + 128, + 4, + 0, + 0, + 0, + )); + }); +} + +/// serve_prometheus must reject a hotkey registered on a different subnet. +#[test] +fn test_serve_prometheus_rejects_hotkey_registered_on_other_network() { + new_test_ext(1).execute_with(|| { + let hotkey = U256::from(55); + let netuid_registered = NetUid::from(5); + let netuid_target = NetUid::from(6); + + add_network(netuid_registered, 13, 0); + add_network(netuid_target, 13, 0); + register_ok_neuron(netuid_registered, hotkey, U256::from(56), 0); + + assert_noop!( + SubtensorModule::serve_prometheus( + <<Test as Config>::RuntimeOrigin>::signed(hotkey), + netuid_target, + 2, + 1676056785, + 9090, + 4, + ), + Error::<Test>::HotKeyNotRegisteredInNetwork + ); + }); +} + +/// serve_axon must not write a NeuronCertificate when the hotkey is not registered on the target subnet. +#[test] +fn test_serve_axon_with_cert_rejects_unregistered_hotkey() { + new_test_ext(1).execute_with(|| { + let hotkey = U256::from(100); + let netuid_registered = NetUid::from(1); + let netuid_target = NetUid::from(2); + + add_network(netuid_registered, 13, 0); + add_network(netuid_target, 13, 0); + register_ok_neuron(netuid_registered, hotkey, U256::from(101), 0); + + assert_noop!( + SubtensorModule::serve_axon( + <<Test as Config>::RuntimeOrigin>::signed(hotkey), + netuid_target, + 2, + 1676056785, + 128, + 4, + 0, + 0, + 0, + ), + Error::<Test>::HotKeyNotRegisteredInNetwork + ); + assert!( + !NeuronCertificates::<Test>::contains_key(netuid_target, hotkey), + "no certificate should be written when registration check fails" + ); + }); +} diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 0b82fa27eb..28d5353ce7 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -8,6 +8,7 @@ use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use frame_system::RawOrigin; use pallet_subtensor_swap::tick::TickIndex; use safe_math::FixedExt; +use share_pool::SafeFloat; use sp_core::{Get, H256, U256}; use substrate_fixed::traits::FromFixed; use substrate_fixed::types::{I96F32, I110F18, U64F64, U96F32}; @@ -4123,9 +4124,9 @@ fn test_add_stake_specific_stake_into_subnet_fail() { }); } -// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_99_999_per_cent_stake_removes_all --exact --show-output +// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_99_999_per_cent_stake_works_precisely --exact --show-output #[test] -fn test_remove_99_9991_per_cent_stake_removes_all() { +fn test_remove_99_9991_per_cent_stake_works_precisely() { new_test_ext(1).execute_with(|| { let subnet_owner_coldkey = U256::from(1); let subnet_owner_hotkey = U256::from(2); @@ -4161,7 +4162,7 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { (U64F64::from_num(alpha) * U64F64::from_num(0.999991)).to_num::<u64>(), ); // we expected the entire stake to be returned - let (expected_balance, _) = mock::swap_alpha_to_tao(netuid, alpha); + let (expected_balance, _) = mock::swap_alpha_to_tao(netuid, remove_amount); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -4175,16 +4176,13 @@ fn test_remove_99_9991_per_cent_stake_removes_all() { expected_balance, epsilon = 10.into(), ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), - TaoBalance::ZERO - ); + assert!(!SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id).is_zero()); let new_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &coldkey_account_id, netuid, ); - assert!(new_alpha.is_zero()); + assert_eq!(new_alpha, alpha - remove_amount); }); } @@ -5653,3 +5651,447 @@ fn test_staking_records_flow() { ); }); } + +// cargo test --package pallet-subtensor --lib -- tests::staking::test_lazy_sharepool_migration_get_stake_reads_from_deprecated_alpha_map --exact --nocapture +#[test] +fn test_lazy_sharepool_migration_get_stake_reads_from_deprecated_alpha_map() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map + Alpha::<Test>::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake) + ); + }); +} + +#[test] +fn test_lazy_sharepool_migration_get_stake_reads_from_alpha_v2_map() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to AlphaV2 map + AlphaV2::<Test>::insert((hotkey, coldkey, netuid), SafeFloat::from(1_u64)); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, SafeFloat::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake) + ); + }); +} + +#[test] +fn test_lazy_sharepool_migration_get_stake_reads_from_cross_alpha_maps() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to Alpha map + Alpha::<Test>::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + // but total shares are in TotalHotkeySharesV2 map (already migrated) + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, SafeFloat::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + AlphaBalance::from(stake) + ); + }); +} + +#[test] +fn test_lazy_sharepool_migration_staking_causes_migration() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map + Alpha::<Test>::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Stake more via stake_into_subnet + increase_stake_on_coldkey_hotkey_account(&coldkey, &hotkey, stake.into(), netuid); + + // Verify that deprecated v1 map values are gone + assert!(Alpha::<Test>::try_get((&hotkey, &coldkey, netuid)).is_err()); + assert!(TotalHotkeyShares::<Test>::try_get(hotkey, netuid).is_err()); + + // Verify that v2 map values are present + let migrated_share = AlphaV2::<Test>::get((&hotkey, &coldkey, netuid)); + let migrated_denominator = TotalHotkeySharesV2::<Test>::get(hotkey, netuid); + + assert_abs_diff_eq!( + f64::from((migrated_share.div(&migrated_denominator)).unwrap()), + 1.0, + epsilon = 0.000000000000001 + ); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map + Alpha::<Test>::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to AlphaV2 map + let share = sf_from_u64(1_u64); + AlphaV2::<Test>::insert((hotkey, coldkey, netuid), share.clone()); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_mixed_v1_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha map and new THS v2 map + let share = sf_from_u64(1_u64); + Alpha::<Test>::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_mixed_v2_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 map and deprecated THS map + let share = sf_from_u64(1_u64); + AlphaV2::<Test>::insert((hotkey, coldkey, netuid), share); + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_from_shares_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated THS map + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value_from_shares + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let current_share = SafeFloat::from(U64F64::from(1_u64)); + let actual_value = share_pool.get_value_from_shares(current_share); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_from_shares_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new THS v2 map + let share = sf_from_u64(1_u64); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value_from_shares + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let current_share = SafeFloat::from(U64F64::from(1_u64)); + let actual_value = share_pool.get_value_from_shares(current_share); + + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_all() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 map + let share = sf_from_u64(1_u64); + AlphaV2::<Test>::insert((hotkey, coldkey, netuid), share.clone()); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_all + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_all(stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + share_pool.update_value_for_all(-(stake as i64)); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_v1_with_migration() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha and THS maps + Alpha::<Test>::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + // Verify deletion from deprecated + assert!(!Alpha::<Test>::contains_key((hotkey, coldkey, netuid))); + assert!(!TotalHotkeyShares::<Test>::contains_key(hotkey, netuid)); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 and THS maps + let share = sf_from_u64(1_u64); + AlphaV2::<Test>::insert((hotkey, coldkey, netuid), share.clone()); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_mixed_v1_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to deprecated Alpha and new THS v2 maps + let share = sf_from_u64(1_u64); + Alpha::<Test>::insert((hotkey, coldkey, netuid), U64F64::from(1_u64)); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + // Verify deletion from deprecated + assert!(!Alpha::<Test>::contains_key((hotkey, coldkey, netuid))); + }); +} + +#[test] +fn test_sharepool_dataops_update_value_for_one_mixed_v2_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add stake to new AlphaV2 and deprecated THS maps + let share = sf_from_u64(1_u64); + AlphaV2::<Test>::insert((hotkey, coldkey, netuid), share); + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and call update_value_for_one + let mut share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + share_pool.update_value_for_one(&coldkey, stake as i64); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, stake * 2); + + // Verify deletion from deprecated + assert!(!TotalHotkeyShares::<Test>::contains_key(hotkey, netuid)); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_returns_zero_on_non_existing_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to deprecated THS map, but no value in Alpha map + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, 0_u64); + }); +} + +#[test] +fn test_sharepool_dataops_get_value_returns_zero_on_non_existing_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to THSV2 map, but no value in AlphaV2 map + let share = sf_from_u64(1_u64); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let actual_value = share_pool.get_value(&coldkey); + assert_eq!(actual_value, 0_u64); + }); +} + +#[test] +fn test_sharepool_dataops_try_get_value_returns_err_on_non_existing_v1() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to deprecated THS map, but no value in Alpha map + TotalHotkeyShares::<Test>::insert(hotkey, netuid, U64F64::from(1_u64)); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let maybe_actual_value = share_pool.try_get_value(&coldkey); + assert!(maybe_actual_value.is_err()); + }); +} + +#[test] +fn test_sharepool_dataops_try_get_value_returns_err_on_non_existing_v2() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + + let netuid = add_dynamic_network(&hotkey, &coldkey); + let stake = 200_000_u64; + + // add to THSV2 map, but no value in AlphaV2 map + let share = sf_from_u64(1_u64); + TotalHotkeySharesV2::<Test>::insert(hotkey, netuid, share); + TotalHotkeyAlpha::<Test>::insert(hotkey, netuid, AlphaBalance::from(stake)); + + // Get real share pool and read get_value + let share_pool = SubtensorModule::get_alpha_share_pool(hotkey, netuid); + let maybe_actual_value = share_pool.try_get_value(&coldkey); + assert!(maybe_actual_value.is_err()); + }); +} diff --git a/pallets/subtensor/src/tests/staking2.rs b/pallets/subtensor/src/tests/staking2.rs index 7de304e80e..536a14579a 100644 --- a/pallets/subtensor/src/tests/staking2.rs +++ b/pallets/subtensor/src/tests/staking2.rs @@ -429,12 +429,9 @@ fn test_share_based_staking_denominator_precision() { netuid, stake_amount, ); - assert_eq!( - stake_amount, - Alpha::<Test>::get((hotkey1, coldkey1, netuid)) - .to_num::<u64>() - .into(), - ); + + let actual_stake: f64 = AlphaV2::<Test>::get((hotkey1, coldkey1, netuid)).into(); + assert_eq!(stake_amount, (actual_stake as u64).into(),); SubtensorModule::decrease_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &coldkey1, @@ -445,15 +442,7 @@ fn test_share_based_staking_denominator_precision() { let stake1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &coldkey1, netuid, ); - let expected_remaining_stake = if (stake_amount.to_u64() as f64 - - unstake_amount.to_u64() as f64) - / (stake_amount.to_u64() as f64) - <= 0.00001 - { - AlphaBalance::ZERO - } else { - stake_amount - unstake_amount - }; + let expected_remaining_stake = stake_amount - unstake_amount; assert_eq!(stake1, expected_remaining_stake); }); }); diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index ced89f95f9..512f83dd1d 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -16,6 +16,7 @@ use frame_support::traits::schedule::DispatchTime; use frame_support::traits::schedule::v3::Named as ScheduleNamed; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; +use share_pool::SafeFloat; use sp_core::{Get, H256, U256}; use sp_runtime::traits::Hash; use sp_runtime::traits::{DispatchInfoOf, DispatchTransaction, TransactionExtension}; @@ -1355,21 +1356,15 @@ fn test_do_swap_coldkey_effect_on_delegations() { approx_total_stake, epsilon = approx_total_stake / 100.into() ); - assert_eq!( - expected_stake, - Alpha::<Test>::get((delegate, new_coldkey, netuid)) - .to_num::<u64>() - .into(), - ); - assert_eq!(Alpha::<Test>::get((delegate, coldkey, netuid)), 0); + let actual_stake_new: u64 = AlphaV2::<Test>::get((delegate, new_coldkey, netuid)).into(); + assert_eq!(expected_stake, actual_stake_new.into()); + let actual_stake_old: u64 = AlphaV2::<Test>::get((delegate, coldkey, netuid)).into(); + assert_eq!(actual_stake_old, 0u64); - assert_eq!( - expected_stake, - Alpha::<Test>::get((delegate, new_coldkey, netuid2)) - .to_num::<u64>() - .into() - ); - assert_eq!(Alpha::<Test>::get((delegate, coldkey, netuid2)), 0); + let actual_stake_new_2: u64 = AlphaV2::<Test>::get((delegate, new_coldkey, netuid2)).into(); + assert_eq!(expected_stake, actual_stake_new_2.into()); + let actual_stake_old_2: u64 = AlphaV2::<Test>::get((delegate, coldkey, netuid2)).into(); + assert_eq!(actual_stake_old_2, 0u64); }); } diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index d7f5a1274f..d0a1de3526 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -5,6 +5,7 @@ use codec::Encode; use frame_support::weights::Weight; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; +use share_pool::SafeFloat; use sp_core::{Get, H160, H256, U256}; use sp_runtime::SaturatedConversion; use substrate_fixed::types::U64F64; @@ -877,7 +878,7 @@ fn test_swap_owner_new_hotkey_already_exists() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_stake_success --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey::test_swap_stake_success --exact --nocapture #[test] fn test_swap_stake_success() { new_test_ext(1).execute_with(|| { @@ -934,7 +935,11 @@ fn test_swap_stake_success() { ); assert_eq!( TotalHotkeyShares::<Test>::get(new_hotkey, netuid), - U64F64::from_num(shares) + U64F64::from_num(0) + ); + assert_eq!( + TotalHotkeySharesV2::<Test>::get(new_hotkey, netuid), + shares.into() ); assert_eq!( Alpha::<Test>::get((old_hotkey, coldkey, netuid)), @@ -942,7 +947,92 @@ fn test_swap_stake_success() { ); assert_eq!( Alpha::<Test>::get((new_hotkey, coldkey, netuid)), - U64F64::from_num(amount) + U64F64::from_num(0) + ); + assert_eq!( + f64::from(AlphaV2::<Test>::get((new_hotkey, coldkey, netuid))), + amount as f64 + ); + assert_eq!( + AlphaDividendsPerSubnet::<Test>::get(netuid, old_hotkey), + AlphaBalance::ZERO + ); + assert_eq!( + AlphaDividendsPerSubnet::<Test>::get(netuid, new_hotkey), + amount.into() + ); + }); +} + +#[test] +fn test_swap_stake_v2_success() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let amount = 10_000; + let shares = U64F64::from_num(123456); + let mut weight = Weight::zero(); + + // Initialize staking variables for old_hotkey + TotalHotkeyAlpha::<Test>::insert(old_hotkey, netuid, AlphaBalance::from(amount)); + TotalHotkeyAlphaLastEpoch::<Test>::insert( + old_hotkey, + netuid, + AlphaBalance::from(amount * 2), + ); + TotalHotkeySharesV2::<Test>::insert(old_hotkey, netuid, SafeFloat::from(shares)); + AlphaV2::<Test>::insert( + (old_hotkey, coldkey, netuid), + SafeFloat::from(U64F64::from_num(amount)), + ); + AlphaDividendsPerSubnet::<Test>::insert(netuid, old_hotkey, AlphaBalance::from(amount)); + + // Perform the swap + SubtensorModule::perform_hotkey_swap_on_all_subnets( + &old_hotkey, + &new_hotkey, + &coldkey, + &mut weight, + false, + ); + + // Verify the swap + assert_eq!( + TotalHotkeyAlpha::<Test>::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlpha::<Test>::get(new_hotkey, netuid), + amount.into() + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::<Test>::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::<Test>::get(new_hotkey, netuid), + AlphaBalance::from(amount * 2) + ); + assert_eq!( + f64::from(TotalHotkeySharesV2::<Test>::get(old_hotkey, netuid)), + 0_f64 + ); + assert_abs_diff_eq!( + f64::from(TotalHotkeySharesV2::<Test>::get(new_hotkey, netuid)), + shares.to_num::<f64>(), + epsilon = 0.0000000001 + ); + assert_eq!( + f64::from(AlphaV2::<Test>::get((old_hotkey, coldkey, netuid))), + 0_f64 + ); + assert_eq!( + f64::from(AlphaV2::<Test>::get((new_hotkey, coldkey, netuid))), + amount as f64 ); assert_eq!( AlphaDividendsPerSubnet::<Test>::get(netuid, old_hotkey), @@ -985,8 +1075,8 @@ fn test_swap_stake_old_hotkey_not_exist() { false, ); - // Verify that new_hotkey has the stake and old_hotkey does not - assert!(Alpha::<Test>::contains_key((new_hotkey, coldkey, netuid))); + // Verify that new_hotkey has the stake (in new AlphaV2 map) and old_hotkey does not + assert!(AlphaV2::<Test>::contains_key((new_hotkey, coldkey, netuid))); assert!(!Alpha::<Test>::contains_key((old_hotkey, coldkey, netuid))); }); } diff --git a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs index 6f43d46fde..eb310d1202 100644 --- a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs +++ b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs @@ -9,6 +9,7 @@ use subtensor_runtime_common::{AlphaBalance, NetUidStorageIndex, TaoBalance, Tok use super::mock::*; use crate::*; +use share_pool::SafeFloat; use sp_core::{Get, H160, H256, U256}; use sp_runtime::SaturatedConversion; use std::collections::BTreeSet; @@ -933,7 +934,7 @@ fn test_swap_owner_new_hotkey_already_exists() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey_with_subnet -- test_swap_stake_success --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_swap_stake_success --exact --nocapture #[test] fn test_swap_stake_success() { new_test_ext(1).execute_with(|| { @@ -991,7 +992,12 @@ fn test_swap_stake_success() { ); assert_eq!( TotalHotkeyShares::<Test>::get(new_hotkey, netuid), - U64F64::from_num(shares) + U64F64::from_num(0) + ); + assert_abs_diff_eq!( + f64::from(TotalHotkeySharesV2::<Test>::get(new_hotkey, netuid)), + shares.to_num::<f64>(), + epsilon = 0.0000000001 ); assert_eq!( Alpha::<Test>::get((old_hotkey, coldkey, netuid)), @@ -999,7 +1005,93 @@ fn test_swap_stake_success() { ); assert_eq!( Alpha::<Test>::get((new_hotkey, coldkey, netuid)), - U64F64::from_num(amount) + U64F64::from_num(0) + ); + assert_eq!( + f64::from(AlphaV2::<Test>::get((new_hotkey, coldkey, netuid))), + amount as f64 + ); + assert_eq!( + AlphaDividendsPerSubnet::<Test>::get(netuid, old_hotkey), + AlphaBalance::ZERO + ); + assert_eq!( + AlphaDividendsPerSubnet::<Test>::get(netuid, new_hotkey), + AlphaBalance::from(amount) + ); + }); +} + +#[test] +fn test_swap_stake_v2_success() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&old_hotkey, &coldkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + let amount = 10_000; + let shares = U64F64::from_num(123456); + + // Initialize staking variables for old_hotkey + TotalHotkeyAlpha::<Test>::insert(old_hotkey, netuid, AlphaBalance::from(amount)); + TotalHotkeyAlphaLastEpoch::<Test>::insert( + old_hotkey, + netuid, + AlphaBalance::from(amount * 2), + ); + TotalHotkeySharesV2::<Test>::insert(old_hotkey, netuid, SafeFloat::from(shares)); + AlphaV2::<Test>::insert( + (old_hotkey, coldkey, netuid), + SafeFloat::from(U64F64::from_num(amount)), + ); + AlphaDividendsPerSubnet::<Test>::insert(netuid, old_hotkey, AlphaBalance::from(amount)); + + // Perform the swap + System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &old_hotkey, + &new_hotkey, + Some(netuid), + false, + ),); + + // Verify the swap + assert_eq!( + TotalHotkeyAlpha::<Test>::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlpha::<Test>::get(new_hotkey, netuid), + AlphaBalance::from(amount) + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::<Test>::get(old_hotkey, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::<Test>::get(new_hotkey, netuid), + AlphaBalance::from(amount * 2) + ); + assert_eq!( + f64::from(TotalHotkeySharesV2::<Test>::get(old_hotkey, netuid)), + 0_f64 + ); + assert_abs_diff_eq!( + f64::from(TotalHotkeySharesV2::<Test>::get(new_hotkey, netuid)), + shares.to_num::<f64>(), + epsilon = 0.0000000001 + ); + assert_eq!( + f64::from(AlphaV2::<Test>::get((old_hotkey, coldkey, netuid))), + 0_f64 + ); + assert_eq!( + f64::from(AlphaV2::<Test>::get((new_hotkey, coldkey, netuid))), + amount as f64 ); assert_eq!( AlphaDividendsPerSubnet::<Test>::get(netuid, old_hotkey), @@ -2205,16 +2297,18 @@ fn test_revert_hotkey_swap_dividends() { ); assert_eq!( TotalHotkeyShares::<Test>::get(hk2, netuid), - U64F64::from_num(shares) + U64F64::from_num(0) ); + assert_eq!(TotalHotkeySharesV2::<Test>::get(hk2, netuid), shares.into()); assert_eq!( Alpha::<Test>::get((hk1, coldkey, netuid)), U64F64::from_num(0) ); assert_eq!( Alpha::<Test>::get((hk2, coldkey, netuid)), - U64F64::from_num(amount) + U64F64::from_num(0) ); + assert_eq!(AlphaV2::<Test>::get((hk2, coldkey, netuid)), amount.into()); assert_eq!( AlphaDividendsPerSubnet::<Test>::get(netuid, hk1), AlphaBalance::ZERO @@ -2261,8 +2355,13 @@ fn test_revert_hotkey_swap_dividends() { ); assert_eq!( TotalHotkeyShares::<Test>::get(hk1, netuid), - U64F64::from_num(shares), - "hk1 TotalHotkeyShares must be restored after revert" + U64F64::from_num(0), + "hk1 TotalHotkeyShares must be migrated to v2" + ); + assert_eq!( + TotalHotkeySharesV2::<Test>::get(hk1, netuid), + shares.into(), + "hk1 TotalHotkeyShares must be restored to v2 after revert" ); assert_eq!( Alpha::<Test>::get((hk2, coldkey, netuid)), @@ -2271,8 +2370,13 @@ fn test_revert_hotkey_swap_dividends() { ); assert_eq!( Alpha::<Test>::get((hk1, coldkey, netuid)), - U64F64::from_num(amount), - "hk1 Alpha must be restored after revert" + U64F64::from_num(0), + "hk1 Alpha must be migrated to v2" + ); + assert_eq!( + AlphaV2::<Test>::get((hk1, coldkey, netuid)), + amount.into(), + "hk1 Alpha must be restored to v2 after revert" ); assert_eq!( AlphaDividendsPerSubnet::<Test>::get(netuid, hk2), diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index 9fbeefd3b6..1a1cd0156e 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -47,6 +47,7 @@ pub trait SwapHandler { fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResult; fn toggle_user_liquidity(netuid: NetUid, enabled: bool); fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; + fn get_alpha_amount_for_tao(netuid: NetUid, tao_amount: TaoBalance) -> AlphaBalance; } pub trait DefaultPriceLimit<PaidIn, PaidOut> diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index b12fd4d567..5063972082 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -289,9 +289,9 @@ impl BalanceOps<AccountId> for MockBalanceOps { _coldkey: &AccountId, _hotkey: &AccountId, _netuid: NetUid, - alpha: AlphaBalance, - ) -> Result<AlphaBalance, DispatchError> { - Ok(alpha) + _alpha: AlphaBalance, + ) -> Result<(), DispatchError> { + Ok(()) } } diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index cc309216bb..0eda907567 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -1157,4 +1157,23 @@ impl<T: Config> SwapHandler for Pallet<T> { fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { Self::do_clear_protocol_liquidity(netuid) } + + /// Get the amount of Alpha that needs to be sold to get a given amount of Tao + fn get_alpha_amount_for_tao(netuid: NetUid, tao_amount: TaoBalance) -> AlphaBalance { + match T::SubnetInfo::mechanism(netuid.into()) { + 1 => { + // For uniswap v3: Use no-slippage method. Amount is supposed to be small, + // hence we can neglect slippage and return slightly lower amount. + let alpha_price = Self::current_price(netuid.into()); + AlphaBalance::from( + U96F32::from(u64::from(tao_amount)) + .safe_div(alpha_price) + .saturating_to_num::<u64>(), + ) + } + + // Static subnet, alpha == tao + _ => u64::from(tao_amount).into(), + } + } } diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 281467277e..f096c80210 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -533,12 +533,7 @@ mod pallet { let tao_provided = T::BalanceOps::decrease_balance(&coldkey, result.tao)?; ensure!(tao_provided == result.tao, Error::<T>::InsufficientBalance); - let alpha_provided = - T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; - ensure!( - alpha_provided == result.alpha, - Error::<T>::InsufficientBalance - ); + T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; // Emit an event Self::deposit_event(Event::LiquidityModified { diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 211020cbba..e2b53c5142 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -2230,15 +2230,10 @@ fn liquidate_v3_refunds_user_funds_and_clears_state() { // Mirror extrinsic bookkeeping: withdraw funds & bump provided‑reserve counters. let tao_taken = <Test as Config>::BalanceOps::decrease_balance(&cold, need_tao.into()) .expect("decrease TAO"); - let alpha_taken = <Test as Config>::BalanceOps::decrease_stake( - &cold, - &hot, - netuid.into(), - need_alpha.into(), - ) - .expect("decrease ALPHA"); + <Test as Config>::BalanceOps::decrease_stake(&cold, &hot, netuid.into(), need_alpha.into()) + .expect("decrease ALPHA"); TaoReserve::increase_provided(netuid.into(), tao_taken); - AlphaReserve::increase_provided(netuid.into(), alpha_taken); + AlphaReserve::increase_provided(netuid.into(), need_alpha.into()); // Users‑only liquidation. assert_ok!(Pallet::<Test>::do_dissolve_all_liquidity_providers(netuid)); @@ -2297,14 +2292,14 @@ fn refund_alpha_single_provider_exact() { let alpha_before_total = alpha_before_hot + alpha_before_owner; // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. - let alpha_taken = <Test as Config>::BalanceOps::decrease_stake( + <Test as Config>::BalanceOps::decrease_stake( &cold, &hot, netuid.into(), alpha_needed.into(), ) .expect("decrease ALPHA"); - AlphaReserve::increase_provided(netuid.into(), alpha_taken); + AlphaReserve::increase_provided(netuid.into(), alpha_needed.into()); // --- Act: users‑only dissolve. assert_ok!(Pallet::<Test>::do_dissolve_all_liquidity_providers(netuid)); @@ -2371,16 +2366,14 @@ fn refund_alpha_multiple_providers_proportional_to_principal() { let a2_before_owner = <Test as Config>::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); let a2_before = a2_before_hot + a2_before_owner; - // Withdraw α and account reserves for each provider. - let a1_taken = - <Test as Config>::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) - .expect("decrease α #1"); - AlphaReserve::increase_provided(netuid.into(), a1_taken); + // Withdraw alpha and account reserves for each provider. + <Test as Config>::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) + .expect("decrease alpha #1"); + AlphaReserve::increase_provided(netuid.into(), a1.into()); - let a2_taken = - <Test as Config>::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) - .expect("decrease α #2"); - AlphaReserve::increase_provided(netuid.into(), a2_taken); + <Test as Config>::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) + .expect("decrease alpha #2"); + AlphaReserve::increase_provided(netuid.into(), a2.into()); // Act assert_ok!(Pallet::<Test>::do_dissolve_all_liquidity_providers(netuid)); @@ -2433,16 +2426,14 @@ fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { let before_owner = <Test as Config>::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); let before_total = before_hot1 + before_hot2 + before_owner; - // Withdraw α from both hotkeys; track provided‑reserve. - let t1 = - <Test as Config>::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) - .expect("decr α #hot1"); - AlphaReserve::increase_provided(netuid.into(), t1); + // Withdraw alpha from both hotkeys; track provided‑reserve. + <Test as Config>::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) + .expect("decr alpha #hot1"); + AlphaReserve::increase_provided(netuid.into(), a1.into()); - let t2 = - <Test as Config>::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) - .expect("decr α #hot2"); - AlphaReserve::increase_provided(netuid.into(), t2); + <Test as Config>::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) + .expect("decr alpha #hot2"); + AlphaReserve::increase_provided(netuid.into(), a2.into()); // Act assert_ok!(Pallet::<Test>::do_dissolve_all_liquidity_providers(netuid)); @@ -2524,7 +2515,7 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() { // --- Mirror extrinsic bookkeeping: withdraw τ & α; bump provided reserves --- let tao_taken = <Test as Config>::BalanceOps::decrease_balance(&cold, tao_needed.into()) .expect("decrease TAO"); - let alpha_taken = <Test as Config>::BalanceOps::decrease_stake( + <Test as Config>::BalanceOps::decrease_stake( &cold, &hot, netuid.into(), @@ -2533,7 +2524,7 @@ fn test_dissolve_v3_green_path_refund_tao_stake_alpha_and_clear_state() { .expect("decrease ALPHA"); TaoReserve::increase_provided(netuid.into(), tao_taken); - AlphaReserve::increase_provided(netuid.into(), alpha_taken); + AlphaReserve::increase_provided(netuid.into(), alpha_needed.into()); // --- Act: dissolve (GREEN PATH: permitted validators exist) --- assert_ok!(Pallet::<Test>::do_dissolve_all_liquidity_providers(netuid)); diff --git a/pallets/transaction-fee/Cargo.toml b/pallets/transaction-fee/Cargo.toml index 2180732677..d5a5c2f418 100644 --- a/pallets/transaction-fee/Cargo.toml +++ b/pallets/transaction-fee/Cargo.toml @@ -25,6 +25,7 @@ pallet-preimage = { workspace = true, default-features = false, optional = true pallet-scheduler = { workspace = true, default-features = false, optional = true } [dev-dependencies] +approx.workspace = true frame-executive.workspace = true pallet-evm-chain-id.workspace = true scale-info.workspace = true diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index c18599e0d5..068867ddd4 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -29,9 +29,9 @@ use subtensor_swap_interface::SwapHandler; // Misc use core::marker::PhantomData; use smallvec::smallvec; +use sp_runtime::traits::SaturatedConversion; use sp_std::vec::Vec; -use substrate_fixed::types::U96F32; -use subtensor_runtime_common::{AuthorshipInfo, NetUid, TaoBalance, Token}; +use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, TaoBalance}; // Tests #[cfg(test)] @@ -67,7 +67,7 @@ pub trait AlphaFeeHandler<T: frame_system::Config> { coldkey: &AccountIdOf<T>, alpha_vec: &[(AccountIdOf<T>, NetUid)], tao_amount: TaoBalance, - ); + ) -> (AlphaBalance, TaoBalance); fn get_all_netuids_for_coldkey_and_hotkey( coldkey: &AccountIdOf<T>, hotkey: &AccountIdOf<T>, @@ -99,12 +99,7 @@ where { fn on_nonzero_unbalanced(imbalance: BalancesImbalanceOf<T>) { if let Some(author) = T::author() { - // Pay block author instead of burning. - // One of these is the right call depending on your exact fungible API: - // let _ = pallet_balances::Pallet::<T>::resolve(&author, imbalance); - // or: let _ = pallet_balances::Pallet::<T>::deposit(&author, imbalance.peek(), Precision::BestEffort); - // - // Prefer "resolve" (moves the actual imbalance) if available: + // Pay block author let _ = <pallet_balances::Pallet<T> as Balanced<_>>::resolve(&author, imbalance); } else { // Fallback: if no author, burn (or just drop). @@ -121,9 +116,9 @@ where T: pallet_subtensor_swap::Config, { /// This function checks if tao_amount fee can be withdraw in Alpha currency - /// by converting Alpha to TAO at the current price and ignoring slippage. + /// by converting Alpha to TAO using the current pool conditions. /// - /// If this function returns true, the transaction will be included in the block + /// If this function returns true, the transaction will be added to the mempool /// and Alpha will be withdraw from the account, no matter whether transaction /// is successful or not. /// @@ -135,64 +130,67 @@ where alpha_vec: &[(AccountIdOf<T>, NetUid)], tao_amount: TaoBalance, ) -> bool { - if alpha_vec.is_empty() { - // Alpha vector is empty, nothing to withdraw + if alpha_vec.len() != 1 { + // Multi-subnet alpha fee deduction is prohibited. return false; } - // Divide tao_amount among all alpha entries - let tao_per_entry = tao_amount - .checked_div(&TaoBalance::from(alpha_vec.len())) - .unwrap_or(TaoBalance::ZERO); - - // The rule here is that we should be able to withdraw at least from one entry. - // This is not ideal because it may not pay all fees, but UX is the priority - // and this approach still provides spam protection. - alpha_vec.iter().any(|(hotkey, netuid)| { - let alpha_balance = U96F32::saturating_from_num( + if let Some((hotkey, netuid)) = alpha_vec.first() { + let alpha_balance = pallet_subtensor::Pallet::<T>::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, *netuid, - ), + ); + let alpha_fee = pallet_subtensor_swap::Pallet::<T>::get_alpha_amount_for_tao( + *netuid, + tao_amount.into(), ); - let alpha_price = pallet_subtensor_swap::Pallet::<T>::current_alpha_price(*netuid); - alpha_price.saturating_mul(alpha_balance) >= u64::from(tao_per_entry) - }) + alpha_balance >= alpha_fee + } else { + false + } } fn withdraw_in_alpha( coldkey: &AccountIdOf<T>, alpha_vec: &[(AccountIdOf<T>, NetUid)], tao_amount: TaoBalance, - ) { - if alpha_vec.is_empty() { - return; + ) -> (AlphaBalance, TaoBalance) { + if alpha_vec.len() != 1 { + return (0.into(), 0.into()); } - let tao_per_entry = tao_amount - .checked_div(&TaoBalance::from(alpha_vec.len())) - .unwrap_or(TaoBalance::ZERO); - - alpha_vec.iter().for_each(|(hotkey, netuid)| { - // Divide tao_amount evenly among all alpha entries - let alpha_balance = U96F32::saturating_from_num( + if let Some((hotkey, netuid)) = alpha_vec.first() { + let alpha_balance = pallet_subtensor::Pallet::<T>::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, *netuid, - ), + ); + let mut alpha_equivalent = pallet_subtensor_swap::Pallet::<T>::get_alpha_amount_for_tao( + *netuid, + tao_amount.into(), ); - let alpha_price = pallet_subtensor_swap::Pallet::<T>::current_alpha_price(*netuid); - let alpha_fee = U96F32::saturating_from_num(tao_per_entry) - .checked_div(alpha_price) - .unwrap_or(alpha_balance) - .min(alpha_balance) - .saturating_to_num::<u64>(); - - pallet_subtensor::Pallet::<T>::decrease_stake_for_hotkey_and_coldkey_on_subnet( + if alpha_equivalent.is_zero() { + alpha_equivalent = alpha_balance; + } + let alpha_fee = alpha_equivalent.min(alpha_balance); + + // Sell alpha_fee and burn received tao (ignore unstake_from_subnet return). + let swap_result = pallet_subtensor::Pallet::<T>::unstake_from_subnet( hotkey, coldkey, *netuid, - alpha_fee.into(), + alpha_fee, + 0.into(), + true, ); - }); + + if let Ok(tao_amount) = swap_result { + (alpha_fee, tao_amount) + } else { + (0.into(), 0.into()) + } + } else { + (0.into(), 0.into()) + } } fn get_all_netuids_for_coldkey_and_hotkey( @@ -211,11 +209,13 @@ where } } -/// Enum that describes either a withdrawn amount of transaction fee in TAO or the -/// fact that fee was charged in Alpha (without an amount because it is not needed) +/// Enum that describes either a withdrawn amount of transaction fee in TAO or +/// the exact charged Alpha amount. pub enum WithdrawnFee<T: frame_system::Config, F: Balanced<AccountIdOf<T>>> { + // Contains withdrawn TAO amount Tao(Credit<AccountIdOf<T>, F>), - Alpha, + // Contains withdrawn Alpha amount and resulting swapped TAO + Alpha((AlphaBalance, TaoBalance)), } /// Custom OnChargeTransaction implementation based on standard FungibleAdapter from transaction_payment @@ -231,7 +231,7 @@ impl<F, OU> SubtensorTxFeeHandler<F, OU> { /// distributed evenly between subnets in case of multiple subnets. pub fn fees_in_alpha<T>(who: &AccountIdOf<T>, call: &CallOf<T>) -> Vec<(AccountIdOf<T>, NetUid)> where - T: frame_system::Config + pallet_subtensor::Config, + T: frame_system::Config + pallet_subtensor::Config + AuthorshipInfo<AccountIdOf<T>>, CallOf<T>: IsSubType<pallet_subtensor::Call<T>>, OU: AlphaFeeHandler<T>, { @@ -303,18 +303,18 @@ impl<F, OU> SubtensorTxFeeHandler<F, OU> { impl<T, F, OU> OnChargeTransaction<T> for SubtensorTxFeeHandler<F, OU> where - T: PTPConfig + pallet_subtensor::Config, + T: PTPConfig + pallet_subtensor::Config + AuthorshipInfo<AccountIdOf<T>>, CallOf<T>: IsSubType<pallet_subtensor::Call<T>>, F: Balanced<T::AccountId>, OU: OnUnbalanced<Credit<T::AccountId, F>> + AlphaFeeHandler<T>, - <F as Inspect<AccountIdOf<T>>>::Balance: Into<TaoBalance>, + <F as Inspect<AccountIdOf<T>>>::Balance: Into<TaoBalance> + From<TaoBalance>, { type LiquidityInfo = Option<WithdrawnFee<T, F>>; type Balance = <F as Inspect<<T as frame_system::Config>::AccountId>>::Balance; fn withdraw_fee( who: &AccountIdOf<T>, - _call: &CallOf<T>, + call: &CallOf<T>, _dispatch_info: &DispatchInfoOf<CallOf<T>>, fee: Self::Balance, _tip: Self::Balance, @@ -333,12 +333,13 @@ where ) { Ok(imbalance) => Ok(Some(WithdrawnFee::Tao(imbalance))), Err(_) => { - // let alpha_vec = Self::fees_in_alpha::<T>(who, call); - // if !alpha_vec.is_empty() { - // let fee_u64: u64 = fee.into(); - // OU::withdraw_in_alpha(who, &alpha_vec, fee_u64); - // return Ok(Some(WithdrawnFee::Alpha)); - // } + let alpha_vec = Self::fees_in_alpha::<T>(who, call); + if !alpha_vec.is_empty() { + let fee_u64: u64 = fee.saturated_into::<u64>(); + let (alpha_fee, tao_amount) = + OU::withdraw_in_alpha(who, &alpha_vec, fee_u64.into()); + return Ok(Some(WithdrawnFee::Alpha((alpha_fee, tao_amount)))); + } Err(InvalidTransaction::Payment.into()) } } @@ -346,7 +347,7 @@ where fn can_withdraw_fee( who: &AccountIdOf<T>, - _call: &CallOf<T>, + call: &CallOf<T>, _dispatch_info: &DispatchInfoOf<CallOf<T>>, fee: Self::Balance, _tip: Self::Balance, @@ -359,14 +360,14 @@ where match F::can_withdraw(who, fee) { WithdrawConsequence::Success => Ok(()), _ => { - // // Fallback to fees in Alpha if possible - // let alpha_vec = Self::fees_in_alpha::<T>(who, call); - // if !alpha_vec.is_empty() { - // let fee_u64: u64 = fee.into(); - // if OU::can_withdraw_in_alpha(who, &alpha_vec, fee_u64) { - // return Ok(()); - // } - // } + // Fallback to fees in Alpha if possible + let alpha_vec = Self::fees_in_alpha::<T>(who, call); + if !alpha_vec.is_empty() { + let fee_u64: u64 = fee.saturated_into::<u64>(); + if OU::can_withdraw_in_alpha(who, &alpha_vec, fee_u64.into()) { + return Ok(()); + } + } Err(InvalidTransaction::Payment.into()) } } @@ -404,7 +405,21 @@ where let (tip, fee) = adjusted_paid.split(tip); OU::on_unbalanceds(Some(fee).into_iter().chain(Some(tip))); } - WithdrawnFee::Alpha => { + WithdrawnFee::Alpha((alpha_fee, tao_amount)) => { + if let Some(author) = T::author() { + // Pay block author + let _ = F::deposit(&author, tao_amount.into(), Precision::BestEffort) + .unwrap_or_else(|_| Debt::<T::AccountId, F>::zero()); + } else { + // Fallback: no author => do nothing + } + frame_system::Pallet::<T>::deposit_event( + pallet_subtensor::Event::<T>::TransactionFeePaidWithAlpha { + who: who.clone(), + alpha_fee, + tao_amount, + }, + ); // Subtensor does not refund Alpha fees, charges are final } } diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index f0ad323168..1b4eac0706 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -413,8 +413,8 @@ impl pallet_subtensor_swap::Config for Test { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = pallet_subtensor::TaoCurrencyReserve<Self>; - type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve<Self>; + type TaoReserve = pallet_subtensor::TaoBalanceReserve<Self>; + type AlphaReserve = pallet_subtensor::AlphaBalanceReserve<Self>; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index 5dd353dcde..c7c39eb030 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -1,8 +1,9 @@ -#![allow(clippy::indexing_slicing, clippy::unwrap_used)] -use crate::TransactionSource; -use frame_support::assert_ok; +#![allow(clippy::expect_used, clippy::indexing_slicing, clippy::unwrap_used)] +use crate::{AlphaFeeHandler, SubtensorTxFeeHandler, TransactionFeeHandler, TransactionSource}; +use approx::assert_abs_diff_eq; use frame_support::dispatch::GetDispatchInfo; use frame_support::pallet_prelude::Zero; +use frame_support::{assert_err, assert_ok}; use pallet_subtensor_swap::AlphaSqrtPrice; use sp_runtime::{ traits::{DispatchTransaction, TransactionExtension, TxBaseImplication}, @@ -71,12 +72,99 @@ fn test_remove_stake_fees_tao() { // Remove stake extrinsic should pay fees in TAO because ck has sufficient TAO balance assert!(actual_tao_fee > 0.into()); assert_eq!(actual_alpha_fee, AlphaBalance::from(0)); + + let events = System::events(); + assert!(events.iter().any(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::TransactionPayment( + pallet_transaction_payment::Event::TransactionFeePaid { .. } + ) + ) + })); + assert!(!events.iter().any(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::SubtensorModule(SubtensorEvent::TransactionFeePaidWithAlpha { .. }) + ) + })); + }); +} + +// cargo test --package subtensor-transaction-fee --lib -- tests::test_rejects_multi_subnet_alpha_fee_deduction --exact --show-output +#[test] +fn test_rejects_multi_subnet_alpha_fee_deduction() { + new_test_ext().execute_with(|| { + let sn = setup_subnets(2, 1); + let stake_amount = TAO; + setup_stake( + sn.subnets[0].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + setup_stake( + sn.subnets[1].netuid, + &sn.coldkey, + &sn.hotkeys[0], + stake_amount, + ); + + let alpha_before_0 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let alpha_before_1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[1].netuid, + ); + + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::unstake_all { + hotkey: sn.hotkeys[0], + }); + let alpha_vec = + SubtensorTxFeeHandler::<Balances, TransactionFeeHandler<Test>>::fees_in_alpha::<Test>( + &sn.coldkey, + &call, + ); + assert_eq!(alpha_vec.len(), 2); + + assert!( + !<TransactionFeeHandler<Test> as AlphaFeeHandler<Test>>::can_withdraw_in_alpha( + &sn.coldkey, + &alpha_vec, + 1.into(), + ) + ); + assert_eq!( + <TransactionFeeHandler<Test> as AlphaFeeHandler<Test>>::withdraw_in_alpha( + &sn.coldkey, + &alpha_vec, + 1.into(), + ), + (0.into(), 0.into()) + ); + + let alpha_after_0 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[0].netuid, + ); + let alpha_after_1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &sn.hotkeys[0], + &sn.coldkey, + sn.subnets[1].netuid, + ); + + assert_eq!(alpha_before_0, alpha_after_0); + assert_eq!(alpha_before_1, alpha_after_1); }); } // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -90,7 +178,7 @@ fn test_remove_stake_fees_alpha() { ); // Simulate stake removal to get how much TAO should we get for unstaked Alpha - let (expected_unstaked_tao, _swap_fee) = + let (expected_unstaked_tao, swap_fee) = mock::swap_alpha_to_tao(sn.subnets[0].netuid, unstake_amount); // Forse-set signer balance to ED @@ -100,6 +188,10 @@ fn test_remove_stake_fees_alpha() { current_balance - ExistentialDeposit::get(), ); + // Get the block builder balance + let block_builder = U256::from(MOCK_BLOCK_BUILDER); + let block_builder_balance_before = Balances::free_balance(block_builder); + // Remove stake let balance_before = Balances::free_balance(sn.coldkey); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -113,6 +205,8 @@ fn test_remove_stake_fees_alpha() { amount_unstaked: unstake_amount, }); + System::reset_events(); + // Dispatch the extrinsic with ChargeTransactionPayment extension let info = call.get_dispatch_info(); let ext = pallet_transaction_payment::ChargeTransactionPayment::<Test>::from(0.into()); @@ -136,8 +230,51 @@ fn test_remove_stake_fees_alpha() { let actual_alpha_fee = alpha_before - alpha_after - unstake_amount; // Remove stake extrinsic should pay fees in Alpha - assert_eq!(actual_tao_fee, 0.into()); + assert_abs_diff_eq!(actual_tao_fee, 0.into(), epsilon = 10.into()); assert!(actual_alpha_fee > 0.into()); + + // Assert that swapped TAO from alpha fee goes to block author + let block_builder_fee_portion = 1.; + let expected_block_builder_swap_reward = swap_fee as f64 * block_builder_fee_portion; + let expected_tx_fee = 14000.; // Use very low value (0.000014) for less test flakiness, value before we 10x tx fees + let block_builder_balance_after = Balances::free_balance(block_builder); + let actual_block_builder_reward = + block_builder_balance_after - block_builder_balance_before; + assert!( + u64::from(actual_block_builder_reward) as f64 + >= expected_block_builder_swap_reward + expected_tx_fee + ); + + let events = System::events(); + let alpha_event = events + .iter() + .position(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::SubtensorModule(SubtensorEvent::TransactionFeePaidWithAlpha { + who, + alpha_fee, + tao_amount: _, + }) if who == &sn.coldkey && *alpha_fee == actual_alpha_fee + ) + }) + .expect("expected TransactionFeePaidWithAlpha event"); + let tao_event = events + .iter() + .position(|event_record| { + matches!( + &event_record.event, + RuntimeEvent::TransactionPayment( + pallet_transaction_payment::Event::TransactionFeePaid { who, .. } + ) if who == &sn.coldkey + ) + }) + .expect("expected TransactionFeePaid event"); + + assert!( + alpha_event < tao_event, + "expected TransactionFeePaidWithAlpha before TransactionFeePaid" + ); }); } @@ -146,7 +283,6 @@ fn test_remove_stake_fees_alpha() { // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_root --exact --show-output #[test] -#[ignore] fn test_remove_stake_root() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -205,7 +341,6 @@ fn test_remove_stake_root() { // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_completely_root --exact --show-output #[test] -#[ignore] fn test_remove_stake_completely_root() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -257,7 +392,6 @@ fn test_remove_stake_completely_root() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_completely_fees_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_completely_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -388,7 +522,6 @@ fn test_remove_stake_not_enough_balance_for_fees() { // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_edge_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_edge_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -523,11 +656,11 @@ fn test_remove_stake_failing_transaction_tao_fees() { }); } -// Validation passes, but transaction fails => Alpha fees are paid +// Validation passes, but transaction fails (artificially disable subtoken) => +// Alpha fees are still paid // // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_failing_transaction_alpha_fees --exact --show-output #[test] -#[ignore] fn test_remove_stake_failing_transaction_alpha_fees() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -540,8 +673,11 @@ fn test_remove_stake_failing_transaction_alpha_fees() { stake_amount, ); - // Make unstaking fail by reducing liquidity to critical - SubnetTAO::<Test>::insert(sn.subnets[0].netuid, TaoBalance::from(1)); + // Provide adequate TAO reserve so that sim swap works ok in validation + SubnetTAO::<Test>::insert(sn.subnets[0].netuid, TaoBalance::from(1_000_000_000_u64)); + + // Provide Alpha reserve so that price is about 1.0 + SubnetAlphaIn::<Test>::insert(sn.subnets[0].netuid, AlphaBalance::from(1_000_000_000_u64)); // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); @@ -550,6 +686,9 @@ fn test_remove_stake_failing_transaction_alpha_fees() { current_balance - ExistentialDeposit::get(), ); + // Disable subtoken so that removing stake tx fails (still allows the validation to pass) + pallet_subtensor::SubtokenEnabled::<Test>::insert(sn.subnets[0].netuid, false); + // Remove stake let balance_before = Balances::free_balance(sn.coldkey); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -560,7 +699,7 @@ fn test_remove_stake_failing_transaction_alpha_fees() { let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { hotkey: sn.hotkeys[0], netuid: sn.subnets[0].netuid, - amount_unstaked: unstake_amount, + amount_unstaked: alpha_before, }); // Dispatch the extrinsic with ChargeTransactionPayment extension @@ -593,7 +732,6 @@ fn test_remove_stake_failing_transaction_alpha_fees() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_remove_stake_limit_fees_alpha --exact --show-output #[test] -#[ignore] fn test_remove_stake_limit_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -607,8 +745,12 @@ fn test_remove_stake_limit_fees_alpha() { ); // Simulate stake removal to get how much TAO should we get for unstaked Alpha - let (expected_unstaked_tao, _swap_fee) = - mock::swap_alpha_to_tao(sn.subnets[0].netuid, unstake_amount); + let alpha_fee = AlphaBalance::from(24229); // This is measured alpha fee that matches the withdrawn tx fee + let (expected_burned_tao_fees, _swap_fee) = + mock::swap_alpha_to_tao(sn.subnets[0].netuid, alpha_fee); + let (expected_unstaked_tao_plus_fees, _swap_fee) = + mock::swap_alpha_to_tao(sn.subnets[0].netuid, unstake_amount + alpha_fee); + let expected_unstaked_tao = expected_unstaked_tao_plus_fees - expected_burned_tao_fees; // Forse-set signer balance to ED let current_balance = Balances::free_balance(sn.coldkey); @@ -654,14 +796,13 @@ fn test_remove_stake_limit_fees_alpha() { let actual_alpha_fee = alpha_before - alpha_after - unstake_amount; // Remove stake extrinsic should pay fees in Alpha - assert_eq!(actual_tao_fee, 0.into()); + assert_abs_diff_eq!(actual_tao_fee, 0.into(), epsilon = 100.into()); assert!(actual_alpha_fee > 0.into()); }); } // cargo test --package subtensor-transaction-fee --lib -- tests::test_unstake_all_fees_alpha --exact --show-output #[test] -#[ignore] fn test_unstake_all_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -703,8 +844,22 @@ fn test_unstake_all_fees_alpha() { }); // Dispatch the extrinsic with ChargeTransactionPayment extension + // Get invalid payment because we cannot pay fees in multiple alphas let info = call.get_dispatch_info(); let ext = pallet_transaction_payment::ChargeTransactionPayment::<Test>::from(0.into()); + assert_err!( + ext.clone().dispatch_transaction( + RuntimeOrigin::signed(coldkey).into(), + call.clone(), + &info, + 0, + 0, + ), + TransactionValidityError::Invalid(InvalidTransaction::Payment), + ); + + // Give the coldkey TAO balance - now should unstake ok + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); assert_ok!(ext.dispatch_transaction( RuntimeOrigin::signed(coldkey).into(), call, @@ -734,7 +889,6 @@ fn test_unstake_all_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_unstake_all_alpha_fees_alpha --exact --show-output #[test] -#[ignore] fn test_unstake_all_alpha_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -771,8 +925,22 @@ fn test_unstake_all_alpha_fees_alpha() { }); // Dispatch the extrinsic with ChargeTransactionPayment extension + // Get invalid payment because we cannot pay fees in multiple alphas let info = call.get_dispatch_info(); let ext = pallet_transaction_payment::ChargeTransactionPayment::<Test>::from(0.into()); + assert_err!( + ext.clone().dispatch_transaction( + RuntimeOrigin::signed(coldkey).into(), + call.clone(), + &info, + 0, + 0, + ), + TransactionValidityError::Invalid(InvalidTransaction::Payment), + ); + + // Give the coldkey TAO balance - now should unstake ok + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000_000_u64.into()); assert_ok!(ext.dispatch_transaction( RuntimeOrigin::signed(coldkey).into(), call, @@ -802,7 +970,6 @@ fn test_unstake_all_alpha_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_move_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_move_stake_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -874,7 +1041,6 @@ fn test_move_stake_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_transfer_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_transfer_stake_fees_alpha() { new_test_ext().execute_with(|| { let destination_coldkey = U256::from(100000); @@ -947,7 +1113,6 @@ fn test_transfer_stake_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_swap_stake_fees_alpha --exact --show-output #[test] -#[ignore] fn test_swap_stake_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1018,7 +1183,6 @@ fn test_swap_stake_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_swap_stake_limit_fees_alpha --exact --show-output #[test] -#[ignore] fn test_swap_stake_limit_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1091,7 +1255,6 @@ fn test_swap_stake_limit_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_burn_alpha_fees_alpha --exact --show-output #[test] -#[ignore] fn test_burn_alpha_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1153,7 +1316,6 @@ fn test_burn_alpha_fees_alpha() { // cargo test --package subtensor-transaction-fee --lib -- tests::test_recycle_alpha_fees_alpha --exact --show-output #[test] -#[ignore] fn test_recycle_alpha_fees_alpha() { new_test_ext().execute_with(|| { let stake_amount = TAO; @@ -1258,7 +1420,7 @@ fn test_add_stake_fees_go_to_block_builder() { // Expect that block builder balance has increased by both the swap fee and the transaction fee let expected_block_builder_swap_reward = swap_fee as f64 * block_builder_fee_portion; - let expected_tx_fee = 0.000136; // Use very low value for less test flakiness + let expected_tx_fee = 14000.; // Use very low value (0.000014) for less test flakiness, value before we 10x tx fees let block_builder_balance_after = Balances::free_balance(block_builder); let actual_reward = block_builder_balance_after - block_builder_balance_before; assert!( diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 93bc9be3a7..8ec79b4092 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -315,7 +315,8 @@ where let hotkey = R::AccountId::from(hotkey.0); let mut coldkeys: Vec<H256> = vec![]; let netuid = NetUid::from(try_u16_from_u256(netuid)?); - for ((coldkey, netuid_in_alpha), _) in pallet_subtensor::Alpha::<R>::iter_prefix((hotkey,)) + for (coldkey, netuid_in_alpha, _) in + pallet_subtensor::Pallet::<R>::alpha_iter_single_prefix(&hotkey) { if netuid == netuid_in_alpha { let key: [u8; 32] = coldkey.into(); diff --git a/primitives/share-pool/Cargo.toml b/primitives/share-pool/Cargo.toml index ba42b0d77d..9282e3cfa6 100644 --- a/primitives/share-pool/Cargo.toml +++ b/primitives/share-pool/Cargo.toml @@ -4,13 +4,34 @@ version = "0.1.0" edition.workspace = true [dependencies] +approx.workspace = true +codec.workspace = true +log.workspace = true +scale-info.workspace = true substrate-fixed.workspace = true +subtensor-macros.workspace = true +sp-core.workspace = true sp-std.workspace = true +num-traits.workspace = true safe-math.workspace = true +[dev-dependencies] +rand = { version = "0.8", features = ["std", "std_rng"] } +rayon = "1.10" + [lints] workspace = true [features] default = ["std"] -std = ["substrate-fixed/std", "sp-std/std", "safe-math/std"] +std = [ + "codec/std", + "log/std", + "rand/std", + "scale-info/std", + "substrate-fixed/std", + "sp-core/std", + "sp-std/std", + "num-traits/std", + "safe-math/std", +] diff --git a/primitives/share-pool/src/lib.rs b/primitives/share-pool/src/lib.rs index d43f36259c..848ee64448 100644 --- a/primitives/share-pool/src/lib.rs +++ b/primitives/share-pool/src/lib.rs @@ -1,26 +1,397 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::result_unit_err)] +#![allow(clippy::result_unit_err, clippy::indexing_slicing)] -use safe_math::*; +use codec::{Decode, Encode}; +#[cfg(not(feature = "std"))] +use num_traits::float::FloatCore as _; +use scale_info::TypeInfo; +use sp_core::U256; use sp_std::marker; use sp_std::ops::Neg; -use substrate_fixed::types::{I64F64, U64F64}; +use substrate_fixed::types::U64F64; +use subtensor_macros::freeze_struct; + +// Maximum mantissa that can be used with SafeFloat +pub const SAFE_FLOAT_MAX: u128 = 1_000_000_000_000_000_000_000_u128; +pub const SAFE_FLOAT_MAX_EXP: i64 = 21_i64; + +/// Controlled precision floating point number with efficient storage +/// +/// Precision is controlled in a way that keeps enough mantissa digits so +/// that updating hotkey stake by 1 rao makes difference in the resulting shared +/// pool variables (both coldkey share and share pool denominator), but also +/// precision should be limited so that updating by 0.1 rao does not make the +/// difference (because there's no such thing as 0.1 rao, rao is integer). +#[freeze_struct("9a55fbe2d60efb41")] +#[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] +pub struct SafeFloat { + mantissa: u128, + exponent: i64, +} + +/// Capped power of 10 in U256 +/// Cap at 10^SAFE_FLOAT_MAX_EXP+1, we don't need greater powers here +fn cappow10(e: u64) -> U256 { + if e > (SAFE_FLOAT_MAX_EXP as u64).saturating_add(1) { + return U256::from(SAFE_FLOAT_MAX.saturating_mul(10)); + } + if e == 0 { + return U256::from(1); + } + U256::from(10) + .checked_pow(U256::from(e)) + .unwrap_or_default() +} + +impl SafeFloat { + pub fn zero() -> Self { + SafeFloat { + mantissa: 0_u128, + exponent: 0_i64, + } + } + + pub fn new(mantissa: u128, exponent: i64) -> Option<Self> { + // Cap mantissa at SAFE_FLOAT_MAX + if mantissa > SAFE_FLOAT_MAX { + return None; + } + + let mut safe_float = SafeFloat::zero(); + + if safe_float.normalize(&U256::from(mantissa), exponent) { + Some(safe_float) + } else { + None + } + } + + /// Sets the new mantissa and exponent adjustsing mantissa and exponent so that + /// SAFE_FLOAT_MAX / 10 < mantissa <= SAFE_FLOAT_MAX + /// + /// Returns true in case of success or false if exponent over- or underflows + pub(crate) fn normalize(&mut self, new_mantissa: &U256, new_exponent: i64) -> bool { + if new_mantissa.is_zero() { + self.mantissa = 0; + self.exponent = 0; + return true; + } + + let ten = U256::from(10); + let max_mantissa = U256::from(SAFE_FLOAT_MAX); + let min_mantissa = U256::from(SAFE_FLOAT_MAX) + .checked_div(ten) + .unwrap_or_default(); + + // Loops are safe because they are bounded by U256 size and result + // in no more than 78 iterations together + let mut normalized_mantissa = *new_mantissa; + let mut normalized_exponent = new_exponent; + + while normalized_mantissa > max_mantissa { + let Some(next_mantissa) = normalized_mantissa.checked_div(ten) else { + return false; + }; + let Some(next_exponent) = normalized_exponent.checked_add(1) else { + return false; + }; + + normalized_mantissa = next_mantissa; + normalized_exponent = next_exponent; + } + + while normalized_mantissa <= min_mantissa { + let Some(next_mantissa) = normalized_mantissa.checked_mul(ten) else { + return false; + }; + let Some(next_exponent) = normalized_exponent.checked_sub(1) else { + return false; + }; + + normalized_mantissa = next_mantissa; + normalized_exponent = next_exponent; + } + + self.mantissa = normalized_mantissa.low_u128(); + self.exponent = normalized_exponent; + + true + } + + /// Divide current value by a preserving precision (SAFE_FLOAT_MAX digits in mantissa) + /// result = m1 * 10^e1 / m2 * 10^e2 + pub fn div(&self, a: &SafeFloat) -> Option<Self> { + // - In m1 / m2 division we need enough digits for a u128. + // This can be calculated in a lossless way in U256 as m1 * MAX_MANTISSA / m2 + // - The new exponent is e1 - e2 - SAFE_FLOAT_MAX_EXP + let maybe_m1_scaled_u256 = + U256::from(self.mantissa).checked_mul(U256::from(SAFE_FLOAT_MAX)); + let m2_u256 = U256::from(a.mantissa); + + // Calculate new exponent + let new_exponent_i128 = (self.exponent as i128) + .saturating_sub(a.exponent as i128) + .saturating_sub(SAFE_FLOAT_MAX_EXP as i128); + if (new_exponent_i128 > i64::MAX as i128) || (new_exponent_i128 < i64::MIN as i128) { + return None; + } + let new_exponent = new_exponent_i128 as i64; + + // Calcuate new mantissa, normalize, and return result + if let Some(m1_scaled_u256) = maybe_m1_scaled_u256 { + let maybe_new_mantissa_u256 = m1_scaled_u256.checked_div(m2_u256); + if let Some(new_mantissa_u256) = maybe_new_mantissa_u256 { + let mut safe_float = SafeFloat::zero(); + if safe_float.normalize(&new_mantissa_u256, new_exponent) { + Some(safe_float) + } else { + None + } + } else { + None + } + } else { + None + } + } + + pub fn add(&self, a: &SafeFloat) -> Option<Self> { + if self.is_zero() { + return Some(a.clone()); + } + if a.is_zero() { + return Some(self.clone()); + } + + let (new_mantissa, new_exponent) = if self.exponent >= a.exponent { + let exp_diff = self.exponent.saturating_sub(a.exponent); + let m1 = U256::from(self.mantissa); + let m2 = U256::from(a.mantissa) + .checked_div(cappow10(exp_diff as u64)) + .unwrap_or_default(); + (m1.saturating_add(m2), self.exponent) + } else { + let exp_diff = a.exponent.saturating_sub(self.exponent); + let m1 = U256::from(self.mantissa) + .checked_div(cappow10(exp_diff as u64)) + .unwrap_or_default(); + let m2 = U256::from(a.mantissa); + (m1.saturating_add(m2), a.exponent) + }; + + let mut safe_float = SafeFloat::zero(); + if safe_float.normalize(&new_mantissa, new_exponent) { + Some(safe_float) + } else { + None + } + } + + pub fn sub(&self, a: &SafeFloat) -> Option<Self> { + if self.is_zero() && a.is_zero() { + return Some(Self::zero()); + } else if self.is_zero() { + return None; + } + if a.is_zero() { + return Some(self.clone()); + } + + let (new_mantissa, new_exponent) = if self.exponent >= a.exponent { + let exp_diff = self.exponent.saturating_sub(a.exponent); + let m1 = U256::from(self.mantissa); + let m2 = U256::from(a.mantissa) + .checked_div(cappow10(exp_diff as u64)) + .unwrap_or_default(); + (m1.saturating_sub(m2), self.exponent) + } else { + let exp_diff = a.exponent.saturating_sub(self.exponent); + let m1 = U256::from(self.mantissa) + .checked_div(cappow10(exp_diff as u64)) + .unwrap_or_default(); + let m2 = U256::from(a.mantissa); + (m1.saturating_sub(m2), a.exponent) + }; + + let mut safe_float = SafeFloat::zero(); + if safe_float.normalize(&new_mantissa, new_exponent) { + Some(safe_float) + } else { + None + } + } + + /// Calculate self * a / b without loss of precision + pub fn mul_div(&self, a: &SafeFloat, b: &SafeFloat) -> Option<Self> { + if b.mantissa == 0_u128 { + return None; + } + + // No overflows here, just unwrap or default + let self_a_mantissa_u256 = U256::from(self.mantissa) + .checked_mul(U256::from(a.mantissa)) + .unwrap_or_default(); + let maybe_self_a_exponent = self.exponent.checked_add(a.exponent); + + if let Some(self_a_exponent) = maybe_self_a_exponent { + // Divide by b in U256 + let maybe_new_exponent = self_a_exponent.checked_sub(b.exponent); + if let Some(new_exponent) = maybe_new_exponent { + let new_mantissa = self_a_mantissa_u256 + .checked_div(U256::from(b.mantissa)) + .unwrap_or_default(); + let mut result = SafeFloat::zero(); + if result.normalize(&new_mantissa, new_exponent) { + Some(result) + } else { + None + } + } else { + None + } + } else { + None + } + } + + pub fn is_zero(&self) -> bool { + self.mantissa == 0u128 + } + + /// Returns true if self > a + /// Both values should be normalized + pub fn gt(&self, a: &SafeFloat) -> bool { + let ten = U256::from(10); + + if self.exponent == a.exponent { + self.mantissa > a.mantissa + } else if self.exponent > a.exponent { + let exp_diff = self.exponent.saturating_sub(a.exponent); + if exp_diff > 1_i64 { + true + } else { + ten.saturating_mul(U256::from(self.mantissa)) > U256::from(a.mantissa) + } + } else { + let exp_diff = a.exponent.saturating_sub(self.exponent); + if exp_diff > 1_i64 { + false + } else { + U256::from(self.mantissa) > ten.saturating_mul(U256::from(a.mantissa)) + } + } + } +} + +// Saturating conversion: negatives -> 0, overflow -> u64::MAX +impl From<&SafeFloat> for u64 { + fn from(value: &SafeFloat) -> Self { + // If exponent is zero, it's just an integer mantissa + if value.exponent == 0 { + return u64::try_from(value.mantissa).unwrap_or(u64::MAX); + } + + // scale = 10^exponent + let scale = cappow10(value.exponent.unsigned_abs()); + + // mantissa * 10^exponent + let q: U256 = if value.exponent > 0 { + U256::from(value.mantissa).saturating_mul(scale) + } else { + U256::from(value.mantissa) + .checked_div(scale) + .unwrap_or_default() + }; + + // Convert quotient to u64, saturating on overflow + if q.is_zero() { + 0 + } else { + q.try_into().unwrap_or(u64::MAX) + } + } +} + +// Convenience impl for owning values +impl From<SafeFloat> for u64 { + fn from(value: SafeFloat) -> Self { + u64::from(&value) + } +} + +impl From<u64> for SafeFloat { + fn from(value: u64) -> Self { + SafeFloat::new(value as u128, 0).unwrap_or_default() + } +} + +impl From<U64F64> for SafeFloat { + fn from(value: U64F64) -> Self { + let bits = value.to_bits(); + // High 64 bits = integer part + let int = (bits >> 64) as u64; + // Low 64 bits = fractional part + let frac = (bits & 0xFFFF_FFFF_FFFF_FFFF) as u64; + + // If strictly zero, shortcut + if bits == 0 { + return SafeFloat::zero(); + } + + // SafeFloat for integer part: int * 10^0 + let safe_int = SafeFloat::new(int as u128, 0).unwrap_or_default(); + + // Numerator of fractional part: frac * 10^0 + let safe_frac_num = SafeFloat::new(frac as u128, 0).unwrap_or_default(); + + // Denominator = 2^64 as an integer SafeFloat: (2^64) * 10^0 + let two64: u128 = 1u128 << 64; + let safe_two64 = SafeFloat::new(two64, 0).unwrap_or_default(); + + // frac_part = frac / 2^64 + let safe_frac = safe_frac_num.div(&safe_two64).unwrap_or_default(); + + // int + frac/2^64, with all mantissa/exponent normalization + safe_int.add(&safe_frac).unwrap_or_default() + } +} + +impl From<&SafeFloat> for f64 { + #[allow( + clippy::arithmetic_side_effects, + reason = "This code is only used in tests" + )] + fn from(value: &SafeFloat) -> Self { + let mant = value.mantissa as f64; + + // powi takes i32, so clamp i64 exponent into i32 range (test-only). + let e = value.exponent.clamp(i32::MIN as i64, i32::MAX as i64) as i32; + + mant * 10_f64.powi(e) + } +} + +impl From<SafeFloat> for f64 { + fn from(value: SafeFloat) -> Self { + f64::from(&value) + } +} pub trait SharePoolDataOperations<Key> { - /// Gets shared value - fn get_shared_value(&self) -> U64F64; + /// Gets shared value (always "the real thing" measured in rao, not fractional) + fn get_shared_value(&self) -> u64; /// Gets single share for a given key - fn get_share(&self, key: &Key) -> U64F64; + fn get_share(&self, key: &Key) -> SafeFloat; // Tries to get a single share for a given key, as a result. - fn try_get_share(&self, key: &Key) -> Result<U64F64, ()>; + fn try_get_share(&self, key: &Key) -> Result<SafeFloat, ()>; /// Gets share pool denominator - fn get_denominator(&self) -> U64F64; + fn get_denominator(&self) -> SafeFloat; /// Updates shared value by provided signed value - fn set_shared_value(&mut self, value: U64F64); + fn set_shared_value(&mut self, value: u64); /// Update single share for a given key by provided signed value - fn set_share(&mut self, key: &Key, share: U64F64); + fn set_share(&mut self, key: &Key, share: SafeFloat); /// Update share pool denominator by provided signed value - fn set_denominator(&mut self, update: U64F64); + fn set_denominator(&mut self, update: SafeFloat); } /// SharePool struct that depends on the Key type and uses the SharePoolDataOperations @@ -47,36 +418,24 @@ where } pub fn get_value(&self, key: &K) -> u64 { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let current_share: U64F64 = self.state_ops.get_share(key); - let denominator: U64F64 = self.state_ops.get_denominator(); - - let maybe_value_per_share = shared_value.checked_div(denominator); - (if let Some(value_per_share) = maybe_value_per_share { - value_per_share.saturating_mul(current_share) - } else { - shared_value - .saturating_mul(current_share) - .checked_div(denominator) - .unwrap_or(U64F64::saturating_from_num(0)) - }) - .saturating_to_num::<u64>() + let shared_value: SafeFloat = + SafeFloat::new(self.state_ops.get_shared_value() as u128, 0).unwrap_or_default(); + let current_share: SafeFloat = self.state_ops.get_share(key); + let denominator: SafeFloat = self.state_ops.get_denominator(); + shared_value + .mul_div(¤t_share, &denominator) + .unwrap_or_default() + .into() } - pub fn get_value_from_shares(&self, current_share: U64F64) -> u64 { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let denominator: U64F64 = self.state_ops.get_denominator(); - - let maybe_value_per_share = shared_value.checked_div(denominator); - (if let Some(value_per_share) = maybe_value_per_share { - value_per_share.saturating_mul(current_share) - } else { - shared_value - .saturating_mul(current_share) - .checked_div(denominator) - .unwrap_or(U64F64::saturating_from_num(0)) - }) - .saturating_to_num::<u64>() + pub fn get_value_from_shares(&self, current_share: SafeFloat) -> u64 { + let shared_value: SafeFloat = + SafeFloat::new(self.state_ops.get_shared_value() as u128, 0).unwrap_or_default(); + let denominator: SafeFloat = self.state_ops.get_denominator(); + shared_value + .mul_div(¤t_share, &denominator) + .unwrap_or_default() + .into() } pub fn try_get_value(&self, key: &K) -> Result<u64, ()> { @@ -89,164 +448,184 @@ where /// Update the total shared value. /// Every key's associated value effectively updates with this operation pub fn update_value_for_all(&mut self, update: i64) { - let shared_value: U64F64 = self.state_ops.get_shared_value(); + let shared_value: u64 = self.state_ops.get_shared_value(); self.state_ops.set_shared_value(if update >= 0 { - shared_value.saturating_add(U64F64::saturating_from_num(update)) + shared_value.saturating_add(update as u64) } else { - shared_value.saturating_sub(U64F64::saturating_from_num(update.neg())) + shared_value.saturating_sub(update.neg() as u64) }); } pub fn sim_update_value_for_one(&mut self, update: i64) -> bool { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let denominator: U64F64 = self.state_ops.get_denominator(); + let shared_value: u64 = self.state_ops.get_shared_value(); + let denominator: SafeFloat = self.state_ops.get_denominator(); // Then, update this key's share - if denominator == 0 { + if denominator.mantissa == 0 { true } else { // There are already keys in the pool, set or update this key - let shares_per_update: I64F64 = - self.get_shares_per_update(update, &shared_value, &denominator); + let shares_per_update = self.get_shares_per_update(update, shared_value, &denominator); - shares_per_update != 0 + !shares_per_update.is_zero() } } fn get_shares_per_update( &self, update: i64, - shared_value: &U64F64, - denominator: &U64F64, - ) -> I64F64 { - let maybe_value_per_share = shared_value.checked_div(*denominator); - if let Some(value_per_share) = maybe_value_per_share { - I64F64::saturating_from_num(update) - .checked_div(I64F64::saturating_from_num(value_per_share)) - .unwrap_or(I64F64::saturating_from_num(0)) - } else { - I64F64::saturating_from_num(update) - .checked_div(I64F64::saturating_from_num(*shared_value)) - .unwrap_or(I64F64::saturating_from_num(0)) - .saturating_mul(I64F64::saturating_from_num(*denominator)) - } + shared_value: u64, + denominator: &SafeFloat, + ) -> SafeFloat { + let shared_value: SafeFloat = SafeFloat::new(shared_value as u128, 0).unwrap_or_default(); + let update_sf: SafeFloat = + SafeFloat::new(update.unsigned_abs() as u128, 0).unwrap_or_default(); + update_sf + .mul_div(denominator, &shared_value) + .unwrap_or_default() } /// Update the value associated with an item identified by the Key /// Returns actual update /// - pub fn update_value_for_one(&mut self, key: &K, update: i64) -> i64 { - let shared_value: U64F64 = self.state_ops.get_shared_value(); - let current_share: U64F64 = self.state_ops.get_share(key); - let denominator: U64F64 = self.state_ops.get_denominator(); - let initial_value: i64 = self.get_value(key) as i64; - let mut actual_update: i64 = update; + pub fn update_value_for_one(&mut self, key: &K, update: i64) { + let shared_value: u64 = self.state_ops.get_shared_value(); + let current_share: SafeFloat = self.state_ops.get_share(key); + let denominator: SafeFloat = self.state_ops.get_denominator(); // Then, update this key's share - if denominator == 0 { + if denominator.is_zero() { // Initialize the pool. The first key gets all. - let update_fixed: U64F64 = U64F64::saturating_from_num(update); - self.state_ops.set_denominator(update_fixed); - self.state_ops.set_share(key, update_fixed); + let update_float: SafeFloat = + SafeFloat::new(update.unsigned_abs() as u128, 0).unwrap_or_default(); + self.state_ops.set_denominator(update_float.clone()); + self.state_ops.set_share(key, update_float); } else { - let shares_per_update: I64F64 = - self.get_shares_per_update(update, &shared_value, &denominator); - - if shares_per_update >= 0 { - self.state_ops.set_denominator( - denominator.saturating_add(U64F64::saturating_from_num(shares_per_update)), - ); - self.state_ops.set_share( - key, - current_share.saturating_add(U64F64::saturating_from_num(shares_per_update)), - ); + let new_denominator; + let new_current_share; + + let shares_per_update: SafeFloat = + self.get_shares_per_update(update, shared_value, &denominator); + + // Handle SafeFloat overflows quietly here because this overflow of i64 exponent + // is extremely hypothetical and should never happen in practice. + if update > 0 { + new_denominator = match denominator.add(&shares_per_update) { + Some(new_denominator) => new_denominator, + None => { + log::error!( + "SafeFloat::add overflow when adding {:?} to {:?}; keeping old denominator", + shares_per_update, + denominator, + ); + // Return the value as it was before the failed addition + denominator + } + }; + + new_current_share = match current_share.add(&shares_per_update) { + Some(new_current_share) => new_current_share, + None => { + log::error!( + "SafeFloat::add overflow when adding {:?} to {:?}; keeping old current_share", + shares_per_update, + current_share, + ); + // Return the value as it was before the failed addition + current_share + } + }; } else { - // Check if this entry is about to break precision - let mut new_denominator = denominator - .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())); - let mut new_share = current_share - .saturating_sub(U64F64::saturating_from_num(shares_per_update.neg())); - - // The condition here is either the share remainder is too little OR - // the new_denominator is too low compared to what shared_value + year worth of emissions would be - if (new_share.safe_div(current_share) < U64F64::saturating_from_num(0.00001)) - || shared_value - .saturating_add(U64F64::saturating_from_num(2_628_000_000_000_000_u64)) - .checked_div(new_denominator) - .is_none() - { - // yes, precision is low, just remove all - new_share = U64F64::saturating_from_num(0); - new_denominator = denominator.saturating_sub(current_share); - actual_update = initial_value.neg(); - } - - self.state_ops.set_denominator(new_denominator); - self.state_ops.set_share(key, new_share); + new_denominator = match denominator.sub(&shares_per_update) { + Some(new_denominator) => new_denominator, + None => { + log::error!( + "SafeFloat::add overflow when adding {:?} to {:?}; keeping old denominator", + shares_per_update, + denominator, + ); + // Return the value as it was before the failed addition + denominator + } + }; + + new_current_share = match current_share.sub(&shares_per_update) { + Some(new_current_share) => new_current_share, + None => { + log::error!( + "SafeFloat::add overflow when adding {:?} to {:?}; keeping old current_share", + shares_per_update, + current_share, + ); + // Return the value as it was before the failed addition + current_share + } + }; } + + self.state_ops.set_denominator(new_denominator); + self.state_ops.set_share(key, new_current_share); } // Update shared value - self.update_value_for_all(actual_update); - - // Return actual udate - actual_update + self.update_value_for_all(update); } } +// cargo test --package share-pool --lib -- tests --nocapture #[cfg(test)] +#[allow(clippy::unwrap_used)] mod tests { use super::*; + use approx::assert_abs_diff_eq; use std::collections::BTreeMap; + use substrate_fixed::types::U64F64; struct MockSharePoolDataOperations { - shared_value: U64F64, - share: BTreeMap<u16, U64F64>, - denominator: U64F64, + shared_value: u64, + share: BTreeMap<u16, SafeFloat>, + denominator: SafeFloat, } impl MockSharePoolDataOperations { fn new() -> Self { MockSharePoolDataOperations { - shared_value: U64F64::saturating_from_num(0), + shared_value: 0u64, share: BTreeMap::new(), - denominator: U64F64::saturating_from_num(0), + denominator: SafeFloat::zero(), } } } impl SharePoolDataOperations<u16> for MockSharePoolDataOperations { - fn get_shared_value(&self) -> U64F64 { + fn get_shared_value(&self) -> u64 { self.shared_value } - fn get_share(&self, key: &u16) -> U64F64 { - *self - .share - .get(key) - .unwrap_or(&U64F64::saturating_from_num(0)) + fn get_share(&self, key: &u16) -> SafeFloat { + self.share.get(key).cloned().unwrap_or_else(SafeFloat::zero) } - fn try_get_share(&self, key: &u16) -> Result<U64F64, ()> { - match self.share.get(key) { - Some(&value) => Ok(value), + fn try_get_share(&self, key: &u16) -> Result<SafeFloat, ()> { + match self.share.get(key).cloned() { + Some(value) => Ok(value), None => Err(()), } } - fn get_denominator(&self) -> U64F64 { - self.denominator + fn get_denominator(&self) -> SafeFloat { + self.denominator.clone() } - fn set_shared_value(&mut self, value: U64F64) { + fn set_shared_value(&mut self, value: u64) { self.shared_value = value; } - fn set_share(&mut self, key: &u16, share: U64F64) { + fn set_share(&mut self, key: &u16, share: SafeFloat) { self.share.insert(*key, share); } - fn set_denominator(&mut self, update: U64F64) { + fn set_denominator(&mut self, update: SafeFloat) { self.denominator = update; } } @@ -254,10 +633,10 @@ mod tests { #[test] fn test_get_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::saturating_from_num(10)); - mock_ops.set_share(&1_u16, U64F64::saturating_from_num(3)); - mock_ops.set_share(&2_u16, U64F64::saturating_from_num(7)); - mock_ops.set_shared_value(U64F64::saturating_from_num(100)); + mock_ops.set_denominator(10u64.into()); + mock_ops.set_share(&1_u16, 3u64.into()); + mock_ops.set_share(&2_u16, 7u64.into()); + mock_ops.set_shared_value(100u64.into()); let share_pool = SharePool::new(mock_ops); let result1 = share_pool.get_value(&1); let result2 = share_pool.get_value(&2); @@ -268,7 +647,7 @@ mod tests { #[test] fn test_division_by_zero() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_denominator(U64F64::saturating_from_num(0)); // Zero denominator + mock_ops.set_denominator(SafeFloat::zero()); // Zero denominator let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); let value = pool.get_value(&1); @@ -278,10 +657,10 @@ mod tests { #[test] fn test_max_shared_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::saturating_from_num(u64::MAX)); - mock_ops.set_share(&1, U64F64::saturating_from_num(3)); // Use a neutral value for share - mock_ops.set_share(&2, U64F64::saturating_from_num(7)); // Use a neutral value for share - mock_ops.set_denominator(U64F64::saturating_from_num(10)); // Neutral value to see max effect + mock_ops.set_shared_value(u64::MAX.into()); + mock_ops.set_share(&1, 3u64.into()); // Use a neutral value for share + mock_ops.set_share(&2, 7u64.into()); // Use a neutral value for share + mock_ops.set_denominator(10u64.into()); // Neutral value to see max effect let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); let max_value = pool.get_value(&1) + pool.get_value(&2); @@ -291,16 +670,16 @@ mod tests { #[test] fn test_max_share_value() { let mut mock_ops = MockSharePoolDataOperations::new(); - mock_ops.set_shared_value(U64F64::saturating_from_num(1_000_000_000)); // Use a neutral value for shared value - mock_ops.set_share(&1, U64F64::saturating_from_num(u64::MAX / 2)); - mock_ops.set_share(&2, U64F64::saturating_from_num(u64::MAX / 2)); - mock_ops.set_denominator(U64F64::saturating_from_num(u64::MAX)); + mock_ops.set_shared_value(1_000_000_000u64); // Use a neutral value for shared value + mock_ops.set_share(&1, (u64::MAX / 2).into()); + mock_ops.set_share(&2, (u64::MAX / 2).into()); + mock_ops.set_denominator((u64::MAX).into()); let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); let value1 = pool.get_value(&1) as i128; let value2 = pool.get_value(&2) as i128; - assert!((value1 - 500_000_000).abs() <= 1); + assert_abs_diff_eq!(value1 as f64, 500_000_000_f64, epsilon = 1.); assert!((value2 - 500_000_000).abs() <= 1); } @@ -331,26 +710,30 @@ mod tests { let mock_ops = MockSharePoolDataOperations::new(); let mut pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); + // 50%/50% stakes consisting of 1 rao each pool.update_value_for_one(&1, 1); pool.update_value_for_one(&2, 1); + // Huge emission resulting in 1M Alpha + // Both stakers should have 500k Alpha each pool.update_value_for_all(999_999_999_999_998); + // Everyone unstakes almost everything, leaving 10 rao in the stake pool.update_value_for_one(&1, -499_999_999_999_990); pool.update_value_for_one(&2, -499_999_999_999_990); + // Huge emission resulting in 1M Alpha + // Both stakers should have 500k Alpha each pool.update_value_for_all(999_999_999_999_980); + // Stakers add 1k Alpha each pool.update_value_for_one(&1, 1_000_000_000_000); pool.update_value_for_one(&2, 1_000_000_000_000); - let value1 = pool.get_value(&1) as i128; - let value2 = pool.get_value(&2) as i128; - - // First to stake gets all accumulated emission if there are no other stakers - // (which is artificial situation because there will be no emissions if there is no stake) - assert!((value1 - 1_001_000_000_000_000).abs() < 100); - assert!((value2 - 1_000_000_000_000).abs() < 100); + let value1 = pool.get_value(&1) as f64; + let value2 = pool.get_value(&2) as f64; + assert_abs_diff_eq!(value1, 501_000_000_000_000_f64, epsilon = 1.); + assert_abs_diff_eq!(value2, 501_000_000_000_000_f64, epsilon = 1.); } // cargo test --package share-pool --lib -- tests::test_denom_high_precision_many_small_unstakes --exact --show-output @@ -359,26 +742,37 @@ mod tests { let mock_ops = MockSharePoolDataOperations::new(); let mut pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); + // 50%/50% stakes consisting of 1 rao each pool.update_value_for_one(&1, 1); pool.update_value_for_one(&2, 1); + // Huge emission resulting in 1M Alpha + // Both stakers should have 500k Alpha + 1 rao each pool.update_value_for_all(1_000_000_000_000_000); - for _ in 0..1_000_000 { - pool.update_value_for_one(&1, -500_000_000); - pool.update_value_for_one(&2, -500_000_000); + // Run X number of small unstake transactions + let tx_count = 1000; + let unstake_amount = -500_000_000; + for _ in 0..tx_count { + pool.update_value_for_one(&1, unstake_amount); + pool.update_value_for_one(&2, unstake_amount); } + // Emit 1M - each gets 500k Alpha pool.update_value_for_all(1_000_000_000_000_000); + // Each adds 1k Alpha pool.update_value_for_one(&1, 1_000_000_000_000); pool.update_value_for_one(&2, 1_000_000_000_000); + // Result, each should get + // (500k+1) + tx_count * unstake_amount + 500k + 1k let value1 = pool.get_value(&1) as i128; let value2 = pool.get_value(&2) as i128; + let expected = 1_001_000_000_000_000 + tx_count * unstake_amount; - assert!((value1 - 1_001_000_000_000_000).abs() < 10); - assert!((value2 - 1_000_000_000_000).abs() < 10); + assert_abs_diff_eq!(value1 as f64, expected as f64, epsilon = 1.); + assert_abs_diff_eq!(value2 as f64, expected as f64, epsilon = 1.); } #[test] @@ -407,46 +801,846 @@ mod tests { // cargo test --package share-pool --lib -- tests::test_get_shares_per_update --exact --show-output #[test] fn test_get_shares_per_update() { + // Test case (update, shared_value, denominator_mantissa, denominator_exponent) + [ + (1_i64, 1_u64, 1_u64, 0_i64), + (1, 1_000_000_000_000_000_000, 1, 0), + (1, 21_000_000_000_000_000, 1, 5), + (1, 21_000_000_000_000_000, 1, -1_000_000), + (1, 21_000_000_000_000_000, 1, -1_000_000_000), + (1, 21_000_000_000_000_000, 1, -1_000_000_001), + (1_000, 21_000_000_000_000_000, 1, 5), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, 5), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, -5), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, -100), + (21_000_000_000_000_000, 21_000_000_000_000_000, 1, 100), + (210_000_000_000_000_000, 21_000_000_000_000_000, 1, 5), + (1_000, 1_000, 21_000_000_000_000_000, 0), + (1_000, 1_000, 21_000_000_000_000_000, -1), + ] + .into_iter() + .for_each( + |(update, shared_value, denominator_mantissa, denominator_exponent)| { + let mock_ops = MockSharePoolDataOperations::new(); + let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); + + let denominator_float = + SafeFloat::new(denominator_mantissa as u128, denominator_exponent) + .unwrap_or_default(); + let denominator_f64: f64 = denominator_float.clone().into(); + let spu: f64 = pool + .get_shares_per_update(update, shared_value, &denominator_float) + .into(); + let expected = update as f64 * denominator_f64 / shared_value as f64; + let precision = 1000.; + assert_abs_diff_eq!(expected, spu, epsilon = expected / precision); + }, + ); + } + + #[test] + fn test_safefloat_normalize() { + // Test case: mantissa, exponent, expected mantissa, expected exponent + [ + (1_u128, 0, 1_000_000_000_000_000_000_000_u128, -21_i64), + (0, 0, 0, 0), + (10_u128, 0, 1_000_000_000_000_000_000_000_u128, -20), + (1_000_u128, 0, 1_000_000_000_000_000_000_000_u128, -18), + ( + 100_000_000_000_000_000_000_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + -1, + ), + (SAFE_FLOAT_MAX, 0, SAFE_FLOAT_MAX, 0), + ] + .into_iter() + .for_each(|(m, e, expected_m, expected_e)| { + let a = SafeFloat::new(m, e).unwrap(); + assert_eq!(a.mantissa, expected_m); + assert_eq!(a.exponent, expected_e); + }); + } + + #[test] + fn test_safefloat_add() { + // Test case: man_a, exp_a, man_b, exp_b, expected mantissa of a+b, expected exponent of a+b [ - (1_i64, 1_u64, 1.0, 1.0), + // 1 + 1 = 2 + ( + 1_u128, + 0, + 1_u128, + 0, + 200_000_000_000_000_000_000_u128, + -20_i64, + ), + // 0 + 1 = 1 + (0, 0, 1, 0, 1_000_000_000_000_000_000_000_u128, -21_i64), + // 0 + 0.1 = 0.1 + (0, 0, 1, -1, 1_000_000_000_000_000_000_000_u128, -22_i64), + // 1e-1000 + 0.1 = 0.1 + (1, -1000, 1, -1, 1_000_000_000_000_000_000_000_u128, -22_i64), + // SAFE_FLOAT_MAX + SAFE_FLOAT_MAX + ( + SAFE_FLOAT_MAX, + 0, + SAFE_FLOAT_MAX, + 0, + SAFE_FLOAT_MAX * 2 / 10, + 1_i64, + ), + // Expected loss of precision: tiny + huge + ( + 1_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + 1, + 1_000_000_000_000_000_000_000_u128, + 1_i64, + ), + ( + 1_u128, + 0, + 1_u128, + 22, + 1_000_000_000_000_000_000_000_u128, + 1_i64, + ), + ( + 1_u128, + 0, + 1_u128, + 23, + 1_000_000_000_000_000_000_000_u128, + 2_i64, + ), + ( + 123_u128, + 0, + 1_u128, + 23, + 1_000_000_000_000_000_000_000_u128, + 2_i64, + ), + ( + 123_u128, + 1, + 1_u128, + 23, + 100_000_000_000_000_000_001_u128, + 3_i64, + ), + // Small-ish + very large (10^22 + 42) + // 42 * 10^0 + 1 * 10^22 ≈ 1e22 + 42 + // Normalized ≈ (1e21 + 4) * 10^1 + ( + 42_u128, + 0, + 1_u128, + 22, + 1_000_000_000_000_000_000_000_u128, + 1_i64, + ), + // "Almost 10^21" + 10^22 + // (10^21 - 1) + 10^22 → floor((10^22 + 10^21 - 1) / 100) * 10^2 + ( + 999_999_999_999_999_999_999_u128, + 0, + 1_u128, + 22, + 109_999_999_999_999_999_999_u128, + 2_i64, + ), + // Small-ish + 10^23 where the small part is completely lost + // 42 + 10^23 -> floor((10^23 + 42)/100) * 10^2 ≈ 1e21 * 10^2 ( - 1_000, - 21_000_000_000_000_000, - 0.00001, - 0.00000000000000000043, + 42_u128, + 0, + 1_u128, + 23, + 1_000_000_000_000_000_000_000_u128, + 2_i64, ), + // Small-ish + 10^23 where tiny part slightly affects mantissa + // 4200 + 10^23 -> floor((10^23 + 4200)/100) * 10^2 = (1e21 + 42) * 10^2 ( - 21_000_000_000_000_000, - 21_000_000_000_000_000, - 0.00001, - 0.00001, + 4_200_u128, + 0, + 1_u128, + 23, + 100_000_000_000_000_000_004_u128, + 3_i64, ), + // (10^21 - 1) + 10^23 + // -> floor((10^23 + 10^21 - 1)/100) = 1e21 + 1e19 - 1 ( - 210_000_000_000_000_000, - 21_000_000_000_000_000, - 0.00001, - 0.0001, + 999_999_999_999_999_999_999_u128, + 0, + 1_u128, + 23, + 100_999_999_999_999_999_999_u128, + 3_i64, ), + // Medium + 10^23 with exponent 1 on the smaller term + // 999_999 * 10^1 + 1 * 10^23 -> (10^22 + 999_999) * 10^1 + // Normalized ≈ (1e21 + 99_999) * 10^2 ( - 1_000, - 1_000, - 21_000_000_000_000_000_f64, - 21_000_000_000_000_000_f64, + 999_999_u128, + 1, + 1_u128, + 23, + 100_000_000_000_000_009_999_u128, + 3_i64, + ), + // Check behaviour with exponent 24, tiny second term + // 1 * 10^24 + 1 -> floor((10^24 + 1)/1000) * 10^3 ≈ 1e21 * 10^3 + ( + 1_u128, + 24, + 1_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + 3_i64, + ), + // 1 * 10^24 + a non-trivial small mantissa + // 1e24 + 123456789012345678901 -> floor(/1000) = 1e21 + 123456789012345678 + ( + 1_u128, + 24, + 123_456_789_012_345_678_901_u128, + 0, + 100_012_345_678_901_234_567_u128, + 4_i64, + ), + // 10^22 and 10^23 combined: + // 1 * 10^22 + 1 * 10^23 = 11 * 10^22 = (1.1 * 10^23) + // Normalized → (1.1e20) * 10^3 + ( + 1_u128, + 22, + 1_u128, + 23, + 110_000_000_000_000_000_000_u128, + 3_i64, + ), + // Both operands already aligned at a huge scale: + // (10^21 - 1) * 10^22 + 1 * 10^22 = 10^21 * 10^22 = 10^43 + // Canonical form: (1e21) * 10^22 + ( + 999_999_999_999_999_999_999_u128, + 22, + 1_u128, + 22, + 1_000_000_000_000_000_000_000_u128, + 22_i64, + ), + ] + .into_iter() + .for_each(|(m_a, e_a, m_b, e_b, expected_m, expected_e)| { + let a = SafeFloat::new(m_a, e_a).unwrap(); + let b = SafeFloat::new(m_b, e_b).unwrap(); + + let a_plus_b = a.add(&b).unwrap(); + let b_plus_a = b.add(&a).unwrap(); + + assert_eq!(a_plus_b.mantissa, expected_m); + assert_eq!(a_plus_b.exponent, expected_e); + assert_eq!(b_plus_a.mantissa, expected_m); + assert_eq!(b_plus_a.exponent, expected_e); + }); + } + + #[test] + fn test_safefloat_div_by_zero_is_none() { + let a = SafeFloat::new(1u128, 0).unwrap(); + assert!(a.div(&SafeFloat::zero()).is_none()); + } + + #[test] + fn test_safefloat_div() { + // Test case: man_a, exp_a, man_b, exp_b + [ + (1_u128, 0_i64, 100_000_000_000_000_000_000_u128, -20_i64), + (1_u128, 0, 1_u128, 0), + (1_u128, 1, 1_u128, 0), + (1_u128, 7, 1_u128, 0), + (1_u128, 50, 1_u128, 0), + (1_u128, 100, 1_u128, 0), + (1_u128, 0, 7_u128, 0), + (1_u128, 1, 7_u128, 0), + (1_u128, 7, 7_u128, 0), + (1_u128, 50, 7_u128, 0), + (1_u128, 100, 7_u128, 0), + (1_u128, 0, 3_u128, 0), + (1_u128, 1, 3_u128, 0), + (1_u128, 7, 3_u128, 0), + (1_u128, 50, 3_u128, 0), + (1_u128, 100, 3_u128, 0), + (2_u128, 0, 3_u128, 0), + (2_u128, 1, 3_u128, 0), + (2_u128, 7, 3_u128, 0), + (2_u128, 50, 3_u128, 0), + (2_u128, 100, 3_u128, 0), + (5_u128, 0, 3_u128, 0), + (5_u128, 1, 3_u128, 0), + (5_u128, 7, 3_u128, 0), + (5_u128, 50, 3_u128, 0), + (5_u128, 100, 3_u128, 0), + (10_u128, 0, 100_000_000_000_000_000_000_u128, -19), + (1_000_u128, 0, 100_000_000_000_000_000_000_u128, -17), + ( + 100_000_000_000_000_000_000_u128, + 0, + 1_000_000_000_000_000_000_000_u128, + -1, + ), + (SAFE_FLOAT_MAX, 0, SAFE_FLOAT_MAX, 0), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX, -100), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX - 1, -100), + (SAFE_FLOAT_MAX - 1, 100, SAFE_FLOAT_MAX, -100), + (SAFE_FLOAT_MAX - 2, 100, SAFE_FLOAT_MAX, -100), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX / 2 - 1, -100), + (SAFE_FLOAT_MAX, 100, SAFE_FLOAT_MAX / 2 - 1, 100), + (1_u128, 0, 100_000_000_000_000_000_000_u128, -20_i64), + ( + 123_456_789_123_456_789_123_u128, + 20_i64, + 87_654_321_987_654_321_987_u128, + -20_i64, + ), + ( + 123_456_789_123_456_789_123_u128, + 100_i64, + 87_654_321_987_654_321_987_u128, + -100_i64, + ), + ( + 123_456_789_123_456_789_123_u128, + -100_i64, + 87_654_321_987_654_321_987_u128, + 100_i64, + ), + ( + 123_456_789_123_456_789_123_u128, + -99_i64, + 87_654_321_987_654_321_987_u128, + 99_i64, + ), + ( + 123_456_789_123_456_789_123_u128, + 123_i64, + 87_654_321_987_654_321_987_u128, + -32_i64, + ), + ( + 123_456_789_123_456_789_123_u128, + -123_i64, + 87_654_321_987_654_321_987_u128, + 32_i64, ), ] - .iter() - .for_each(|(update, shared_value, denominator, expected)| { - let mock_ops = MockSharePoolDataOperations::new(); - let pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); - - let shared_fixed = U64F64::from_num(*shared_value); - let denominator_fixed = U64F64::from_num(*denominator); - let expected_fixed = I64F64::from_num(*expected); - - let spu: I64F64 = - pool.get_shares_per_update(*update, &shared_fixed, &denominator_fixed); - let precision: I64F64 = I64F64::from_num(1000.); - assert!((spu - expected_fixed).abs() <= expected_fixed / precision,); + .into_iter() + .for_each(|(ma, ea, mb, eb)| { + let a = SafeFloat::new(ma, ea).unwrap(); + let b = SafeFloat::new(mb, eb).unwrap(); + + let actual: f64 = a.div(&b).unwrap().into(); + let expected = + ma as f64 * (10_f64).powi(ea as i32) / (mb as f64 * (10_f64).powi(eb as i32)); + + assert_abs_diff_eq!(actual, expected, epsilon = actual / 100_000_000_000_000_f64); }); } + + #[test] + fn test_safefloat_mul_div() { + // result = a * b / c + // should not lose precision gained in a * b + // Test case: man_a, exp_a, man_b, exp_b, man_c, exp_c + [ + (1_u128, -20_i64, 1_u128, -20_i64, 1_u128, -20_i64), + (123_u128, 20_i64, 123_u128, -20_i64, 321_u128, 0_i64), + ( + 123_123_123_123_123_123_u128, + 20_i64, + 321_321_321_321_321_321_u128, + -20_i64, + 777_777_777_777_777_777_u128, + 0_i64, + ), + ( + 11_111_111_111_111_111_111_u128, + 20_i64, + 99_321_321_321_321_321_321_u128, + -20_i64, + 77_777_777_777_777_777_777_u128, + 0_i64, + ), + ] + .into_iter() + .for_each(|(ma, ea, mb, eb, mc, ec)| { + let a = SafeFloat::new(ma, ea).unwrap(); + let b = SafeFloat::new(mb, eb).unwrap(); + let c = SafeFloat::new(mc, ec).unwrap(); + + let actual: f64 = a.mul_div(&b, &c).unwrap().into(); + let expected = (ma as f64 * (10_f64).powi(ea as i32)) + * (mb as f64 * (10_f64).powi(eb as i32)) + / (mc as f64 * (10_f64).powi(ec as i32)); + + assert_abs_diff_eq!(actual, expected, epsilon = actual / 100_000_000_000_000_f64); + }); + } + + #[test] + fn test_safefloat_from_u64f64() { + [ + // U64F64::from_num(1000.0), + // U64F64::from_num(10.0), + // U64F64::from_num(1.0), + U64F64::from_num(0.1), + // U64F64::from_num(0.00000001), + // U64F64::from_num(123_456_789_123_456u128), + // // Exact zero + // U64F64::from_num(0.0), + // // Very small positive value (well above Q64.64 resolution) + // U64F64::from_num(1e-18), + // // Value just below 1 + // U64F64::from_num(0.999_999_999_999_999_f64), + // // Value just above 1 + // U64F64::from_num(1.000_000_000_000_001_f64), + // // "Random-looking" fractional with many digits + // U64F64::from_num(1.234_567_890_123_45_f64), + // // Large integer, but smaller than the max integer part of U64F64 + // U64F64::from_num(999_999_999_999_999_999u128), + // // Very large integer near the upper bound of integer range + // U64F64::from_num(u64::MAX as u128), + // // Large number with fractional part + // U64F64::from_num(123_456_789_123_456.78_f64), + // // Medium-large with tiny fractional part to test precision on tail digits + // U64F64::from_num(1_000_000_000_000.000_001_f64), + // // Smallish with long fractional part + // U64F64::from_num(0.123_456_789_012_345_f64), + ] + .into_iter() + .for_each(|f| { + let safe_float: SafeFloat = f.into(); + let actual: f64 = safe_float.into(); + let expected = f.to_num::<f64>(); + + // Relative epsilon ~1e-14 of the magnitude + let epsilon = if actual == 0.0 { + 0.0 + } else { + actual.abs() / 100_000_000_000_000_f64 + }; + + assert_abs_diff_eq!(actual, expected, epsilon = epsilon); + }); + } + + /// This is a real-life scenario test when someone lost 7 TAO on Chutes (SN64) + /// when paying fees in Alpha. The scenario occured because the update of share value + /// of one coldkey (update_value_for_one) hit the scenario of full unstake. + /// + /// Specifically, the following condition was triggered: + /// + /// `(shared_value + 2_628_000_000_000_000_u64).checked_div(new_denominator)` + /// + /// returned None because new_denominator was too low and division of + /// `shared_value + 2_628_000_000_000_000_u64` by new_denominator has overflown U64F64. + /// + /// This test fails on the old version of share pool (with much lower tolerances). + /// + /// cargo test --package share-pool --lib -- tests::test_loss_due_to_precision --exact --nocapture + #[test] + fn test_loss_due_to_precision() { + let mock_ops = MockSharePoolDataOperations::new(); + let mut pool = SharePool::<u16, MockSharePoolDataOperations>::new(mock_ops); + + // Setup pool so that initial coldkey's alpha is 10% of 1e12 = 1e11 rao. + let low_denominator = SafeFloat::new(1u128, -14).unwrap(); + let low_share = SafeFloat::new(1u128, -15).unwrap(); + pool.state_ops.set_denominator(low_denominator); + pool.state_ops.set_shared_value(1_000_000_000_000_u64); + pool.state_ops.set_share(&1, low_share); + + let value_before = pool.get_value(&1) as i128; + assert_abs_diff_eq!(value_before as f64, 100_000_000_000., epsilon = 0.1); + + // Remove a little stake + let unstake_amount = 1000i64; + pool.update_value_for_one(&1, unstake_amount.neg()); + + let value_after = pool.get_value(&1) as i128; + assert_abs_diff_eq!( + (value_before - value_after) as f64, + unstake_amount as f64, + epsilon = unstake_amount as f64 / 1_000_000_000. + ); + } + + fn rel_err(a: f64, b: f64) -> f64 { + let denom = a.abs().max(b.abs()).max(1.0); + (a - b).abs() / denom + } + + fn push_unique(v: &mut Vec<u128>, x: u128) { + if x != 0 && !v.contains(&x) { + v.push(x); + } + } + + // cargo test --package share-pool --lib -- tests::test_safefloat_mul_div_wide_range --exact --include-ignored --show-output + #[test] + #[ignore = "long-running sweep test; run explicitly when needed"] + fn test_safefloat_mul_div_wide_range() { + use rayon::prelude::*; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + // Build mantissa corpus + let mut mantissas = Vec::<u128>::new(); + + let linear_steps: u128 = 200; + let linear_step = (SAFE_FLOAT_MAX / linear_steps).max(1); + let mut m = 1u128; + while m <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, m); + match m.checked_add(linear_step) { + Some(next) if next > m => m = next, + _ => break, + } + } + push_unique(&mut mantissas, SAFE_FLOAT_MAX); + + let mut p = 1u128; + while p <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, p); + if p > 1 { + push_unique(&mut mantissas, p - 1); + } + if let Some(next) = p.checked_add(1) + && next <= SAFE_FLOAT_MAX + { + push_unique(&mut mantissas, next); + } + + match p.checked_mul(10) { + Some(next) if next > p && next <= SAFE_FLOAT_MAX => p = next, + _ => break, + } + } + + for delta in [ + 0u128, 1, 2, 3, 7, 9, 10, 11, 99, 100, 101, 999, 1_000, 10_000, + ] { + if SAFE_FLOAT_MAX > delta { + push_unique(&mut mantissas, SAFE_FLOAT_MAX - delta); + } + } + + mantissas.sort_unstable(); + mantissas.dedup(); + + let exp_min: i64 = -120; + let exp_max: i64 = 120; + let exp_step: usize = 5; + let exponents: Vec<i64> = (exp_min..=exp_max).step_by(exp_step).collect(); + + // Precompute all (a, b) pairs as outer work items. + // Each Rayon task will then iterate all c's sequentially. + let mut outer_cases: Vec<(u128, i64, u128, i64)> = Vec::new(); + + for &ma in &mantissas { + for &ea in &exponents { + for &mb in &mantissas { + for &eb in &exponents { + outer_cases.push((ma, ea, mb, eb)); + } + } + } + } + + let checked = Arc::new(AtomicUsize::new(0)); + let skipped_non_finite = Arc::new(AtomicUsize::new(0)); + let skipped_invalid_sf = Arc::new(AtomicUsize::new(0)); + + let progress_step = 10_000usize; + let total_outer = outer_cases.len(); + + outer_cases.into_par_iter().for_each(|(ma, ea, mb, eb)| { + let a = match SafeFloat::new(ma, ea) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let b = match SafeFloat::new(mb, eb) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + for &mc in &mantissas { + for &ec in &exponents { + let c = match SafeFloat::new(mc, ec) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + continue; + } + }; + + let actual_sf = a.mul_div(&b, &c).unwrap(); + let actual: f64 = actual_sf.into(); + + let expected = + (ma as f64 * 10_f64.powi(ea as i32)) + * (mb as f64 * 10_f64.powi(eb as i32)) + / (mc as f64 * 10_f64.powi(ec as i32)); + + if !expected.is_finite() || !actual.is_finite() { + skipped_non_finite.fetch_add(1, Ordering::Relaxed); + continue; + } + + let err = rel_err(actual, expected); + + assert!( + err <= 1e-12, + concat!( + "mul_div mismatch:\n", + " a = {}e{}\n", + " b = {}e{}\n", + " c = {}e{}\n", + " actual = {:.20e}\n", + " expected = {:.20e}\n", + " rel_err = {:.20e}" + ), + ma, ea, mb, eb, mc, ec, actual, expected, err + ); + + checked.fetch_add(1, Ordering::Relaxed); + } + } + + let done_outer = checked.load(Ordering::Relaxed); + if done_outer % progress_step == 0 { + let invalid = skipped_invalid_sf.load(Ordering::Relaxed); + let non_finite = skipped_non_finite.load(Ordering::Relaxed); + log::debug!( + "progress: checked={}, skipped_invalid_sf={}, skipped_non_finite={}, outer_total={}", + done_outer, + invalid, + non_finite, + total_outer, + ); + } + }); + + let checked = checked.load(Ordering::Relaxed); + let skipped_non_finite = skipped_non_finite.load(Ordering::Relaxed); + let skipped_invalid_sf = skipped_invalid_sf.load(Ordering::Relaxed); + + println!( + "checked={}, skipped_non_finite={}, skipped_invalid_sf={}, mantissas={}, exponents={}, outer_cases={}", + checked, + skipped_non_finite, + skipped_invalid_sf, + mantissas.len(), + exponents.len(), + total_outer, + ); + + assert!(checked > 0, "test did not validate any finite cases"); + } + + #[test] + #[ignore = "long-running broad-range test; run explicitly when needed"] + fn test_safefloat_div_wide_range() { + use rayon::prelude::*; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + fn rel_err(a: f64, b: f64) -> f64 { + let denom = a.abs().max(b.abs()).max(1.0); + (a - b).abs() / denom + } + + fn push_unique(v: &mut Vec<u128>, x: u128) { + if x != 0 && !v.contains(&x) { + v.push(x); + } + } + + // Build a broad mantissa corpus: + // - coarse linear sweep + // - powers of 10 and neighbors + // - values near SAFE_FLOAT_MAX + let mut mantissas = Vec::<u128>::new(); + + let linear_steps: u128 = 200; + let linear_step = (SAFE_FLOAT_MAX / linear_steps).max(1); + let mut m = 1u128; + while m <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, m); + match m.checked_add(linear_step) { + Some(next) if next > m => m = next, + _ => break, + } + } + push_unique(&mut mantissas, SAFE_FLOAT_MAX); + + let mut p = 1u128; + while p <= SAFE_FLOAT_MAX { + push_unique(&mut mantissas, p); + if p > 1 { + push_unique(&mut mantissas, p - 1); + } + if let Some(next) = p.checked_add(1) + && next <= SAFE_FLOAT_MAX + { + push_unique(&mut mantissas, next); + } + + match p.checked_mul(10) { + Some(next) if next > p && next <= SAFE_FLOAT_MAX => p = next, + _ => break, + } + } + + for delta in [ + 0u128, 1, 2, 3, 7, 9, 10, 11, 99, 100, 101, 999, 1_000, 10_000, + ] { + if SAFE_FLOAT_MAX > delta { + push_unique(&mut mantissas, SAFE_FLOAT_MAX - delta); + } + } + + mantissas.sort_unstable(); + mantissas.dedup(); + + // Exponent sweep. + // Keep it large enough to stress normalization / exponent math, + // but still practical for f64 reference calculations. + let exp_min: i64 = -120; + let exp_max: i64 = 120; + let exp_step: usize = 5; + let exponents: Vec<i64> = (exp_min..=exp_max).step_by(exp_step).collect(); + + let m_len = mantissas.len(); + let e_len = exponents.len(); + let total_cases = m_len * e_len * m_len * e_len; + + let checked = Arc::new(AtomicUsize::new(0)); + let skipped_non_finite = Arc::new(AtomicUsize::new(0)); + let skipped_invalid_sf = Arc::new(AtomicUsize::new(0)); + let done_counter = Arc::new(AtomicUsize::new(0)); + + (0..total_cases).into_par_iter().for_each(|idx| { + let mut rem = idx; + + let eb_idx = rem % e_len; + rem /= e_len; + + let mb_idx = rem % m_len; + rem /= m_len; + + let ea_idx = rem % e_len; + rem /= e_len; + + let ma_idx = rem % m_len; + + let ma = mantissas[ma_idx]; + let ea = exponents[ea_idx]; + let mb = mantissas[mb_idx]; + let eb = exponents[eb_idx]; + + let a = match SafeFloat::new(ma, ea) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + done_counter.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let b = match SafeFloat::new(mb, eb) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + done_counter.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let actual_sf = match a.div(&b) { + Some(x) => x, + None => { + skipped_invalid_sf.fetch_add(1, Ordering::Relaxed); + done_counter.fetch_add(1, Ordering::Relaxed); + return; + } + }; + + let actual: f64 = actual_sf.into(); + let expected = + (ma as f64 * 10_f64.powi(ea as i32)) / (mb as f64 * 10_f64.powi(eb as i32)); + + if !actual.is_finite() || !expected.is_finite() { + skipped_non_finite.fetch_add(1, Ordering::Relaxed); + } else { + let err = rel_err(actual, expected); + + assert!( + err <= 1e-12, + concat!( + "div mismatch:\n", + " a = {}e{}\n", + " b = {}e{}\n", + " actual = {:.20e}\n", + " expected = {:.20e}\n", + " rel_err = {:.20e}" + ), + ma, + ea, + mb, + eb, + actual, + expected, + err + ); + + checked.fetch_add(1, Ordering::Relaxed); + } + + let done = done_counter.fetch_add(1, Ordering::Relaxed) + 1; + if done % 10_000 == 0 { + let progress = done as f64 / total_cases as f64 * 100.0; + log::debug!("div progress = {progress:.4}%"); + } + }); + + let checked = checked.load(Ordering::Relaxed); + let skipped_non_finite = skipped_non_finite.load(Ordering::Relaxed); + let skipped_invalid_sf = skipped_invalid_sf.load(Ordering::Relaxed); + + println!( + "div checked={}, skipped_non_finite={}, skipped_invalid_sf={}, mantissas={}, exponents={}, total_cases={}", + checked, + skipped_non_finite, + skipped_invalid_sf, + mantissas.len(), + exponents.len(), + total_cases, + ); + + assert!(checked > 0, "div test did not validate any finite cases"); + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 4bbb6a88c8..ecb8944aa9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -268,7 +268,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 391, + spec_version: 394, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -1210,8 +1210,8 @@ impl pallet_subtensor_swap::Config for Runtime { type SubnetInfo = SubtensorModule; type BalanceOps = SubtensorModule; type ProtocolId = SwapProtocolId; - type TaoReserve = pallet_subtensor::TaoCurrencyReserve<Self>; - type AlphaReserve = pallet_subtensor::AlphaCurrencyReserve<Self>; + type TaoReserve = pallet_subtensor::TaoBalanceReserve<Self>; + type AlphaReserve = pallet_subtensor::AlphaBalanceReserve<Self>; type MaxFeeRate = SwapMaxFeeRate; type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; diff --git a/scripts/inspect_orphan_axons.py b/scripts/inspect_orphan_axons.py new file mode 100644 index 0000000000..9d7a3c90d1 --- /dev/null +++ b/scripts/inspect_orphan_axons.py @@ -0,0 +1,105 @@ +""" +Inspect mainnet state for orphaned Axon/Prometheus/NeuronCertificate entries. + +An entry is "orphaned" when the hotkey is not registered on that subnet (not in Uids), +yet has an entry in Axons, Prometheus, or NeuronCertificates for that netuid. + +This can happen because serve_axon / serve_prometheus used +is_hotkey_registered_on_any_network instead of is_hotkey_registered_on_network. + +Usage: + scripts/.venv/bin/python scripts/inspect_orphan_axons.py +""" + +from substrateinterface import SubstrateInterface + +MAINNET_URL = "wss://entrypoint-finney.opentensor.ai:443" + +def query_double_map_keys(substrate, pallet, storage, block_hash): + """Return all (key1, key2) pairs from a StorageDoubleMap.""" + result = substrate.query_map( + module=pallet, + storage_function=storage, + block_hash=block_hash, + ) + keys = [] + for (key, _value) in result: + # key is a list of two decoded keys for a double map + keys.append((key[0].value, key[1].value)) + return keys + +def main(): + print(f"Connecting to {MAINNET_URL} ...") + substrate = SubstrateInterface(url=MAINNET_URL) + + block_hash = substrate.get_block_hash() + print(f"At block hash: {block_hash}\n") + + # --- Build registered set: (netuid, hotkey) --- + print("Fetching Uids (registered neurons) ...") + uids_entries = substrate.query_map( + module="SubtensorModule", + storage_function="Uids", + block_hash=block_hash, + ) + registered = set() + for (key, _value) in uids_entries: + netuid = key[0].value + hotkey = key[1].value + registered.add((netuid, hotkey)) + print(f" {len(registered)} registered (netuid, hotkey) pairs") + + # --- Check Axons --- + print("\nFetching Axons ...") + axon_keys = query_double_map_keys(substrate, "SubtensorModule", "Axons", block_hash) + print(f" {len(axon_keys)} total Axons entries") + orphan_axons = [(n, h) for (n, h) in axon_keys if (n, h) not in registered] + print(f" {len(orphan_axons)} orphaned Axons entries (hotkey not registered on that netuid)") + + # --- Check Prometheus --- + print("\nFetching Prometheus ...") + prom_keys = query_double_map_keys(substrate, "SubtensorModule", "Prometheus", block_hash) + print(f" {len(prom_keys)} total Prometheus entries") + orphan_prom = [(n, h) for (n, h) in prom_keys if (n, h) not in registered] + print(f" {len(orphan_prom)} orphaned Prometheus entries") + + # --- Check NeuronCertificates --- + print("\nFetching NeuronCertificates ...") + cert_keys = query_double_map_keys(substrate, "SubtensorModule", "NeuronCertificates", block_hash) + print(f" {len(cert_keys)} total NeuronCertificates entries") + orphan_certs = [(n, h) for (n, h) in cert_keys if (n, h) not in registered] + print(f" {len(orphan_certs)} orphaned NeuronCertificates entries") + + total_orphans = len(orphan_axons) + len(orphan_prom) + len(orphan_certs) + print(f"\n=== TOTAL orphaned entries: {total_orphans} ===") + + if orphan_axons: + print("\nOrphaned Axon entries by netuid:") + by_net = {} + for (n, h) in orphan_axons: + by_net.setdefault(n, []).append(h) + for netuid, hotkeys in sorted(by_net.items()): + print(f" netuid={netuid}: {len(hotkeys)} orphans") + + if orphan_prom: + print("\nOrphaned Prometheus entries by netuid:") + by_net = {} + for (n, h) in orphan_prom: + by_net.setdefault(n, []).append(h) + for netuid, hotkeys in sorted(by_net.items()): + print(f" netuid={netuid}: {len(hotkeys)} orphans") + + if orphan_certs: + print("\nOrphaned NeuronCertificate entries by netuid:") + by_net = {} + for (n, h) in orphan_certs: + by_net.setdefault(n, []).append(h) + for netuid, hotkeys in sorted(by_net.items()): + print(f" netuid={netuid}: {len(hotkeys)} orphans") + + return total_orphans + +if __name__ == "__main__": + total = main() + print(f"\nDecision threshold: >1000 total orphans => use sudo extrinsic; <=1000 => simple migration") + print(f"Result: {total} orphans => {'SUDO EXTRINSIC' if total > 1000 else 'MIGRATION'}") diff --git a/scripts/localnet.sh b/scripts/localnet.sh index 5a67e8f3bc..b74820f21e 100755 --- a/scripts/localnet.sh +++ b/scripts/localnet.sh @@ -35,14 +35,14 @@ if [ "$fast_runtime" == "False" ]; then echo "fast_runtime is Off" : "${CHAIN:=local}" : "${BUILD_BINARY:=1}" - : "${FEATURES:="pow-faucet"}" + : "${FEATURES:="pow-faucet metadata-hash"}" BUILD_DIR="$BASE_DIR/target/non-fast-runtime" else # Block of code to execute if fast_runtime is not False echo "fast_runtime is On" : "${CHAIN:=local}" : "${BUILD_BINARY:=1}" - : "${FEATURES:="pow-faucet fast-runtime"}" + : "${FEATURES:="pow-faucet metadata-hash fast-runtime"}" BUILD_DIR="$BASE_DIR/target/fast-runtime" fi diff --git a/scripts/srtool/build-srtool-image.sh b/scripts/srtool/build-srtool-image.sh index d736fe8a1e..e2c9580973 100755 --- a/scripts/srtool/build-srtool-image.sh +++ b/scripts/srtool/build-srtool-image.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker build --build-arg RUSTC_VERSION="1.85.0" -t srtool https://github.com/paritytech/srtool.git#refs/tags/v0.17.0 \ No newline at end of file +docker build --build-arg RUSTC_VERSION="1.89.0" -t srtool https://github.com/paritytech/srtool.git#refs/tags/v0.18.3 diff --git a/ts-tests/.gitignore b/ts-tests/.gitignore new file mode 100644 index 0000000000..d5e657e031 --- /dev/null +++ b/ts-tests/.gitignore @@ -0,0 +1,4 @@ +tmp +specs +html +.papi \ No newline at end of file diff --git a/e2e/.nvmrc b/ts-tests/.nvmrc similarity index 100% rename from e2e/.nvmrc rename to ts-tests/.nvmrc diff --git a/ts-tests/biome.jsonc b/ts-tests/biome.jsonc new file mode 100644 index 0000000000..426aca155a --- /dev/null +++ b/ts-tests/biome.jsonc @@ -0,0 +1,60 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false, + }, + "files": { + "ignoreUnknown": false, + "ignore": [ + "**/html/**", + "**/build/**", + "**/target/**", + "**/scripts/tmp/**", + "**/node_modules/**", + "**/.yarn/**", + "test/tsconfig.json", + "tmp", + "**/tmp/", + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "lineWidth": 120, + "indentWidth": 4, + }, + "organizeImports": { + "enabled": true, + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off", + "noShadowRestrictedNames": "off", + }, + "correctness": { + "noUnusedImports": "error", + }, + }, + "ignore": [], + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "semicolons": "always", + "trailingCommas": "es5", + }, + }, + "json": { + "formatter": { + "enabled": false, + }, + "linter": { + "enabled": false, + }, + }, +} diff --git a/ts-tests/configs/zombie_extended.json b/ts-tests/configs/zombie_extended.json new file mode 100644 index 0000000000..2f0f772442 --- /dev/null +++ b/ts-tests/configs/zombie_extended.json @@ -0,0 +1,56 @@ +{ + "settings": { + "timeout": 1000, + "provider": "native" + }, + "relaychain": { + "chain_spec_path": "specs/chain-spec.json", + "default_command": "../target/release/node-subtensor", + "default_args": ["-lmev-shield=debug"], + "genesis": { + "runtimeGenesis": { + "patch": { + "configuration": { + "config": { + + } + } + } + } + }, + "nodes": [ + { + "name": "one", + "rpc_port": "9947", + "validator": true + }, + { + "name": "two", + "rpc_port": "9948", + "validator": true + }, + { + "name": "three", + "rpc_port": "9949", + "validator": true + }, + { + "name": "four", + "rpc_port": "9950" + }, + { + "name": "five" + }, + { + "name": "six" + } + ] + }, + "types": { + "Header": { + "number": "u64", + "parent_hash": "Hash", + "post_state": "Hash" + } + } +} \ No newline at end of file diff --git a/ts-tests/configs/zombie_node.json b/ts-tests/configs/zombie_node.json new file mode 100644 index 0000000000..f073be80bd --- /dev/null +++ b/ts-tests/configs/zombie_node.json @@ -0,0 +1,47 @@ +{ + "settings": { + "timeout": 1000, + "provider": "native" + }, + "relaychain": { + "chain_spec_path": "specs/chain-spec.json", + "default_command": "../target/release/node-subtensor", + "default_args": [ + ], + "genesis": { + "runtimeGenesis": { + "patch": { + "configuration": { + "config": { + + } + } + } + } + }, + "nodes": [ + { + "name": "one", + "rpc_port": "9947", + "validator": true + }, + { + "name": "two", + "rpc_port": "9948", + "validator": true + }, + { + "name": "three", + "rpc_port": "9949", + "validator": true + } + ] + }, + "types": { + "Header": { + "number": "u64", + "parent_hash": "Hash", + "post_state": "Hash" + } + } +} \ No newline at end of file diff --git a/ts-tests/moonwall.config.json b/ts-tests/moonwall.config.json new file mode 100644 index 0000000000..118705e10b --- /dev/null +++ b/ts-tests/moonwall.config.json @@ -0,0 +1,127 @@ +{ + "$schema": "https://raw.githubusercontent.com/Moonsong-Labs/moonwall/main/packages/types/config_schema.json", + "label": "💃 MasterConfig", + "defaultTestTimeout": 120000, + "scriptsDir": "scripts/", + "environments": [ + { + "name": "dev", + "timeout": 120000, + "envVars": ["DEBUG_COLORS=1"], + "testFileDir": [ + "suites/dev" + ], + "runScripts": [], + "multiThreads": true, + "reporters": ["basic"], + "foundation": { + "type": "dev", + "launchSpec": [ + { + "name": "subtensor", + "binPath": "../target/release/node-subtensor", + "options": [ + "--one", + "--dev", + "--force-authoring", + "--rpc-cors=all", + "--no-prometheus", + "--no-telemetry", + "--reserved-only", + "--tmp", + "--sealing=manual" + ], + "disableDefaultEthProviders": true, + "newRpcBehaviour": true + } + ] + } + }, + { + "name": "zombienet_staking", + "timeout": 600000, + "testFileDir": ["suites/zombienet_staking"], + "runScripts": [ + "generate-types.sh", + "build-spec.sh" + ], + "foundation": { + "type": "zombie", + "zombieSpec": { + "configPath": "./configs/zombie_node.json", + "skipBlockCheck": [] + } + }, + "vitestArgs": { + "bail": 1 + }, + "connections": [ + { + "name": "Node", + "type": "papi", + "endpoints": ["ws://127.0.0.1:9947"] + } + ] + }, { + "name": "zombienet_shield", + "timeout": 600000, + "testFileDir": ["suites/zombienet_shield"], + "runScripts": [ + "generate-types.sh", + "build-spec.sh" + ], + "foundation": { + "type": "zombie", + "zombieSpec": { + "configPath": "./configs/zombie_extended.json", + "skipBlockCheck": [] + } + }, + "vitestArgs": { + "bail": 1 + }, + "connections": [ + { + "name": "Node", + "type": "papi", + "endpoints": ["ws://127.0.0.1:9947"], + "descriptor": "subtensor" + }, + { + "name": "NodeFull", + "type": "papi", + "endpoints": ["ws://127.0.0.1:9950"], + "descriptor": "subtensor" + } + ] + }, { + "name": "smoke_mainnet", + "testFileDir": ["suites/smoke"], + "foundation": { + "type": "read_only" + }, + "reporters": ["basic","html"], + "connections": [ + { + "name": "node", + "type": "polkadotJs", + "endpoints": ["wss://openrpc.taostats.io"] + } + ] + }, { + "name": "smoke_testnet", + "testFileDir": ["suites/smoke"], + "foundation": { + "type": "read_only" + }, + "reporters": ["basic","html"], + "connections": [ + { + "name": "node", + "type": "polkadotJs", + "endpoints": ["wss://test.finney.opentensor.ai:443"] + } + ] + } + ] +} diff --git a/ts-tests/package.json b/ts-tests/package.json new file mode 100644 index 0000000000..75b5506cf8 --- /dev/null +++ b/ts-tests/package.json @@ -0,0 +1,72 @@ +{ + "name": "test", + "private": true, + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "clean": "rm -rf node_modules tmp", + "start": "moonwall", + "fmt": "biome format .", + "fmt:fix": "biome format --write .", + "lint": "biome lint . && tsc --noEmit", + "lint:fix": "biome lint --write .", + "generate-types": "polkadot-api add subtensor --wsUrl ws://localhost:9944 --skip-codegen && polkadot-api" + }, + "keywords": [], + "author": "", + "license": "ISC", + "optionalDependencies": { + "@polkadot-api/descriptors": "file:.papi/descriptors" + }, + "dependencies": { + "@inquirer/prompts": "7.3.1", + "@noble/ciphers": "^2.1.1", + "polkadot-api": "1.19.2", + "@polkadot-api/merkleize-metadata": "^1.1.15", + "@polkadot-api/substrate-bindings": "^0.17.0", + "@polkadot/api": "*", + "@polkadot/keyring": "*", + "@polkadot/types": "*", + "@polkadot/types-codec": "*", + "@polkadot/util": "*", + "@polkadot/util-crypto": "*", + "@zombienet/orchestrator": "0.0.105", + "ethereum-cryptography": "3.1.0", + "mlkem": "^2.7.0", + "ps-node": "0.1.6" + }, + "devDependencies": { + "@acala-network/chopsticks": "1.2.3", + "@biomejs/biome": "1.9.4", + "@moonwall/cli": "5.18.3", + "@moonwall/util": "5.18.3", + "@polkadot/wasm-crypto": "^7.4.1", + "@types/debug": "4.1.12", + "@types/json-bigint": "1.0.4", + "@types/node": "*", + "@types/ps-node": "0.1.3", + "@types/yargs": "^17.0.33", + "@vitest/ui": "3.1.3", + "@zombienet/utils": "^0.0.28", + "bottleneck": "2.19.5", + "chalk": "^5.4.0", + "debug": "4.3.7", + "ethers": "^6.13.4", + "json-bigint": "1.0.0", + "pnpm": "10.32.1", + "solc": "0.8.21", + "toml": "^3.0.0", + "tsx": "*", + "typescript": "5.8.3", + "viem": "2.38.0", + "vitest": "3.2.4", + "web3": "4.15.0", + "web3-providers-ws": "4.0.8", + "yargs": "18.0.0" + }, + "volta": { + "node": "22.12.0" + } +} diff --git a/ts-tests/pnpm-lock.yaml b/ts-tests/pnpm-lock.yaml new file mode 100644 index 0000000000..d92a6b9cd6 --- /dev/null +++ b/ts-tests/pnpm-lock.yaml @@ -0,0 +1,10526 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@inquirer/prompts': + specifier: 7.3.1 + version: 7.3.1(@types/node@25.3.5) + '@noble/ciphers': + specifier: ^2.1.1 + version: 2.1.1 + '@polkadot-api/merkleize-metadata': + specifier: ^1.1.15 + version: 1.1.29 + '@polkadot-api/substrate-bindings': + specifier: ^0.17.0 + version: 0.17.0 + '@polkadot/api': + specifier: '*' + version: 16.5.4 + '@polkadot/keyring': + specifier: '*' + version: 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/types': + specifier: '*' + version: 16.5.4 + '@polkadot/types-codec': + specifier: '*' + version: 16.5.4 + '@polkadot/util': + specifier: '*' + version: 14.0.1 + '@polkadot/util-crypto': + specifier: '*' + version: 14.0.1(@polkadot/util@14.0.1) + '@zombienet/orchestrator': + specifier: 0.0.105 + version: 0.0.105(@polkadot/util@14.0.1)(@types/node@25.3.5)(chokidar@3.6.0) + ethereum-cryptography: + specifier: 3.1.0 + version: 3.1.0 + mlkem: + specifier: ^2.7.0 + version: 2.7.0 + polkadot-api: + specifier: 1.19.2 + version: 1.19.2(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(yaml@2.8.2) + ps-node: + specifier: 0.1.6 + version: 0.1.6 + devDependencies: + '@acala-network/chopsticks': + specifier: 1.2.3 + version: 1.2.3(debug@4.3.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3)) + '@biomejs/biome': + specifier: 1.9.4 + version: 1.9.4 + '@moonwall/cli': + specifier: 5.18.3 + version: 5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api-derive@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/rpc-provider@16.5.4)(@polkadot/types-codec@16.5.4)(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@types/node@25.3.5)(chokidar@3.6.0)(debug@4.3.7)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3))(tsx@4.21.0)(typescript@5.8.3)(zod@3.25.76) + '@moonwall/util': + specifier: 5.18.3 + version: 5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api-derive@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/rpc-provider@16.5.4)(@polkadot/types-codec@16.5.4)(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@types/node@25.3.5)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76) + '@polkadot/wasm-crypto': + specifier: ^7.4.1 + version: 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@types/debug': + specifier: 4.1.12 + version: 4.1.12 + '@types/json-bigint': + specifier: 1.0.4 + version: 1.0.4 + '@types/node': + specifier: '*' + version: 25.3.5 + '@types/ps-node': + specifier: 0.1.3 + version: 0.1.3 + '@types/yargs': + specifier: ^17.0.33 + version: 17.0.35 + '@vitest/ui': + specifier: 3.1.3 + version: 3.1.3(vitest@3.2.4) + '@zombienet/utils': + specifier: ^0.0.28 + version: 0.0.28(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3) + bottleneck: + specifier: 2.19.5 + version: 2.19.5 + chalk: + specifier: ^5.4.0 + version: 5.6.2 + debug: + specifier: 4.3.7 + version: 4.3.7(supports-color@8.1.1) + ethers: + specifier: ^6.13.4 + version: 6.16.0 + json-bigint: + specifier: 1.0.0 + version: 1.0.0 + pnpm: + specifier: 10.32.1 + version: 10.32.1 + solc: + specifier: 0.8.21 + version: 0.8.21(debug@4.3.7) + toml: + specifier: ^3.0.0 + version: 3.0.0 + tsx: + specifier: '*' + version: 4.21.0 + typescript: + specifier: 5.8.3 + version: 5.8.3 + viem: + specifier: 2.38.0 + version: 2.38.0(typescript@5.8.3)(zod@3.25.76) + vitest: + specifier: 3.2.4 + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(@vitest/ui@3.1.3)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2) + web3: + specifier: 4.15.0 + version: 4.15.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-providers-ws: + specifier: 4.0.8 + version: 4.0.8 + yargs: + specifier: 18.0.0 + version: 18.0.0 + optionalDependencies: + '@polkadot-api/descriptors': + specifier: file:.papi/descriptors + version: file:.papi/descriptors(polkadot-api@1.19.2(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(yaml@2.8.2)) + +packages: + + '@acala-network/chopsticks-core@1.2.3': + resolution: {integrity: sha512-BHKo0FjnTktOFFeJqydByn2btwMKedRp2xC2zT1+Hr8cpZ1UTfLGW+XWbfg8/RwfXRYt5IWQwxqPyXGpCquCcw==} + engines: {node: '>=v22'} + + '@acala-network/chopsticks-core@1.2.7': + resolution: {integrity: sha512-TU//5U2Mx4YAQYjew+05i95j+YsfusuUJAST8Oy7Jkeaflc5CdzGXGc2Tjcn7J6VOSQA1/ngbDjdDIyf4Xjjlw==} + engines: {node: '>=v22'} + + '@acala-network/chopsticks-db@1.2.3': + resolution: {integrity: sha512-Wn3n7Xmuo/523NP4COSYDB75xI1h3r2AhY99ioO26mCEkv8RAH053f/yVgUGPQDTX1ov3DygKj47zxP4szwEiQ==} + engines: {node: '>=v22'} + + '@acala-network/chopsticks-db@1.2.7': + resolution: {integrity: sha512-QVF22l8kehU4SxSdIHd8VsRZyxQGNQjR92fFzybS+zDJXN1B7cIItMfHMe0giiH1aEoA4V1DW9Y6eC4PV7JVGg==} + engines: {node: '>=v22'} + + '@acala-network/chopsticks-executor@1.2.3': + resolution: {integrity: sha512-FcO3NtCfgkAh07P4eHsKcYr2JmImI95xs7xQjEICfyRRNTAonBfJFR8D2voRcoatxkNz6VCVVRgCvILBAuER9Q==} + + '@acala-network/chopsticks-executor@1.2.7': + resolution: {integrity: sha512-pDptKUkKpy74b0Ui29QYsm4Gr7BFa3ehZ6VKbe8Chgveye7bMEIMJYpiXSbzovHwz47NGMJD0+d4v8NzWi7V9g==} + + '@acala-network/chopsticks@1.2.3': + resolution: {integrity: sha512-roD+7fyjU3kHEO1czUF9vBIBabFN4VFfYv4tBLA2fg+Hc/GMM3OJ98oHrCMSusp0UgpPpyo4sHKAT0gS418n2g==} + engines: {node: '>=v22'} + hasBin: true + + '@acala-network/chopsticks@1.2.7': + resolution: {integrity: sha512-kqahze0KrCqb0zX9OUW003iNux2g6WZF+WEU2uayPGJ7pIx1PE9Dq4+8cCMR99MKo6OwmCsslFDueUG4BwyZyQ==} + engines: {node: '>=v22'} + hasBin: true + + '@adraffy/ens-normalize@1.10.1': + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@alcalzone/ansi-tokenize@0.2.5': + resolution: {integrity: sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw==} + engines: {node: '>=18'} + + '@ark/util@0.56.0': + resolution: {integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@asamuzakjp/dom-selector@2.0.2': + resolution: {integrity: sha512-x1KXOatwofR6ZAYzXRBL5wrdV0vwNxlTCK9NCuLqAzQYARqGcvFwiJA6A1ERuh+dgeA4Dxm3JBYictIes+SqUQ==} + + '@ast-grep/napi-darwin-arm64@0.40.5': + resolution: {integrity: sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@ast-grep/napi-darwin-x64@0.40.5': + resolution: {integrity: sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@ast-grep/napi-linux-arm64-gnu@0.40.5': + resolution: {integrity: sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@ast-grep/napi-linux-arm64-musl@0.40.5': + resolution: {integrity: sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@ast-grep/napi-linux-x64-gnu@0.40.5': + resolution: {integrity: sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@ast-grep/napi-linux-x64-musl@0.40.5': + resolution: {integrity: sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@ast-grep/napi-win32-arm64-msvc@0.40.5': + resolution: {integrity: sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@ast-grep/napi-win32-ia32-msvc@0.40.5': + resolution: {integrity: sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@ast-grep/napi-win32-x64-msvc@0.40.5': + resolution: {integrity: sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@ast-grep/napi@0.40.5': + resolution: {integrity: sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A==} + engines: {node: '>= 10'} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + + '@biomejs/biome@1.9.4': + resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@1.9.4': + resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@1.9.4': + resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@1.9.4': + resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-arm64@1.9.4': + resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-linux-x64-musl@1.9.4': + resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@1.9.4': + resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-win32-arm64@1.9.4': + resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@1.9.4': + resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@commander-js/extra-typings@14.0.0': + resolution: {integrity: sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==} + peerDependencies: + commander: ~14.0.0 + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@dmsnell/diff-match-patch@1.1.0': + resolution: {integrity: sha512-yejLPmM5pjsGvxS9gXablUSbInW7H976c/FJ4iQxWIm7/38xBySRemTPDe34lhg1gVLbJntX0+sH0jYfU+PN9A==} + + '@effect/cluster@0.55.0': + resolution: {integrity: sha512-WeLrGpdtWhr4ap3kcW+G+qYyDqzEa65ypPWsU+sNU2gordENsAkxUQqo8taKr114AOk6WE74q0I7iWABcfGlhQ==} + peerDependencies: + '@effect/platform': ^0.93.6 + '@effect/rpc': ^0.72.2 + '@effect/sql': ^0.48.6 + '@effect/workflow': ^0.15.0 + effect: ^3.19.8 + + '@effect/experimental@0.57.11': + resolution: {integrity: sha512-M5uug3Drs/gyTHLfA+XzcIZQGUEV/Jn5yi1POki4oZswhpzNmsVTHl4THpxAordRKwa5lFvTSlsRP684YH7pSw==} + peerDependencies: + '@effect/platform': ^0.93.6 + effect: ^3.19.9 + ioredis: ^5 + lmdb: ^3 + peerDependenciesMeta: + ioredis: + optional: true + lmdb: + optional: true + + '@effect/platform-node-shared@0.56.0': + resolution: {integrity: sha512-0RawLcUCLHVGs4ch1nY26P4xM+U6R03ZR02MgNHMsL0slh8YYlal5PnwD/852rJ59O9prQX3Kq8zs+cGVoLAJw==} + peerDependencies: + '@effect/cluster': ^0.55.0 + '@effect/platform': ^0.93.6 + '@effect/rpc': ^0.72.2 + '@effect/sql': ^0.48.6 + effect: ^3.19.8 + + '@effect/platform-node@0.103.0': + resolution: {integrity: sha512-N2JmOvHInHAC+JFdt+ME8/Pn9vdgBwYTTcqlSXkT+mBzq6fAKdwHkXHoFUMbk8bWtJGx70oezLLEetatjsveaA==} + peerDependencies: + '@effect/cluster': ^0.55.0 + '@effect/platform': ^0.93.6 + '@effect/rpc': ^0.72.2 + '@effect/sql': ^0.48.6 + effect: ^3.19.8 + + '@effect/platform@0.93.8': + resolution: {integrity: sha512-xTEy6fyTy4ijmFC3afKgtvYtn/JyPoIov4ZSUWJZUv3VeOcUPNGrrqG6IJlWkXs3NhvSywKv7wc1kw3epCQVZw==} + peerDependencies: + effect: ^3.19.12 + + '@effect/rpc@0.72.2': + resolution: {integrity: sha512-BmTXybXCOq96D2r9mvSW/YdiTQs5CStnd4II+lfVKrMr3pMNERKLZ2LG37Tfm4Sy3Q8ire6IVVKO/CN+VR0uQQ==} + peerDependencies: + '@effect/platform': ^0.93.3 + effect: ^3.19.5 + + '@effect/sql@0.48.6': + resolution: {integrity: sha512-OBIG/DYFxYTA9EXXhqi6sAcX0YLz8Huu8L+wj3a0aOSRPpHm9HkL9a5lacRPPvrVl31rKcEteGa/lO6n26gIFg==} + peerDependencies: + '@effect/experimental': ^0.57.9 + '@effect/platform': ^0.93.6 + effect: ^3.19.8 + + '@effect/workflow@0.15.2': + resolution: {integrity: sha512-UAo5QWEvyyKsnf4EQ7WL3zwiuZS4Wd5fmAxdpcpZSIxNOvsABp3DOuyRCiidD8l3sQhdPwES/UsVK4QOCQ7wew==} + peerDependencies: + '@effect/experimental': ^0.57.11 + '@effect/platform': ^0.93.6 + '@effect/rpc': ^0.72.2 + effect: ^3.19.10 + + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@ethereumjs/rlp@4.0.1': + resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} + engines: {node: '>=14'} + hasBin: true + + '@ethereumjs/rlp@5.0.2': + resolution: {integrity: sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==} + engines: {node: '>=18'} + hasBin: true + + '@gar/promisify@1.1.3': + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + + '@grpc/grpc-js@1.14.3': + resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} + engines: {node: '>=18'} + + '@inquirer/ansi@2.0.3': + resolution: {integrity: sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/checkbox@4.3.2': + resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/checkbox@5.1.0': + resolution: {integrity: sha512-/HjF1LN0a1h4/OFsbGKHNDtWICFU/dqXCdym719HFTyJo9IG7Otr+ziGWc9S0iQuohRZllh+WprSgd5UW5Fw0g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@6.0.8': + resolution: {integrity: sha512-Di6dgmiZ9xCSUxWUReWTqDtbhXCuG2MQm2xmgSAIruzQzBqNf49b8E07/vbCYY506kDe8BiwJbegXweG8M1klw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.1.5': + resolution: {integrity: sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.23': + resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@5.0.8': + resolution: {integrity: sha512-sLcpbb9B3XqUEGrj1N66KwhDhEckzZ4nI/W6SvLXyBX8Wic3LDLENlWRvkOGpCPoserabe+MxQkpiMoI8irvyA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.23': + resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@5.0.8': + resolution: {integrity: sha512-QieW3F1prNw3j+hxO7/NKkG1pk3oz7pOB6+5Upwu3OIwADfPX0oZVppsqlL+Vl/uBHHDSOBY0BirLctLnXwGGg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@2.0.3': + resolution: {integrity: sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} + engines: {node: '>=18'} + + '@inquirer/figures@2.0.3': + resolution: {integrity: sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/input@4.3.1': + resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/input@5.0.8': + resolution: {integrity: sha512-p0IJslw0AmedLEkOU+yrEX3Aj2RTpQq7ZOf8nc1DIhjzaxRWrrgeuE5Kyh39fVRgtcACaMXx/9WNo8+GjgBOfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.23': + resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@4.0.8': + resolution: {integrity: sha512-uGLiQah9A0F9UIvJBX52m0CnqtLaym0WpT9V4YZrjZ+YRDKZdwwoEPz06N6w8ChE2lrnsdyhY9sL+Y690Kh9gQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.23': + resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@5.0.8': + resolution: {integrity: sha512-zt1sF4lYLdvPqvmvHdmjOzuUUjuCQ897pdUCO8RbXMUDKXJTTyOQgtn23le+jwcb+MpHl3VAFvzIdxRAf6aPlA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.3.1': + resolution: {integrity: sha512-r1CiKuDV86BDpvj9DRFR+V+nIjsVBOsa2++dqdPqLYAef8kgHYvmQ8ySdP/ZeAIOWa27YGJZRkENdP3dK0H3gg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@8.3.0': + resolution: {integrity: sha512-JAj66kjdH/F1+B7LCigjARbwstt3SNUOSzMdjpsvwJmzunK88gJeXmcm95L9nw1KynvFVuY4SzXh/3Y0lvtgSg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.11': + resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@5.2.4': + resolution: {integrity: sha512-fTuJ5Cq9W286isLxwj6GGyfTjx1Zdk4qppVEPexFuA6yioCCXS4V1zfKroQqw7QdbDPN73xs2DiIAlo55+kBqg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.2.2': + resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@4.1.4': + resolution: {integrity: sha512-9yPTxq7LPmYjrGn3DRuaPuPbmC6u3fiWcsE9ggfLcdgO/ICHYgxq7mEy1yJ39brVvgXhtOtvDVjDh9slJxE4LQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.4.2': + resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@5.1.0': + resolution: {integrity: sha512-OyYbKnchS1u+zRe14LpYrN8S0wH1vD0p2yKISvSsJdH2TpI87fh4eZdWnpdbrGauCRWDph3NwxRmM4Pcm/hx1Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.3': + resolution: {integrity: sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.1': + resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@moonwall/cli@5.18.3': + resolution: {integrity: sha512-bAVmZA3J7i6tp5AQfeStO8K0gh9fOhr3pQIxx95P8LW2g2pr2WNwKGfwa7oV3UZvhSnqX+dLiuzqj8uAslrTIw==} + engines: {node: '>=20', pnpm: '>=7'} + hasBin: true + peerDependencies: + '@polkadot/api': ^16.1.0 + '@polkadot/api-derive': ^16.1.0 + '@polkadot/keyring': ^13.0.0 + '@polkadot/rpc-provider': ^16.1.0 + '@polkadot/types': ^16.1.0 + '@polkadot/types-codec': ^16.1.0 + '@polkadot/util': ^13.0.0 + '@polkadot/util-crypto': ^13.0.0 + + '@moonwall/types@5.18.3': + resolution: {integrity: sha512-sQ8FwTsDPtaGb1jAEda0yVoiXUbHT/Ccsn6q/2fol7aPO/5KiJf/0Xoo321EJNB1vyfrJ2qj8+Nvt/xPB8xt6Q==} + engines: {node: '>=20'} + peerDependencies: + '@polkadot/api': ^16.1.0 + '@polkadot/api-base': ^16.1.0 + '@polkadot/keyring': ^13.0.0 + '@polkadot/types': ^16.1.0 + '@polkadot/util': ^13.0.0 + '@polkadot/util-crypto': ^13.0.0 + + '@moonwall/util@5.18.3': + resolution: {integrity: sha512-byhaoJNar50qmVWb1BtTlXAI5eMqpZ7zTzc/yO/pC5GI0O2wyNEgQNUnXZPA2ctw8qzN+NYBxzAbh/lkep5EGg==} + engines: {node: '>=20', pnpm: '>=7'} + peerDependencies: + '@polkadot/api': ^16.1.0 + '@polkadot/api-derive': ^16.1.0 + '@polkadot/keyring': ^13.0.0 + '@polkadot/rpc-provider': ^16.1.0 + '@polkadot/types': ^16.1.0 + '@polkadot/types-codec': ^16.1.0 + '@polkadot/util': ^13.0.0 + '@polkadot/util-crypto': ^13.0.0 + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@noble/ciphers@1.2.1': + resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/ciphers@2.1.1': + resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} + engines: {node: '>= 20.19.0'} + + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/curves@1.4.2': + resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} + + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/ed25519@1.7.5': + resolution: {integrity: sha512-xuS0nwRMQBvSxDa7UxMb61xTiH3MxTgUfhyPUALVIe0FlOAz4sjELwyDRyUvqeEYfRSG9qNjFIycqLZppg4RSA==} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} + + '@noble/secp256k1@1.7.2': + resolution: {integrity: sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ==} + + '@npmcli/fs@1.1.1': + resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} + + '@npmcli/move-file@1.1.2': + resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} + engines: {node: '>=10'} + deprecated: This functionality has been moved to @npmcli/fs + + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} + + '@octokit/core@7.0.6': + resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.3': + resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} + engines: {node: '>= 20'} + + '@octokit/graphql@9.0.3': + resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} + engines: {node: '>= 20'} + + '@octokit/openapi-types@27.0.0': + resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} + + '@octokit/plugin-paginate-rest@14.0.0': + resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-request-log@6.0.0': + resolution: {integrity: sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@17.0.0': + resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.8': + resolution: {integrity: sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==} + engines: {node: '>= 20'} + + '@octokit/rest@22.0.1': + resolution: {integrity: sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==} + engines: {node: '>= 20'} + + '@octokit/types@16.0.0': + resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@3.0.2': + resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==} + engines: {node: '>=12'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@polkadot-api/cli@0.15.2': + resolution: {integrity: sha512-eC/wUxjaN8miAmwSwJ/XIZ1zG+4leB2fs6h0fcZrbVI9SJXwuoWGTCMtErq+fbgRlDoK3cxEUO16JBKhLkCWXw==} + hasBin: true + + '@polkadot-api/codegen@0.19.1': + resolution: {integrity: sha512-129a0vHChzKuvQDELMYPpmqZtA5VFlJ7vo5HZh47bo67qYi1veRgDrNQVGM8yaHzi7Coo481b/SDruZbbbgd3Q==} + + '@polkadot-api/descriptors@file:.papi/descriptors': + resolution: {directory: .papi/descriptors, type: directory} + peerDependencies: + polkadot-api: '>=1.11.2' + + '@polkadot-api/ink-contracts@0.4.0': + resolution: {integrity: sha512-e2u5KhuYoiM+PyHsvjkI0O1nmFuC0rLH64uBerMqwK7hWENdM/ej9OqKawIzp6NQuYSHF5P4U8NBT0mjP9Y1yQ==} + + '@polkadot-api/json-rpc-provider-proxy@0.1.0': + resolution: {integrity: sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==} + + '@polkadot-api/json-rpc-provider-proxy@0.2.4': + resolution: {integrity: sha512-nuGoY9QpBAiRU7xmXN3nugFvPcnSu3IxTLm1OWcNTGlZ1LW5bvdQHz3JLk56+Jlyb3GJ971hqdg2DJsMXkKCOg==} + + '@polkadot-api/json-rpc-provider@0.0.1': + resolution: {integrity: sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==} + + '@polkadot-api/json-rpc-provider@0.0.4': + resolution: {integrity: sha512-9cDijLIxzHOBuq6yHqpqjJ9jBmXrctjc1OFqU+tQrS96adQze3mTIH6DTgfb/0LMrqxzxffz1HQGrIlEH00WrA==} + + '@polkadot-api/known-chains@0.9.11': + resolution: {integrity: sha512-ZbKXjPNI56DieJrM3DwuzNkjgLIGLjmXt5280cYJksGfatJkS/fZXIsAz0gBvs3UDeghd4co5a/OEEPiI5X8YQ==} + + '@polkadot-api/legacy-provider@0.3.2': + resolution: {integrity: sha512-/aM4jKNED5ONhOg1WnUzfSM9cJ17FHpZvASWLUGNbC2Y6CZKmLQ9UFm9fZnIbpMmC01Mz3L5orE+IlCo6g54Ag==} + peerDependencies: + rxjs: '>=7.8.0' + + '@polkadot-api/logs-provider@0.0.6': + resolution: {integrity: sha512-4WgHlvy+xee1ADaaVf6+MlK/+jGMtsMgAzvbQOJZnP4PfQuagoTqaeayk8HYKxXGphogLlPbD06tANxcb+nvAg==} + + '@polkadot-api/merkleize-metadata@1.1.25': + resolution: {integrity: sha512-deNOiMY/XvyN47/N3C+GrkM0a1i7xcy4I3F3H9wW1XtyxffAmNpoj58L7Zr2RtXYhfekmhdUZlzdD1+DOYeqvg==} + + '@polkadot-api/merkleize-metadata@1.1.29': + resolution: {integrity: sha512-z8ivYDdr4xlh50MQ7hLaSVw4VM6EV7gGgd+v/ej09nue0W08NG77zf7pXWeRKgOXe3+hPOSQQRSZT2OlIYRfqA==} + + '@polkadot-api/metadata-builders@0.13.5': + resolution: {integrity: sha512-3XqLKVv3eGDOUHEeC1KkBCeb/IjnfzdGNxydXJtonr+sbu6Ds7om5sSjqqWASf1bRSO0aHzVO3upPANveCcysg==} + + '@polkadot-api/metadata-builders@0.13.9': + resolution: {integrity: sha512-V2GljT6StuK40pfmO5l53CvgFNgy60Trrv20mOZDCsFU9J82F+a1HYAABDYlRgoZ9d0IDwc+u+vI+RHUJoR4xw==} + + '@polkadot-api/metadata-builders@0.3.2': + resolution: {integrity: sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==} + + '@polkadot-api/metadata-compatibility@0.3.6': + resolution: {integrity: sha512-rt6LTWph3L5sr7u940Ipvw2hao5to6T5BlbpRDkXHru+Xkl46tipTtrEjghtqkLBmOdVR6yiAVelOLWsiqPXnQ==} + + '@polkadot-api/observable-client@0.15.1': + resolution: {integrity: sha512-iR0ALA2C1aMzXqxqZqksLuScaImXbSWyaVs9Ym9Jz9SCeh2FSP6yK43BLW+RZOfcS84POxuGAktTXFssYM6fkg==} + peerDependencies: + rxjs: '>=7.8.0' + + '@polkadot-api/observable-client@0.3.2': + resolution: {integrity: sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==} + peerDependencies: + '@polkadot-api/substrate-client': 0.1.4 + rxjs: '>=7.8.0' + + '@polkadot-api/pjs-signer@0.6.15': + resolution: {integrity: sha512-JsrsuV5aa8Ghnkle+ZiR15xB/xqW9PFNsP3jFsG/n0DlfbKI+mSfrBZ6v3gkpccQIHtRnOA4yB1qRijjIEp2WQ==} + + '@polkadot-api/polkadot-sdk-compat@2.3.3': + resolution: {integrity: sha512-p30po+iv4trniSJ7UZiIt/rFInvtA9Tzg65EzuRkCaQAnh54a3MPp9w/q+x+SNLEcfzVLvf8LyPnMPOIpKuj5w==} + + '@polkadot-api/polkadot-signer@0.1.6': + resolution: {integrity: sha512-X7ghAa4r7doETtjAPTb50IpfGtrBmy3BJM5WCfNKa1saK04VFY9w+vDn+hwEcM4p0PcDHt66Ts74hzvHq54d9A==} + + '@polkadot-api/raw-client@0.1.1': + resolution: {integrity: sha512-HxalpNEo8JCYXfxKM5p3TrK8sEasTGMkGjBNLzD4TLye9IK2smdb5oTvp2yfkU1iuVBdmjr69uif4NaukOYo2g==} + + '@polkadot-api/signer@0.2.9': + resolution: {integrity: sha512-2KntINp+HlrnsRquQiDaoGU400Guh/CbbTdkq23Y14qLjjKUQbGGs7RLHuVCxagxKw4UFlQpO36Ku0lHj3rq5Q==} + + '@polkadot-api/signers-common@0.1.16': + resolution: {integrity: sha512-/+EqdH+aIWCzV0TWiHG7vuklxyHQ2lOkQAL6H/sVe2zsHpUjGfFzO/VAzVLH2acYHbpslKFLrA/y8RAIzYHhkg==} + + '@polkadot-api/sm-provider@0.1.11': + resolution: {integrity: sha512-XSli7BF3Xpyh0sdu1MNRJ1qyT3Werd5Z+tQa4iXR+nzo5Kpvg67paG9A8z1K7vNF83pRw4rvnXE9G5HbC+tPvA==} + peerDependencies: + '@polkadot-api/smoldot': '>=0.3' + + '@polkadot-api/smoldot@0.3.14': + resolution: {integrity: sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ==} + + '@polkadot-api/substrate-bindings@0.16.3': + resolution: {integrity: sha512-KN/nghI3SM0t7WsULwLRB3s4DnWogGCi5TuvXB0yPkkiB5GJugMPuHTTUxDkWmjZ0vLUFlmkaZ/sfFf0tvo8xQ==} + + '@polkadot-api/substrate-bindings@0.17.0': + resolution: {integrity: sha512-YdbkvG/27N5A94AiKE4soVjDy0Nw74Nn+KD29mUnFmIZvL3fsN/DTYkxvMDVsOuanFXyAIXmzDMoi7iky0fyIw==} + + '@polkadot-api/substrate-bindings@0.6.0': + resolution: {integrity: sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==} + + '@polkadot-api/substrate-client@0.1.4': + resolution: {integrity: sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==} + + '@polkadot-api/substrate-client@0.4.7': + resolution: {integrity: sha512-Mmx9VKincVqfVQmq89gzDk4DN3uKwf8CxoqYvq+EiPUZ1QmMUc7X4QMwG1MXIlYdnm5LSXzn+2Jn8ik8xMgL+w==} + + '@polkadot-api/utils@0.1.0': + resolution: {integrity: sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==} + + '@polkadot-api/utils@0.2.0': + resolution: {integrity: sha512-nY3i5fQJoAxU4n3bD7Fs208/KR2J95SGfVc58kDjbRYN5a84kWaGEqzjBNtP9oqht49POM8Bm9mbIrkvC1Bzuw==} + + '@polkadot-api/wasm-executor@0.2.3': + resolution: {integrity: sha512-B2h1o+Qlo9idpASaHvMSoViB2I5ko5OAfwfhYF8LQDkTADK0B+SeStzNj1Qn+FG34wqTuv7HzBCdjaUgzYINJQ==} + + '@polkadot-api/ws-provider@0.6.2': + resolution: {integrity: sha512-YCllTdysvh30t4YWJubS1G8ULCZTOXGC+x8evbuFUNM1d70gpD98+zi4ba4lZGd1IlZ8v0zJuvC7G+/9Jcrm4w==} + + '@polkadot/api-augment@14.3.1': + resolution: {integrity: sha512-PE6DW+8kRhbnGKn7qCF7yM6eEt/kqrY8bh1i0RZcPY9QgwXW4bZZrtMK4WssX6Z70NTEoOW6xHYIjc7gFZuz8g==} + engines: {node: '>=18'} + + '@polkadot/api-augment@16.5.4': + resolution: {integrity: sha512-9FTohz13ih458V2JBFjRACKHPqfM6j4bmmTbcSaE7hXcIOYzm4ABFo7xq5osLyvItganjsICErL2vRn2zULycw==} + engines: {node: '>=18'} + + '@polkadot/api-base@14.3.1': + resolution: {integrity: sha512-GZT6rTpT3HYZ/C3rLPjoX3rX3DOxNG/zgts+jKjNrCumAeZkVq5JErKIX8/3f2TVaE2Kbqniy3d1TH/AL4HBPA==} + engines: {node: '>=18'} + + '@polkadot/api-base@16.5.4': + resolution: {integrity: sha512-V69v3ieg5+91yRUCG1vFRSLr7V7MvHPvo/QrzleIUu8tPXWldJ0kyXbWKHVNZEpVBA9LpjGvII+MHUW7EaKMNg==} + engines: {node: '>=18'} + + '@polkadot/api-derive@14.3.1': + resolution: {integrity: sha512-PhqUEJCY54vXtIaoYqGUtJY06wHd/K0cBmBz9yCLxp8UZkLoGWhfJRTruI25Jnucf9awS5cZKYqbsoDrL09Oqg==} + engines: {node: '>=18'} + + '@polkadot/api-derive@16.5.4': + resolution: {integrity: sha512-0JP2a6CaqTviacHsmnUKF4VLRsKdYOzQCqdL9JpwY/QBz/ZLqIKKPiSRg285EVLf8n/hWdTfxbWqQCsRa5NL+Q==} + engines: {node: '>=18'} + + '@polkadot/api@14.3.1': + resolution: {integrity: sha512-ZBKSXEVJa1S1bnmpnA7KT/fX3sJDIJOdVD9Hp3X+G73yvXzuK5k1Mn5z9bD/AcMs/HAGcbuYU+b9+b9IByH9YQ==} + engines: {node: '>=18'} + + '@polkadot/api@16.5.4': + resolution: {integrity: sha512-mX1fwtXCBAHXEyZLSnSrMDGP+jfU2rr7GfDVQBz0cBY1nmY8N34RqPWGrZWj8o4DxVu1DQ91sGncOmlBwEl0Qg==} + engines: {node: '>=18'} + + '@polkadot/keyring@13.5.9': + resolution: {integrity: sha512-bMCpHDN7U8ytxawjBZ89/he5s3AmEZuOdkM/ABcorh/flXNPfyghjFK27Gy4OKoFxX52yJ2sTHR4NxM87GuFXQ==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9 + + '@polkadot/keyring@14.0.1': + resolution: {integrity: sha512-kHydQPCeTvJrMC9VQO8LPhAhTUxzxfNF1HEknhZDBPPsxP/XpkYsEy/Ln1QzJmQqD5VsgwzLDE6cExbJ2CT9CA==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1 + + '@polkadot/networks@13.5.9': + resolution: {integrity: sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA==} + engines: {node: '>=18'} + + '@polkadot/networks@14.0.1': + resolution: {integrity: sha512-wGlBtXDkusRAj4P7uxfPz80gLO1+j99MLBaQi3bEym2xrFrFhgIWVHOZlBit/1PfaBjhX2Z8XjRxaM2w1p7w2w==} + engines: {node: '>=18'} + + '@polkadot/rpc-augment@14.3.1': + resolution: {integrity: sha512-Z8Hp8fFHwFCiTX0bBCDqCZ4U26wLIJl1NRSjJTsAr+SS68pYZBDGCwhKztpKGqndk1W1akRUaxrkGqYdIFmspQ==} + engines: {node: '>=18'} + + '@polkadot/rpc-augment@16.5.4': + resolution: {integrity: sha512-j9v3Ttqv/EYGezHtVksGJAFZhE/4F7LUWooOazh/53ATowMby3lZUdwInrK6bpYmG2whmYMw/Fo283fwDroBtQ==} + engines: {node: '>=18'} + + '@polkadot/rpc-core@14.3.1': + resolution: {integrity: sha512-FV2NPhFwFxmX8LqibDcGc6IKTBqmvwr7xwF2OA60Br4cX+AQzMSVpFlfQcETll+0M+LnRhqGKGkP0EQWXaSowA==} + engines: {node: '>=18'} + + '@polkadot/rpc-core@16.5.4': + resolution: {integrity: sha512-92LOSTWujPjtmKOPvfCPs8rAaPFU+18wTtkIzwPwKxvxkN/SWsYSGIxmsoags9ramyHB6jp7Lr59TEuGMxIZzQ==} + engines: {node: '>=18'} + + '@polkadot/rpc-provider@14.3.1': + resolution: {integrity: sha512-NF/Z/7lzT+jp5LZzC49g+YIjRzXVI0hFag3+B+4zh6E/kKADdF59EHj2Im4LDhRGOnEO9AE4H6/UjNEbZ94JtA==} + engines: {node: '>=18'} + + '@polkadot/rpc-provider@16.5.4': + resolution: {integrity: sha512-mNAIBRA3jMvpnHsuqAX4InHSIqBdgxFD6ayVUFFAzOX8Fh6Xpd4RdI1dqr6a1pCzjnPSby4nbg+VuadWwauVtg==} + engines: {node: '>=18'} + + '@polkadot/types-augment@14.3.1': + resolution: {integrity: sha512-SC4M6TBlgCglNz+gRbvfoVRDz0Vyeev6v0HeAdw0H6ayEW4BXUdo5bFr0092bdS5uTrEPgiSyUry5TJs2KoXig==} + engines: {node: '>=18'} + + '@polkadot/types-augment@16.5.4': + resolution: {integrity: sha512-AGjXR+Q9O9UtVkGw/HuOXlbRqVpvG6H8nr+taXP71wuC6RD9gznFBFBqoNkfWHD2w89esNVQLTvXHVxlLpTXqA==} + engines: {node: '>=18'} + + '@polkadot/types-codec@14.3.1': + resolution: {integrity: sha512-3y3RBGd+8ebscGbNUOjqUjnRE7hgicgid5LtofHK3O1EDcJQJnYBDkJ7fOAi96CDgHsg+f2FWWkBWEPgpOQoMQ==} + engines: {node: '>=18'} + + '@polkadot/types-codec@16.5.4': + resolution: {integrity: sha512-OQtT1pmJu2F3/+Vh1OiXifKoeRy+CU1+Lu7dgTcdO705dnxU4447Zup5JVCJDnxBmMITts/38vbFN2pD225AnA==} + engines: {node: '>=18'} + + '@polkadot/types-create@14.3.1': + resolution: {integrity: sha512-F4EBvF3Zvym0xrkAA5Yz01IAVMepMV3w2Dwd0C9IygEAQ5sYLLPHmf72/aXn+Ag+bSyT2wlJHpDc+nEBXNQ3Gw==} + engines: {node: '>=18'} + + '@polkadot/types-create@16.5.4': + resolution: {integrity: sha512-URQnvr/sgvgIRSxIW3lmml6HMSTRRj2hTZIm6nhMTlYSVT4rLWx0ZbYUAjoPBbaJ+BmoqZ6Bbs+tA+5cQViv5Q==} + engines: {node: '>=18'} + + '@polkadot/types-known@14.3.1': + resolution: {integrity: sha512-58b3Yc7+sxwNjs8axmrA9OCgnxmEKIq7XCH2VxSgLqTeqbohVtxwUSCW/l8NPrq1nxzj4J2sopu0PPg8/++q4g==} + engines: {node: '>=18'} + + '@polkadot/types-known@16.5.4': + resolution: {integrity: sha512-Dd59y4e3AFCrH9xiqMU4xlG5+Zy0OTy7GQvqJVYXZFyAH+4HYDlxXjJGcSidGAmJcclSYfS3wyEkfw+j1EOVEw==} + engines: {node: '>=18'} + + '@polkadot/types-support@14.3.1': + resolution: {integrity: sha512-MfVe4iIOJIfBr+gj8Lu8gwIvhnO6gDbG5LeaKAjY6vS6Oh0y5Ztr8NdMIl8ccSpoyt3LqIXjfApeGzHiLzr6bw==} + engines: {node: '>=18'} + + '@polkadot/types-support@16.5.4': + resolution: {integrity: sha512-Ra6keCaO73ibxN6MzA56jFq9EReje7jjE4JQfzV5IpyDZdXcmPyJiEfa2Yps/YSP13Gc2e38t9FFyVau0V+SFQ==} + engines: {node: '>=18'} + + '@polkadot/types@14.3.1': + resolution: {integrity: sha512-O748XgCLDQYxS5nQ6TJSqW88oC4QNIoNVlWZC2Qq4SmEXuSzaNHQwSVtdyPRJCCc4Oi1DCQvGui4O+EukUl7HA==} + engines: {node: '>=18'} + + '@polkadot/types@16.5.4': + resolution: {integrity: sha512-8Oo1QWaL0DkIc/n2wKBIozPWug/0b2dPVhL+XrXHxJX7rIqS0x8sXDRbM9r166sI0nTqJiUho7pRIkt2PR/DMQ==} + engines: {node: '>=18'} + + '@polkadot/util-crypto@13.5.9': + resolution: {integrity: sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 13.5.9 + + '@polkadot/util-crypto@14.0.1': + resolution: {integrity: sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 14.0.1 + + '@polkadot/util@13.5.9': + resolution: {integrity: sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw==} + engines: {node: '>=18'} + + '@polkadot/util@14.0.1': + resolution: {integrity: sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg==} + engines: {node: '>=18'} + + '@polkadot/wasm-bridge@7.5.4': + resolution: {integrity: sha512-6xaJVvoZbnbgpQYXNw9OHVNWjXmtcoPcWh7hlwx3NpfiLkkjljj99YS+XGZQlq7ks2fVCg7FbfknkNb8PldDaA==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': '*' + '@polkadot/x-randomvalues': '*' + + '@polkadot/wasm-crypto-asmjs@7.5.4': + resolution: {integrity: sha512-ZYwxQHAJ8pPt6kYk9XFmyuFuSS+yirJLonvP+DYbxOrARRUHfN4nzp4zcZNXUuaFhpbDobDSFn6gYzye6BUotA==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': '*' + + '@polkadot/wasm-crypto-init@7.5.4': + resolution: {integrity: sha512-U6s4Eo2rHs2n1iR01vTz/sOQ7eOnRPjaCsGWhPV+ZC/20hkVzwPAhiizu/IqMEol4tO2yiSheD4D6bn0KxUJhg==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': '*' + '@polkadot/x-randomvalues': '*' + + '@polkadot/wasm-crypto-wasm@7.5.4': + resolution: {integrity: sha512-PsHgLsVTu43eprwSvUGnxybtOEuHPES6AbApcs7y5ZbM2PiDMzYbAjNul098xJK/CPtrxZ0ePDFnaQBmIJyTFw==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': '*' + + '@polkadot/wasm-crypto@7.5.4': + resolution: {integrity: sha512-1seyClxa7Jd7kQjfnCzTTTfYhTa/KUTDUaD3DMHBk5Q4ZUN1D1unJgX+v1aUeXSPxmzocdZETPJJRZjhVOqg9g==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': '*' + '@polkadot/x-randomvalues': '*' + + '@polkadot/wasm-util@7.5.4': + resolution: {integrity: sha512-hqPpfhCpRAqCIn/CYbBluhh0TXmwkJnDRjxrU9Bnqtw9nMNa97D8JuOjdd2pi0rxm+eeLQ/f1rQMp71RMM9t4w==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': '*' + + '@polkadot/x-bigint@13.5.9': + resolution: {integrity: sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g==} + engines: {node: '>=18'} + + '@polkadot/x-bigint@14.0.1': + resolution: {integrity: sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ==} + engines: {node: '>=18'} + + '@polkadot/x-fetch@13.5.9': + resolution: {integrity: sha512-urwXQZtT4yYROiRdJS6zHu18J/jCoAGpbgPIAjwdqjT11t9XIq4SjuPMxD19xBRhbYe9ocWV8i1KHuoMbZgKbA==} + engines: {node: '>=18'} + + '@polkadot/x-fetch@14.0.1': + resolution: {integrity: sha512-yFsnO0xfkp3bIcvH70ZvmeUINYH1YnjOIS1B430f3w6axkqKhAOWCgzzKGMSRgn4dtm3YgwMBKPQ4nyfIsGOJQ==} + engines: {node: '>=18'} + + '@polkadot/x-global@13.5.9': + resolution: {integrity: sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA==} + engines: {node: '>=18'} + + '@polkadot/x-global@14.0.1': + resolution: {integrity: sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA==} + engines: {node: '>=18'} + + '@polkadot/x-randomvalues@13.5.9': + resolution: {integrity: sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 13.5.9 + '@polkadot/wasm-util': '*' + + '@polkadot/x-randomvalues@14.0.1': + resolution: {integrity: sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg==} + engines: {node: '>=18'} + peerDependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-util': '*' + + '@polkadot/x-textdecoder@13.5.9': + resolution: {integrity: sha512-W2HhVNUbC/tuFdzNMbnXAWsIHSg9SC9QWDNmFD3nXdSzlXNgL8NmuiwN2fkYvCQBtp/XSoy0gDLx0C+Fo19cfw==} + engines: {node: '>=18'} + + '@polkadot/x-textdecoder@14.0.1': + resolution: {integrity: sha512-CcWiPCuPVJsNk4Vq43lgFHqLRBQHb4r9RD7ZIYgmwoebES8TNm4g2ew9ToCzakFKSpzKu6I07Ne9wv/dt5zLuw==} + engines: {node: '>=18'} + + '@polkadot/x-textencoder@13.5.9': + resolution: {integrity: sha512-SG0MHnLUgn1ZxFdm0KzMdTHJ47SfqFhdIPMcGA0Mg/jt2rwrfrP3jtEIJMsHfQpHvfsNPfv55XOMmoPWuQnP/Q==} + engines: {node: '>=18'} + + '@polkadot/x-textencoder@14.0.1': + resolution: {integrity: sha512-VY51SpQmF1ccmAGLfxhYnAe95Spfz049WZ/+kK4NfsGF9WejxVdU53Im5C80l45r8qHuYQsCWU3+t0FNunh2Kg==} + engines: {node: '>=18'} + + '@polkadot/x-ws@13.5.9': + resolution: {integrity: sha512-NKVgvACTIvKT8CjaQu9d0dERkZsWIZngX/4NVSjc01WHmln4F4y/zyBdYn/Z2V0Zw28cISx+lB4qxRmqTe7gbg==} + engines: {node: '>=18'} + + '@polkadot/x-ws@14.0.1': + resolution: {integrity: sha512-Q18hoSuOl7F4aENNGNt9XYxkrjwZlC6xye9OQrPDeHam1SrvflGv9mSZHyo+mwJs0z1PCz2STpPEN9PKfZvHng==} + engines: {node: '>=18'} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + cpu: [x64] + os: [win32] + + '@rx-state/core@0.1.4': + resolution: {integrity: sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ==} + peerDependencies: + rxjs: '>=7' + + '@scure/base@1.1.9': + resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/base@2.0.0': + resolution: {integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==} + + '@scure/bip32@1.4.0': + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + + '@scure/bip32@1.6.2': + resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.3.0': + resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + + '@scure/bip39@1.5.4': + resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + + '@scure/sr25519@0.2.0': + resolution: {integrity: sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg==} + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@sqltools/formatter@1.2.5': + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@substrate/connect-extension-protocol@2.2.2': + resolution: {integrity: sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==} + + '@substrate/connect-known-chains@1.10.3': + resolution: {integrity: sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==} + + '@substrate/connect@0.8.11': + resolution: {integrity: sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==} + deprecated: versions below 1.x are no longer maintained + + '@substrate/light-client-extension-helpers@1.0.0': + resolution: {integrity: sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==} + peerDependencies: + smoldot: 2.x + + '@substrate/ss58-registry@1.51.0': + resolution: {integrity: sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==} + + '@tootallnate/once@1.1.2': + resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} + engines: {node: '>= 6'} + + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/bn.js@5.2.0': + resolution: {integrity: sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-bigint@1.0.4': + resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} + + '@types/long@4.0.2': + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.7.5': + resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + + '@types/node@24.12.0': + resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} + + '@types/node@25.3.5': + resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==} + + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + + '@types/ps-node@0.1.3': + resolution: {integrity: sha512-9lJ7dWmWsdZnh/QMwX11+T+Se8Q1dk4LJqUwAEMsl7riiYmvmSl/Hfr7FlfJawD2+eb12A1hYAmu1J89k2PVdQ==} + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + + '@types/tmp@0.2.6': + resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/ws@8.5.3': + resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.1.3': + resolution: {integrity: sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==} + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/ui@3.1.3': + resolution: {integrity: sha512-IipSzX+8DptUdXN/GWq3hq5z18MwnpphYdOMm0WndkRGYELzfq7NDP8dMpZT7JGW1uXFrIGxOW2D0Xi++ulByg==} + peerDependencies: + vitest: 3.1.3 + + '@vitest/ui@3.2.4': + resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==} + peerDependencies: + vitest: 3.2.4 + + '@vitest/utils@3.1.3': + resolution: {integrity: sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@zombienet/orchestrator@0.0.105': + resolution: {integrity: sha512-vw+Pt1N9oChdA+2WHgwygG4wpXaKnPJPIRbm3OWbhscCwHbWlmcVVZhZN3khC4+WMo+kvFt3XhzV6hZrZI5Bug==} + engines: {node: '>=18'} + + '@zombienet/orchestrator@0.0.113': + resolution: {integrity: sha512-ifLaVm+viGBTuqtgtJohj1Bn/4zcdxasZs/jmDxWEbPPtCsBc/WW0Jdtn+nBmwmVz90EYwvGfkGxHalsld1RaQ==} + engines: {node: '>=18'} + + '@zombienet/utils@0.0.28': + resolution: {integrity: sha512-0AvQgSmz8UeVoSPXQRjibk4picMdqfo4q86wYbuJzg951Np82nodzEMGTssBC6JFm/8Bb/M1N6bCh5Wi2d0fxg==} + engines: {node: '>=18'} + + '@zombienet/utils@0.0.30': + resolution: {integrity: sha512-ygm438M/73AaWZxZtTnEjv2uDMKgqTd9XDSLzNNExKh0nALklfbUPuMv+P3GrttSH1bEQCiPDfPmmyZI4YpypA==} + engines: {node: '>=18'} + + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + + a-sync-waterfall@1.0.1: + resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abitype@0.7.1: + resolution: {integrity: sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==} + peerDependencies: + typescript: '>=4.9.4' + zod: ^3 >=3.19.1 + peerDependenciesMeta: + zod: + optional: true + + abitype@1.1.0: + resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + + aproba@2.1.0: + resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} + + are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + arkregex@0.0.4: + resolution: {integrity: sha512-biS/FkvSwQq59TZ453piUp8bxMui11pgOMV9WHAnli1F8o0ayNCZzUwQadL/bGIUic5TkS/QlPcyMuI8ZIwedQ==} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + auto-bind@5.0.1: + resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios@1.13.6: + resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bn.js@5.2.3: + resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} + + boolean@3.2.0: + resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + buildcheck@0.0.7: + resolution: {integrity: sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==} + engines: {node: '>=10.0.0'} + + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@15.3.0: + resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} + engines: {node: '>= 10'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + cfonts@3.3.1: + resolution: {integrity: sha512-ZGEmN3W9mViWEDjsuPo4nK4h39sfh6YtoneFYp9WLPI/rw8BaSSrfQC6jkrGW3JMvV3ZnExJB/AEqXc/nHYxkw==} + engines: {node: '>=10'} + hasBin: true + + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + class-is@1.1.0: + resolution: {integrity: sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + clear@0.1.0: + resolution: {integrity: sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw==} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-progress@3.12.0: + resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==} + engines: {node: '>=4'} + + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + + code-excerpt@4.0.0: + resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comlink@4.4.2: + resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==} + + command-exists@1.2.9: + resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + connected-domain@1.0.0: + resolution: {integrity: sha512-lHlohUiJxlpunvDag2Y0pO20bnvarMjnrdciZeuJUqRwrf/5JHNhdpiPIr5GQ8IkqrFj5TDMQwcCjblGo1oeuA==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + convert-to-spaces@2.0.1: + resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deepmerge-ts@7.1.5: + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + engines: {node: '>=16.0.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + detect-indent@7.0.2: + resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} + engines: {node: '>=12.20'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + + diff@5.2.2: + resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} + engines: {node: '>=0.3.1'} + + docker-modem@5.0.6: + resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} + engines: {node: '>= 8.0'} + + dockerode@4.0.9: + resolution: {integrity: sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q==} + engines: {node: '>= 8.0'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + effect@3.19.19: + resolution: {integrity: sha512-Yc8U/SVXo2dHnaP7zNBlAo83h/nzSJpi7vph6Hzyl4ulgMBIgPmz3UzOjb9sBgpFE00gC0iETR244sfXDNLHRg==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + err-code@3.0.1: + resolution: {integrity: sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + + es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + ethereum-cryptography@2.2.1: + resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} + + ethereum-cryptography@3.1.0: + resolution: {integrity: sha512-ZqHd92eOIH9RExpBUOgzpAgflyFv9/+Ca39G8V+oCjJPGjJUihQcG/Gl67I/Xn2HGS87dgnrCG3kb1jNClLi6g==} + engines: {node: ^14.21.3 || >=16, npm: '>=9'} + + ethers@6.16.0: + resolution: {integrity: sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==} + engines: {node: '>=14.0.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + + fast-content-type-parse@3.0.0: + resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} + + fast-copy@4.0.2: + resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-my-way-ts@0.1.6: + resolution: {integrity: sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.4: + resolution: {integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} + engines: {node: '>=14.14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.promises.exists@1.1.4: + resolution: {integrity: sha512-lJzUGWbZn8vhGWBedA+RYjB/BeJ+3458ljUfmplqhIeb6ewzTFWNPCR1HCiYCkXV9zxcHz9zXkJzMsEgDLzh3Q==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.5.0: + resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + engines: {node: '>=18'} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + 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 + hasBin: true + + glob@7.2.3: + resolution: {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 + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + 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 + + global-agent@3.0.0: + resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} + engines: {node: '>=10.0'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-proxy-agent@4.0.1: + resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} + engines: {node: '>= 6'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + idb@8.0.3: + resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + + inflight@1.0.6: + resolution: {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. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ink@6.8.0: + resolution: {integrity: sha512-sbl1RdLOgkO9isK42WCZlJCFN9hb++sX9dsklOvfd1YQ3bQ2AiFu12Q6tFlr0HvEUvzraJntQCCpfEoUe9DSzA==} + engines: {node: '>=20'} + peerDependencies: + '@types/react': '>=19.0.0' + react: '>=19.0.0' + react-devtools-core: '>=6.1.2' + peerDependenciesMeta: + '@types/react': + optional: true + react-devtools-core: + optional: true + + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + + is-accessor-descriptor@1.0.1: + resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} + engines: {node: '>= 0.10'} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-data-descriptor@1.0.1: + resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} + engines: {node: '>= 0.4'} + + is-descriptor@1.0.3: + resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-in-ci@2.0.0: + resolution: {integrity: sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==} + engines: {node: '>=20'} + hasBin: true + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iso-random-stream@2.0.2: + resolution: {integrity: sha512-yJvs+Nnelic1L2vH2JzWvvPQFA4r7kSTnpST/+LkAQjSz0hos2oqLD+qIVi9Qk38Hoe7mNDt3j0S27R58MVjLQ==} + engines: {node: '>=10'} + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdom@23.2.0: + resolution: {integrity: sha512-L88oL7D/8ufIES+Zjz7v0aes+oBMh2Xnh3ygWvL0OaICOomKEPKuPnIfBJekiXr+BHbbMjrWn/xqrDQuxFTeyA==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json-with-bigint@3.5.7: + resolution: {integrity: sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsondiffpatch@0.5.0: + resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} + engines: {node: '>=8.17.0'} + hasBin: true + bundledDependencies: [] + + jsondiffpatch@0.7.3: + resolution: {integrity: sha512-zd4dqFiXSYyant2WgSXAZ9+yYqilNVvragVNkNRn2IFZKgjyULNrKRznqN4Zon0MkLueCg+3QaPVCnDAVP20OQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + kubernetes-types@1.30.0: + resolution: {integrity: sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==} + + libp2p-crypto@0.21.2: + resolution: {integrity: sha512-EXFrhSpiHtJ+/L8xXDvQNK5VjUMG51u878jzZcaT5XhuN/zFg6PWJFnl/qB2Y2j7eMWnvCRP7Kp+ua2H36cG4g==} + engines: {node: '>=12.0.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + + long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.2.6: + resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + engines: {node: 20 || >=22} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + make-fetch-happen@9.1.0: + resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} + engines: {node: '>= 10'} + + matcher@3.0.0: + resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} + engines: {node: '>=10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@1.4.1: + resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} + engines: {node: '>=8'} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlkem@2.7.0: + resolution: {integrity: sha512-I2bcB5d6jtkdan6MLGOxObpUbidqv0ej+PhbCGnXUqmcGYZ6X8F0qBpU6HE4mvYc81NSznBrVDp+Uc808Ba2RA==} + engines: {node: '>=16.0.0'} + + mlly@1.8.1: + resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} + + mocha@10.8.2: + resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} + engines: {node: '>= 14.0.0'} + hasBin: true + + mock-socket@9.3.1: + resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==} + engines: {node: '>= 8'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.8: + resolution: {integrity: sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==} + + multiformats@9.9.0: + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + + multipasta@0.2.7: + resolution: {integrity: sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==} + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nan@2.25.0: + resolution: {integrity: sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + napi-maybe-compressed-blob-darwin-arm64@0.0.11: + resolution: {integrity: sha512-hZ9ye4z8iMDVPEnx9A/Ag6k7xHX/BcK5Lntw/VANBUm9ioLSuRvHTALG4XaqVDGXo4U2NFDwSLRDyhFPYvqckQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + napi-maybe-compressed-blob-darwin-x64@0.0.11: + resolution: {integrity: sha512-TqWNP7Vehi73xLXyUGjdLppP0W6T0Ef2D/X9HmAZNwglt+MkTujX10CDODfbFWvGy+NkaC5XqnzxCn19wbZZcA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + napi-maybe-compressed-blob-linux-arm64-gnu@0.0.11: + resolution: {integrity: sha512-7D5w6MDZghcb3VtXRg2ShCEh9Z3zMeBVRG4xsMulEWT2j9/09Nopu+9KfI/2ngRvm78MniWSIlqds5PRAlCROA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + napi-maybe-compressed-blob-linux-x64-gnu@0.0.11: + resolution: {integrity: sha512-JKY8KcZpQtKiL1smMKfukcOmsDVeZaw9fKXKsWC+wySc2wsvH7V2wy8PffSQ0lWERkI7Yn3k7xPjB463m/VNtg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + napi-maybe-compressed-blob@0.0.11: + resolution: {integrity: sha512-1dj4ET34TfEes0+josVLvwpJe337Jk6txd3XUjVmVs3budSo2eEjvN6pX4myYE1pS4x/k2Av57n/ypRl2u++AQ==} + engines: {node: '>= 10'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + nock@13.5.6: + resolution: {integrity: sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==} + engines: {node: '>= 10.13'} + + node-abi@3.87.0: + resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==} + engines: {node: '>=10'} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-forge@1.3.3: + resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} + engines: {node: '>= 6.13.0'} + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-gyp@8.4.1: + resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} + engines: {node: '>= 10.12.0'} + hasBin: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + normalize-package-data@6.0.2: + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + nunjucks@3.2.4: + resolution: {integrity: sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==} + engines: {node: '>= 6.9.0'} + hasBin: true + peerDependencies: + chokidar: ^3.3.0 + peerDependenciesMeta: + chokidar: + optional: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + ora@9.3.0: + resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} + engines: {node: '>=20'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + ox@0.9.6: + resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + patch-console@2.0.0: + resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + peer-id@0.16.0: + resolution: {integrity: sha512-EmL7FurFUduU9m1PS9cfJ5TAuCvxKQ7DKpfx3Yj6IKWyBRtosriFuOag/l3ni/dtPgPLwiA4R9IvpL7hsDLJuQ==} + engines: {node: '>=15.0.0'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} + hasBin: true + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.3.1: + resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} + hasBin: true + + pino@9.14.0: + resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} + hasBin: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pnpm@10.32.1: + resolution: {integrity: sha512-pwaTjw6JrBRWtlY+q07fHR+vM2jRGR/FxZeQ6W3JGORFarLmfWE94QQ9LoyB+HMD5rQNT/7KnfFe8a1Wc0jyvg==} + engines: {node: '>=18.12'} + hasBin: true + + polkadot-api@1.19.2: + resolution: {integrity: sha512-ZaE5GMZ7pd2bw5dPLWWEB2WjzbhsPdnQjBuJ9HstEUkxcfaQkNIqRy33vGv1QmJ6xyp1aiffSTa9r1DGbjuixQ==} + hasBin: true + peerDependencies: + rxjs: '>=7.8.0' + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. + hasBin: true + + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + propagate@2.0.1: + resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==} + engines: {node: '>= 8'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + protobufjs@6.11.4: + resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} + hasBin: true + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + ps-node@0.1.6: + resolution: {integrity: sha512-w7QJhUTbu70hpDso0YXDRNKCPNuchV8UTUZsAv0m7Qj5g85oHOJfr9drA1EjvK4nQK/bG8P97W4L6PJ3IQLoOA==} + + psl@1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-reconciler@0.33.0: + resolution: {integrity: sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^19.2.0 + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + read-pkg@9.0.1: + resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} + engines: {node: '>=18'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rlp@3.0.0: + resolution: {integrity: sha512-PD6U2PGk6Vq2spfgiWZdomLvRGDreBLxi5jv5M8EpRo3pU6VEm31KO+HFxE18Q3vgqfDrQ9pZA3FP95rkijNKw==} + hasBin: true + + roarr@2.15.4: + resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} + engines: {node: '>=8.0'} + + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scale-ts@1.6.1: + resolution: {integrity: sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + secure-json-parse@4.1.0: + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + + semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + serialize-error@7.0.1: + resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} + engines: {node: '>=10'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + smoldot@2.0.26: + resolution: {integrity: sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==} + + smoldot@2.0.39: + resolution: {integrity: sha512-yFMSzI6nkqWFTNao99lBA/TguUFU+bR3A5UGTDd/QqqB12jqzvZnmW/No6l2rKmagt8Qx/KybMNowV/E28znhA==} + + socks-proxy-agent@6.2.1: + resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} + engines: {node: '>= 10'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + solc@0.8.21: + resolution: {integrity: sha512-N55ogy2dkTRwiONbj4e6wMZqUNaLZkiRcjGyeafjLYzo/tf/IvhHY5P5wpe+H3Fubh9idu071i8eOGO31s1ylg==} + engines: {node: '>=10.0.0'} + hasBin: true + + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + + sort-keys@5.1.0: + resolution: {integrity: sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==} + engines: {node: '>=12'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.23: + resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} + + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + sql-highlight@6.1.0: + resolution: {integrity: sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==} + engines: {node: '>=14'} + + sqlite3@5.1.7: + resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} + + ssh2@1.17.0: + resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} + engines: {node: '>=10.16.0'} + + ssri@8.0.1: + resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + engines: {node: '>= 8'} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stdin-discarder@0.3.1: + resolution: {integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==} + engines: {node: '>=18'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.0: + resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + engines: {node: '>=20'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + table-parser@0.1.3: + resolution: {integrity: sha512-LCYeuvqqoPII3lzzYaXKbC3Forb+d2u4bNwhk/9FlivuGRxPE28YEWAYcujeSlLLDlMfvy29+WPybFJZFiKMYg==} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + deprecated: Old versions of tar 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 + + terminal-size@4.0.1: + resolution: {integrity: sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==} + engines: {node: '>=18'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + thread-stream@4.0.0: + resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} + engines: {node: '>=20'} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + tmp-promise@3.0.3: + resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + + toml@https://codeload.github.com/pepoviola/toml-node/tar.gz/5e17114f1af5b5b70e4f2ec10cd007623c928988: + resolution: {tarball: https://codeload.github.com/pepoviola/toml-node/tar.gz/5e17114f1af5b5b70e4f2ec10cd007623c928988} + version: 3.0.0 + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsc-prog@2.3.0: + resolution: {integrity: sha512-ycET2d75EgcX7y8EmG4KiZkLAwUzbY4xRhA6NU0uVbHkY4ZjrAAuzTMxXI85kOwATqPnBI5C/7y7rlpY0xdqHA==} + engines: {node: '>=12'} + peerDependencies: + typescript: '>=4' + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + type-fest@0.13.1: + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-fest@5.4.4: + resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} + engines: {node: '>=20'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typeorm@0.3.28: + resolution: {integrity: sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==} + engines: {node: '>=16.13.0'} + hasBin: true + peerDependencies: + '@google-cloud/spanner': ^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + '@sap/hana-client': ^2.14.22 + better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 + ioredis: ^5.0.4 + mongodb: ^5.8.0 || ^6.0.0 + mssql: ^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^6.3.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 || ^5.0.14 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 + peerDependenciesMeta: + '@google-cloud/spanner': + optional: true + '@sap/hana-client': + optional: true + better-sqlite3: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + + uint8arrays@3.1.1: + resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici@7.22.0: + resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} + engines: {node: '>=20.18.1'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unique-filename@1.1.1: + resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} + + unique-slug@2.0.2: + resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} + + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + viem@2.38.0: + resolution: {integrity: sha512-YU5TG8dgBNeYPrCMww0u9/JVeq2ZCk9fzk6QybrPkBooFysamHXL1zC3ua10aLPt9iWoA/gSVf1D9w7nc5B1aA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.41.2: + resolution: {integrity: sha512-LYliajglBe1FU6+EH9mSWozp+gRA/QcHfxeD9Odf83AdH5fwUS7DroH4gHvlv6Sshqi1uXrYFA2B/EOczxd15g==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + web3-core@4.7.1: + resolution: {integrity: sha512-9KSeASCb/y6BG7rwhgtYC4CvYY66JfkmGNEYb7q1xgjt9BWfkf09MJPaRyoyT5trdOxYDHkT9tDlypvQWaU8UQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-errors@1.3.1: + resolution: {integrity: sha512-w3NMJujH+ZSW4ltIZZKtdbkbyQEvBzyp3JRn59Ckli0Nz4VMsVq8aF1bLWM7A2kuQ+yVEm3ySeNU+7mSRwx7RQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-eth-abi@4.4.1: + resolution: {integrity: sha512-60ecEkF6kQ9zAfbTY04Nc9q4eEYM0++BySpGi8wZ2PD1tw/c0SDvsKhV6IKURxLJhsDlb08dATc3iD6IbtWJmg==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-eth-accounts@4.3.1: + resolution: {integrity: sha512-rTXf+H9OKze6lxi7WMMOF1/2cZvJb2AOnbNQxPhBDssKOllAMzLhg1FbZ4Mf3lWecWfN6luWgRhaeSqO1l+IBQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-eth-contract@4.7.2: + resolution: {integrity: sha512-3ETqs2pMNPEAc7BVY/C3voOhTUeJdkf2aM3X1v+edbngJLHAxbvxKpOqrcO0cjXzC4uc2Q8Zpf8n8zT5r0eLnA==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-eth-ens@4.4.0: + resolution: {integrity: sha512-DeyVIS060hNV9g8dnTx92syqvgbvPricE3MerCxe/DquNZT3tD8aVgFfq65GATtpCgDDJffO2bVeHp3XBemnSQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-eth-iban@4.0.7: + resolution: {integrity: sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-eth-personal@4.1.0: + resolution: {integrity: sha512-RFN83uMuvA5cu1zIwwJh9A/bAj0OBxmGN3tgx19OD/9ygeUZbifOL06jgFzN0t+1ekHqm3DXYQM8UfHpXi7yDQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-eth@4.11.1: + resolution: {integrity: sha512-q9zOkzHnbLv44mwgLjLXuyqszHuUgZWsQayD2i/rus2uk0G7hMn11bE2Q3hOVnJS4ws4VCtUznlMxwKQ+38V2w==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-net@4.1.0: + resolution: {integrity: sha512-WWmfvHVIXWEoBDWdgKNYKN8rAy6SgluZ0abyRyXOL3ESr7ym7pKWbfP4fjApIHlYTh8tNqkrdPfM4Dyi6CA0SA==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-providers-http@4.2.0: + resolution: {integrity: sha512-IPMnDtHB7dVwaB7/mMxAZzyq7d5ezfO1+Vw0bNfAeIi7gaDlJiggp85SdyAfOgov8AMUA/dyiY72kQ0KmjXKvQ==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-providers-ipc@4.0.7: + resolution: {integrity: sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-providers-ws@4.0.8: + resolution: {integrity: sha512-goJdgata7v4pyzHRsg9fSegUG4gVnHZSHODhNnn6J93ykHkBI1nz4fjlGpcQLUMi4jAMz6SHl9Ibzs2jj9xqPw==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-rpc-methods@1.3.0: + resolution: {integrity: sha512-/CHmzGN+IYgdBOme7PdqzF+FNeMleefzqs0LVOduncSaqsppeOEoskLXb2anSpzmQAP3xZJPaTrkQPWSJMORig==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-rpc-providers@1.0.0-rc.4: + resolution: {integrity: sha512-PXosCqHW0EADrYzgmueNHP3Y5jcSmSwH+Dkqvn7EYD0T2jcsdDAIHqk6szBiwIdhumM7gv9Raprsu/s/f7h1fw==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-types@1.10.0: + resolution: {integrity: sha512-0IXoaAFtFc8Yin7cCdQfB9ZmjafrbP6BO0f0KT/khMhXKUpoJ6yShrVhiNpyRBo8QQjuOagsWzwSK2H49I7sbw==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-utils@4.3.3: + resolution: {integrity: sha512-kZUeCwaQm+RNc2Bf1V3BYbF29lQQKz28L0y+FA4G0lS8IxtJVGi5SeDTUkpwqqkdHHC7JcapPDnyyzJ1lfWlOw==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3-validator@2.0.6: + resolution: {integrity: sha512-qn9id0/l1bWmvH4XfnG/JtGKKwut2Vokl6YXP5Kfg424npysmtRLe9DgiNBM9Op7QL/aSiaA0TVXibuIuWcizg==} + engines: {node: '>=14', npm: '>=6.12.0'} + + web3@4.15.0: + resolution: {integrity: sha512-0QWDWE4gDWldXb4KWq++K8m/A9zsR0LpJLtVT39/b4OjfdW0d4mE0qAUd3UocxuKTh1eG5pOCfumbGS5l6p1qg==} + engines: {node: '>=14.0.0', npm: '>=6.12.0'} + + web3@4.16.0: + resolution: {integrity: sha512-SgoMSBo6EsJ5GFCGar2E/pR2lcR/xmUSuQ61iK6yDqzxmm42aPPxSqZfJz2z/UCR6pk03u77pU8TGV6lgMDdIQ==} + engines: {node: '>=14.0.0', npm: '>=6.12.0'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@6.0.0: + resolution: {integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==} + engines: {node: '>=20'} + + window-size@1.1.1: + resolution: {integrity: sha512-5D/9vujkmVQ7pSmc0SCBmHXbkv6eaHwXEx65MywhmUMsI8sGqJ972APq1lotfcwMKPFLuCFfL8xGHLIp7jaBmA==} + engines: {node: '>= 0.10.0'} + hasBin: true + + workerpool@6.5.1: + resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + write-json-file@6.0.0: + resolution: {integrity: sha512-MNHcU3f9WxnNyR6MxsYSj64Jz0+dwIpisWKWq9gqLj/GwmA9INg3BZ3vt70/HB3GEwrnDQWr4RPrywnhNzmUFA==} + engines: {node: '>=18'} + + write-package@7.2.0: + resolution: {integrity: sha512-uMQTubF/vcu+Wd0b5BGtDmiXePd/+44hUWQz2nZPbs92/BnxRo74tqs+hqDo12RLiEd+CXFKUwxvvIZvtt34Jw==} + engines: {node: '>=18'} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + 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 + + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + 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 + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@acala-network/chopsticks-core@1.2.3': + dependencies: + '@acala-network/chopsticks-executor': 1.2.3 + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/types-known': 16.5.4 + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) + comlink: 4.4.2 + eventemitter3: 5.0.4 + lodash: 4.17.23 + lru-cache: 11.2.6 + pino: 9.14.0 + pino-pretty: 13.1.3 + rxjs: 7.8.2 + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@acala-network/chopsticks-core@1.2.7': + dependencies: + '@acala-network/chopsticks-executor': 1.2.7 + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/types-known': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + comlink: 4.4.2 + eventemitter3: 5.0.4 + lodash: 4.17.23 + lru-cache: 11.2.6 + pino: 9.14.0 + pino-pretty: 13.1.3 + rxjs: 7.8.2 + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@acala-network/chopsticks-db@1.2.3(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3))': + dependencies: + '@acala-network/chopsticks-core': 1.2.3 + '@polkadot/util': 13.5.9 + idb: 8.0.3 + reflect-metadata: 0.2.2 + sqlite3: 5.1.7 + typeorm: 0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3)) + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - babel-plugin-macros + - better-sqlite3 + - bluebird + - bufferutil + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + - utf-8-validate + + '@acala-network/chopsticks-db@1.2.7(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3))': + dependencies: + '@acala-network/chopsticks-core': 1.2.7 + '@polkadot/util': 14.0.1 + idb: 8.0.3 + reflect-metadata: 0.2.2 + sqlite3: 5.1.7 + typeorm: 0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3)) + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - babel-plugin-macros + - better-sqlite3 + - bluebird + - bufferutil + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + - utf-8-validate + + '@acala-network/chopsticks-executor@1.2.3': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@13.5.9) + + '@acala-network/chopsticks-executor@1.2.7': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + + '@acala-network/chopsticks@1.2.3(debug@4.3.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3))': + dependencies: + '@acala-network/chopsticks-core': 1.2.3 + '@acala-network/chopsticks-db': 1.2.3(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3)) + '@pnpm/npm-conf': 3.0.2 + '@polkadot/api': 16.5.4 + '@polkadot/api-augment': 16.5.4 + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) + axios: 1.13.6(debug@4.3.7) + comlink: 4.4.2 + dotenv: 16.6.1 + global-agent: 3.0.0 + js-yaml: 4.1.1 + jsondiffpatch: 0.5.0 + lodash: 4.17.23 + ws: 8.19.0 + yargs: 18.0.0 + zod: 3.25.76 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - babel-plugin-macros + - better-sqlite3 + - bluebird + - bufferutil + - debug + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + - utf-8-validate + + '@acala-network/chopsticks@1.2.7(debug@4.3.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3))': + dependencies: + '@acala-network/chopsticks-core': 1.2.7 + '@acala-network/chopsticks-db': 1.2.7(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3)) + '@dmsnell/diff-match-patch': 1.1.0 + '@pnpm/npm-conf': 3.0.2 + '@polkadot/api': 16.5.4 + '@polkadot/api-augment': 16.5.4 + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + axios: 1.13.6(debug@4.3.7) + comlink: 4.4.2 + dotenv: 16.6.1 + global-agent: 3.0.0 + js-yaml: 4.1.1 + jsondiffpatch: 0.7.3 + lodash: 4.17.23 + ws: 8.19.0 + yargs: 18.0.0 + zod: 3.25.76 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - babel-plugin-macros + - better-sqlite3 + - bluebird + - bufferutil + - debug + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + - utf-8-validate + + '@adraffy/ens-normalize@1.10.1': {} + + '@adraffy/ens-normalize@1.11.1': {} + + '@alcalzone/ansi-tokenize@0.2.5': + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + '@ark/util@0.56.0': {} + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@asamuzakjp/dom-selector@2.0.2': + dependencies: + bidi-js: 1.0.3 + css-tree: 2.3.1 + is-potential-custom-element-name: 1.0.1 + + '@ast-grep/napi-darwin-arm64@0.40.5': + optional: true + + '@ast-grep/napi-darwin-x64@0.40.5': + optional: true + + '@ast-grep/napi-linux-arm64-gnu@0.40.5': + optional: true + + '@ast-grep/napi-linux-arm64-musl@0.40.5': + optional: true + + '@ast-grep/napi-linux-x64-gnu@0.40.5': + optional: true + + '@ast-grep/napi-linux-x64-musl@0.40.5': + optional: true + + '@ast-grep/napi-win32-arm64-msvc@0.40.5': + optional: true + + '@ast-grep/napi-win32-ia32-msvc@0.40.5': + optional: true + + '@ast-grep/napi-win32-x64-msvc@0.40.5': + optional: true + + '@ast-grep/napi@0.40.5': + optionalDependencies: + '@ast-grep/napi-darwin-arm64': 0.40.5 + '@ast-grep/napi-darwin-x64': 0.40.5 + '@ast-grep/napi-linux-arm64-gnu': 0.40.5 + '@ast-grep/napi-linux-arm64-musl': 0.40.5 + '@ast-grep/napi-linux-x64-gnu': 0.40.5 + '@ast-grep/napi-linux-x64-musl': 0.40.5 + '@ast-grep/napi-win32-arm64-msvc': 0.40.5 + '@ast-grep/napi-win32-ia32-msvc': 0.40.5 + '@ast-grep/napi-win32-x64-msvc': 0.40.5 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/helper-validator-identifier@7.28.5': {} + + '@balena/dockerignore@1.0.2': {} + + '@biomejs/biome@1.9.4': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.9.4 + '@biomejs/cli-darwin-x64': 1.9.4 + '@biomejs/cli-linux-arm64': 1.9.4 + '@biomejs/cli-linux-arm64-musl': 1.9.4 + '@biomejs/cli-linux-x64': 1.9.4 + '@biomejs/cli-linux-x64-musl': 1.9.4 + '@biomejs/cli-win32-arm64': 1.9.4 + '@biomejs/cli-win32-x64': 1.9.4 + + '@biomejs/cli-darwin-arm64@1.9.4': + optional: true + + '@biomejs/cli-darwin-x64@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-arm64@1.9.4': + optional: true + + '@biomejs/cli-linux-x64-musl@1.9.4': + optional: true + + '@biomejs/cli-linux-x64@1.9.4': + optional: true + + '@biomejs/cli-win32-arm64@1.9.4': + optional: true + + '@biomejs/cli-win32-x64@1.9.4': + optional: true + + '@colors/colors@1.5.0': + optional: true + + '@commander-js/extra-typings@14.0.0(commander@14.0.3)': + dependencies: + commander: 14.0.3 + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@dmsnell/diff-match-patch@1.1.0': {} + + '@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.19) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/workflow': 0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19) + effect: 3.19.19 + kubernetes-types: 1.30.0 + + '@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.19) + effect: 3.19.19 + uuid: 11.1.0 + + '@effect/platform-node-shared@0.56.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19)': + dependencies: + '@effect/cluster': 0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19) + '@effect/platform': 0.93.8(effect@3.19.19) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@parcel/watcher': 2.5.6 + effect: 3.19.19 + multipasta: 0.2.7 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@effect/platform-node@0.103.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19)': + dependencies: + '@effect/cluster': 0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19) + '@effect/platform': 0.93.8(effect@3.19.19) + '@effect/platform-node-shared': 0.56.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + effect: 3.19.19 + mime: 3.0.0 + undici: 7.22.0 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@effect/platform@0.93.8(effect@3.19.19)': + dependencies: + effect: 3.19.19 + find-my-way-ts: 0.1.6 + msgpackr: 1.11.8 + multipasta: 0.2.7 + + '@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19)': + dependencies: + '@effect/platform': 0.93.8(effect@3.19.19) + effect: 3.19.19 + msgpackr: 1.11.8 + + '@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19)': + dependencies: + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/platform': 0.93.8(effect@3.19.19) + effect: 3.19.19 + uuid: 11.1.0 + + '@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19)': + dependencies: + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/platform': 0.93.8(effect@3.19.19) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + effect: 3.19.19 + + '@esbuild/aix-ppc64@0.27.3': + optional: true + + '@esbuild/android-arm64@0.27.3': + optional: true + + '@esbuild/android-arm@0.27.3': + optional: true + + '@esbuild/android-x64@0.27.3': + optional: true + + '@esbuild/darwin-arm64@0.27.3': + optional: true + + '@esbuild/darwin-x64@0.27.3': + optional: true + + '@esbuild/freebsd-arm64@0.27.3': + optional: true + + '@esbuild/freebsd-x64@0.27.3': + optional: true + + '@esbuild/linux-arm64@0.27.3': + optional: true + + '@esbuild/linux-arm@0.27.3': + optional: true + + '@esbuild/linux-ia32@0.27.3': + optional: true + + '@esbuild/linux-loong64@0.27.3': + optional: true + + '@esbuild/linux-mips64el@0.27.3': + optional: true + + '@esbuild/linux-ppc64@0.27.3': + optional: true + + '@esbuild/linux-riscv64@0.27.3': + optional: true + + '@esbuild/linux-s390x@0.27.3': + optional: true + + '@esbuild/linux-x64@0.27.3': + optional: true + + '@esbuild/netbsd-arm64@0.27.3': + optional: true + + '@esbuild/netbsd-x64@0.27.3': + optional: true + + '@esbuild/openbsd-arm64@0.27.3': + optional: true + + '@esbuild/openbsd-x64@0.27.3': + optional: true + + '@esbuild/openharmony-arm64@0.27.3': + optional: true + + '@esbuild/sunos-x64@0.27.3': + optional: true + + '@esbuild/win32-arm64@0.27.3': + optional: true + + '@esbuild/win32-ia32@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@ethereumjs/rlp@4.0.1': {} + + '@ethereumjs/rlp@5.0.2': {} + + '@gar/promisify@1.1.3': + optional: true + + '@grpc/grpc-js@1.14.3': + dependencies: + '@grpc/proto-loader': 0.8.0 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@grpc/proto-loader@0.8.0': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@inquirer/ansi@1.0.2': {} + + '@inquirer/ansi@2.0.3': {} + + '@inquirer/checkbox@4.3.2(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@25.3.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/checkbox@5.1.0(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 2.0.3 + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/figures': 2.0.3 + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/confirm@5.1.21(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/confirm@6.0.8(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/core@10.3.2(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@25.3.5) + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/core@11.1.5(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 2.0.3 + '@inquirer/figures': 2.0.3 + '@inquirer/type': 4.0.3(@types/node@25.3.5) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/editor@4.2.23(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/external-editor': 1.0.3(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/editor@5.0.8(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/external-editor': 2.0.3(@types/node@25.3.5) + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/expand@4.0.23(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.3.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/expand@5.0.8(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/external-editor@1.0.3(@types/node@25.3.5)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/external-editor@2.0.3(@types/node@25.3.5)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/figures@1.0.15': {} + + '@inquirer/figures@2.0.3': {} + + '@inquirer/input@4.3.1(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/input@5.0.8(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/number@3.0.23(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/number@4.0.8(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/password@4.0.23(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/password@5.0.8(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 2.0.3 + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/prompts@7.3.1(@types/node@25.3.5)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@25.3.5) + '@inquirer/confirm': 5.1.21(@types/node@25.3.5) + '@inquirer/editor': 4.2.23(@types/node@25.3.5) + '@inquirer/expand': 4.0.23(@types/node@25.3.5) + '@inquirer/input': 4.3.1(@types/node@25.3.5) + '@inquirer/number': 3.0.23(@types/node@25.3.5) + '@inquirer/password': 4.0.23(@types/node@25.3.5) + '@inquirer/rawlist': 4.1.11(@types/node@25.3.5) + '@inquirer/search': 3.2.2(@types/node@25.3.5) + '@inquirer/select': 4.4.2(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/prompts@8.3.0(@types/node@25.3.5)': + dependencies: + '@inquirer/checkbox': 5.1.0(@types/node@25.3.5) + '@inquirer/confirm': 6.0.8(@types/node@25.3.5) + '@inquirer/editor': 5.0.8(@types/node@25.3.5) + '@inquirer/expand': 5.0.8(@types/node@25.3.5) + '@inquirer/input': 5.0.8(@types/node@25.3.5) + '@inquirer/number': 4.0.8(@types/node@25.3.5) + '@inquirer/password': 5.0.8(@types/node@25.3.5) + '@inquirer/rawlist': 5.2.4(@types/node@25.3.5) + '@inquirer/search': 4.1.4(@types/node@25.3.5) + '@inquirer/select': 5.1.0(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/rawlist@4.1.11(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.3.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/rawlist@5.2.4(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/search@3.2.2(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@25.3.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/search@4.1.4(@types/node@25.3.5)': + dependencies: + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/figures': 2.0.3 + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/select@4.4.2(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@25.3.5) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/select@5.1.0(@types/node@25.3.5)': + dependencies: + '@inquirer/ansi': 2.0.3 + '@inquirer/core': 11.1.5(@types/node@25.3.5) + '@inquirer/figures': 2.0.3 + '@inquirer/type': 4.0.3(@types/node@25.3.5) + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/type@3.0.10(@types/node@25.3.5)': + optionalDependencies: + '@types/node': 25.3.5 + + '@inquirer/type@4.0.3(@types/node@25.3.5)': + optionalDependencies: + '@types/node': 25.3.5 + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.1': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@moonwall/cli@5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api-derive@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/rpc-provider@16.5.4)(@polkadot/types-codec@16.5.4)(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@types/node@25.3.5)(chokidar@3.6.0)(debug@4.3.7)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3))(tsx@4.21.0)(typescript@5.8.3)(zod@3.25.76)': + dependencies: + '@acala-network/chopsticks': 1.2.7(debug@4.3.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3)) + '@ast-grep/napi': 0.40.5 + '@effect/cluster': 0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19) + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/platform': 0.93.8(effect@3.19.19) + '@effect/platform-node': 0.103.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19) + '@effect/workflow': 0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(@effect/platform@0.93.8(effect@3.19.19))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19))(effect@3.19.19) + '@inquirer/prompts': 8.3.0(@types/node@25.3.5) + '@moonwall/types': 5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@vitest/ui@3.2.4)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76) + '@moonwall/util': 5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api-derive@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/rpc-provider@16.5.4)(@polkadot/types-codec@16.5.4)(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@types/node@25.3.5)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76) + '@octokit/rest': 22.0.1 + '@polkadot/api': 16.5.4 + '@polkadot/api-derive': 16.5.4 + '@polkadot/keyring': 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + '@types/react': 19.2.7 + '@types/tmp': 0.2.6 + '@vitest/ui': 3.2.4(vitest@3.2.4) + '@zombienet/orchestrator': 0.0.113(@polkadot/util@14.0.1)(@types/node@25.3.5)(chokidar@3.6.0) + '@zombienet/utils': 0.0.30(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3) + arkregex: 0.0.4 + bottleneck: 2.19.5 + cfonts: 3.3.1 + chalk: 5.6.2 + clear: 0.1.0 + cli-progress: 3.12.0 + colors: 1.4.0 + dockerode: 4.0.9 + dotenv: 17.2.3 + effect: 3.19.19 + ethers: 6.16.0 + ink: 6.8.0(@types/react@19.2.7)(react@19.2.4) + jsonc-parser: 3.3.1 + minimatch: 10.1.1 + pino: 10.3.1 + polkadot-api: 1.19.2(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(yaml@2.8.2) + react: 19.2.4 + reflect-metadata: 0.2.2 + semver: 7.7.4 + tiny-invariant: 1.3.3 + tmp: 0.2.5 + viem: 2.41.2(typescript@5.8.3)(zod@3.25.76) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2) + web3: 4.16.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-providers-ws: 4.0.8 + ws: 8.19.0 + yaml: 2.8.2 + yargs: 18.0.0 + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@google-cloud/spanner' + - '@microsoft/api-extractor' + - '@polkadot/api-base' + - '@sap/hana-client' + - '@swc/core' + - '@swc/wasm' + - '@types/debug' + - '@types/node' + - '@vitest/browser' + - babel-plugin-macros + - better-sqlite3 + - bluebird + - bufferutil + - canvas + - chokidar + - debug + - encoding + - happy-dom + - ioredis + - jiti + - jsdom + - less + - lightningcss + - lmdb + - mongodb + - mssql + - msw + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - postcss + - react-devtools-core + - redis + - rxjs + - sass + - sass-embedded + - sql.js + - stylus + - sugarss + - supports-color + - terser + - ts-node + - tsx + - typeorm-aurora-data-api-driver + - typescript + - utf-8-validate + - zod + + '@moonwall/types@5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@vitest/ui@3.2.4)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76)': + dependencies: + '@polkadot/api': 16.5.4 + '@polkadot/api-base': 16.5.4 + '@polkadot/keyring': 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/types': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + '@types/node': 24.12.0 + '@zombienet/utils': 0.0.30(@types/node@24.12.0)(chokidar@3.6.0)(typescript@5.8.3) + bottleneck: 2.19.5 + ethers: 6.16.0 + polkadot-api: 1.19.2(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(yaml@2.8.2) + viem: 2.41.2(typescript@5.8.3)(zod@3.25.76) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2) + web3: 4.16.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@microsoft/api-extractor' + - '@swc/core' + - '@swc/wasm' + - '@types/debug' + - '@vitest/browser' + - '@vitest/ui' + - bufferutil + - chokidar + - encoding + - happy-dom + - jiti + - jsdom + - less + - lightningcss + - msw + - postcss + - rxjs + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - utf-8-validate + - yaml + - zod + + '@moonwall/util@5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api-derive@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/rpc-provider@16.5.4)(@polkadot/types-codec@16.5.4)(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@types/node@25.3.5)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76)': + dependencies: + '@inquirer/prompts': 8.3.0(@types/node@25.3.5) + '@moonwall/types': 5.18.3(@polkadot/api-base@16.5.4)(@polkadot/api@16.5.4)(@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1))(@polkadot/types@16.5.4)(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)(@types/debug@4.1.12)(@vitest/ui@3.2.4)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76) + '@polkadot/api': 16.5.4 + '@polkadot/api-derive': 16.5.4 + '@polkadot/keyring': 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + '@vitest/ui': 3.2.4(vitest@3.2.4) + arkregex: 0.0.4 + bottleneck: 2.19.5 + chalk: 5.6.2 + clear: 0.1.0 + colors: 1.4.0 + dotenv: 17.2.3 + ethers: 6.16.0 + pino: 10.3.1 + pino-pretty: 13.1.3 + rlp: 3.0.0 + semver: 7.7.4 + tiny-invariant: 1.3.3 + viem: 2.41.2(typescript@5.8.3)(zod@3.25.76) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2) + web3: 4.16.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + ws: 8.19.0 + yargs: 18.0.0 + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@microsoft/api-extractor' + - '@polkadot/api-base' + - '@swc/core' + - '@swc/wasm' + - '@types/debug' + - '@types/node' + - '@vitest/browser' + - bufferutil + - chokidar + - encoding + - happy-dom + - jiti + - jsdom + - less + - lightningcss + - msw + - postcss + - rxjs + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - utf-8-validate + - yaml + - zod + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@noble/ciphers@1.2.1': {} + + '@noble/ciphers@1.3.0': {} + + '@noble/ciphers@2.1.1': {} + + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/curves@1.4.2': + dependencies: + '@noble/hashes': 1.4.0 + + '@noble/curves@1.8.1': + dependencies: + '@noble/hashes': 1.7.1 + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/ed25519@1.7.5': {} + + '@noble/hashes@1.3.2': {} + + '@noble/hashes@1.4.0': {} + + '@noble/hashes@1.7.1': {} + + '@noble/hashes@1.8.0': {} + + '@noble/hashes@2.0.1': {} + + '@noble/secp256k1@1.7.2': {} + + '@npmcli/fs@1.1.1': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.7.4 + optional: true + + '@npmcli/move-file@1.1.2': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + optional: true + + '@octokit/auth-token@6.0.0': {} + + '@octokit/core@7.0.6': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.3 + '@octokit/request': 10.0.8 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.3': + dependencies: + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/graphql@9.0.3': + dependencies: + '@octokit/request': 10.0.8 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/openapi-types@27.0.0': {} + + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/request-error@7.1.0': + dependencies: + '@octokit/types': 16.0.0 + + '@octokit/request@10.0.8': + dependencies: + '@octokit/endpoint': 11.0.3 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + fast-content-type-parse: 3.0.0 + json-with-bigint: 3.5.7 + universal-user-agent: 7.0.3 + + '@octokit/rest@22.0.1': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) + + '@octokit/types@16.0.0': + dependencies: + '@octokit/openapi-types': 27.0.0 + + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.3 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + + '@pinojs/redact@0.4.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@3.0.2': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + + '@polka/url@1.0.0-next.29': {} + + '@polkadot-api/cli@0.15.2(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.2)': + dependencies: + '@commander-js/extra-typings': 14.0.0(commander@14.0.3) + '@polkadot-api/codegen': 0.19.1 + '@polkadot-api/ink-contracts': 0.4.0 + '@polkadot-api/json-rpc-provider': 0.0.4 + '@polkadot-api/known-chains': 0.9.11 + '@polkadot-api/legacy-provider': 0.3.2(rxjs@7.8.2) + '@polkadot-api/metadata-compatibility': 0.3.6 + '@polkadot-api/observable-client': 0.15.1(rxjs@7.8.2) + '@polkadot-api/polkadot-sdk-compat': 2.3.3 + '@polkadot-api/sm-provider': 0.1.11(@polkadot-api/smoldot@0.3.14) + '@polkadot-api/smoldot': 0.3.14 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/substrate-client': 0.4.7 + '@polkadot-api/utils': 0.2.0 + '@polkadot-api/wasm-executor': 0.2.3 + '@polkadot-api/ws-provider': 0.6.2 + '@types/node': 24.12.0 + commander: 14.0.3 + execa: 9.6.1 + fs.promises.exists: 1.1.4 + ora: 9.3.0 + read-pkg: 9.0.1 + rxjs: 7.8.2 + tsc-prog: 2.3.0(typescript@5.9.3) + tsup: 8.5.1(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + typescript: 5.9.3 + write-package: 7.2.0 + transitivePeerDependencies: + - '@microsoft/api-extractor' + - '@swc/core' + - bufferutil + - jiti + - postcss + - supports-color + - tsx + - utf-8-validate + - yaml + + '@polkadot-api/codegen@0.19.1': + dependencies: + '@polkadot-api/ink-contracts': 0.4.0 + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/metadata-compatibility': 0.3.6 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/descriptors@file:.papi/descriptors(polkadot-api@1.19.2(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + polkadot-api: 1.19.2(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(yaml@2.8.2) + optional: true + + '@polkadot-api/ink-contracts@0.4.0': + dependencies: + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/json-rpc-provider-proxy@0.1.0': + optional: true + + '@polkadot-api/json-rpc-provider-proxy@0.2.4': {} + + '@polkadot-api/json-rpc-provider@0.0.1': + optional: true + + '@polkadot-api/json-rpc-provider@0.0.4': {} + + '@polkadot-api/known-chains@0.9.11': {} + + '@polkadot-api/legacy-provider@0.3.2(rxjs@7.8.2)': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.4 + '@polkadot-api/raw-client': 0.1.1 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + rxjs: 7.8.2 + + '@polkadot-api/logs-provider@0.0.6': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.4 + + '@polkadot-api/merkleize-metadata@1.1.25': + dependencies: + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/merkleize-metadata@1.1.29': + dependencies: + '@polkadot-api/metadata-builders': 0.13.9 + '@polkadot-api/substrate-bindings': 0.17.0 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/metadata-builders@0.13.5': + dependencies: + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/metadata-builders@0.13.9': + dependencies: + '@polkadot-api/substrate-bindings': 0.17.0 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/metadata-builders@0.3.2': + dependencies: + '@polkadot-api/substrate-bindings': 0.6.0 + '@polkadot-api/utils': 0.1.0 + optional: true + + '@polkadot-api/metadata-compatibility@0.3.6': + dependencies: + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/substrate-bindings': 0.16.3 + + '@polkadot-api/observable-client@0.15.1(rxjs@7.8.2)': + dependencies: + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/substrate-client': 0.4.7 + '@polkadot-api/utils': 0.2.0 + rxjs: 7.8.2 + + '@polkadot-api/observable-client@0.3.2(@polkadot-api/substrate-client@0.1.4)(rxjs@7.8.2)': + dependencies: + '@polkadot-api/metadata-builders': 0.3.2 + '@polkadot-api/substrate-bindings': 0.6.0 + '@polkadot-api/substrate-client': 0.1.4 + '@polkadot-api/utils': 0.1.0 + rxjs: 7.8.2 + optional: true + + '@polkadot-api/pjs-signer@0.6.15': + dependencies: + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/polkadot-signer': 0.1.6 + '@polkadot-api/signers-common': 0.1.16 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/polkadot-sdk-compat@2.3.3': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.4 + + '@polkadot-api/polkadot-signer@0.1.6': {} + + '@polkadot-api/raw-client@0.1.1': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.4 + + '@polkadot-api/signer@0.2.9': + dependencies: + '@noble/hashes': 2.0.1 + '@polkadot-api/merkleize-metadata': 1.1.25 + '@polkadot-api/polkadot-signer': 0.1.6 + '@polkadot-api/signers-common': 0.1.16 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/signers-common@0.1.16': + dependencies: + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/polkadot-signer': 0.1.6 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/sm-provider@0.1.11(@polkadot-api/smoldot@0.3.14)': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.4 + '@polkadot-api/json-rpc-provider-proxy': 0.2.4 + '@polkadot-api/smoldot': 0.3.14 + + '@polkadot-api/smoldot@0.3.14': + dependencies: + '@types/node': 24.12.0 + smoldot: 2.0.39 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@polkadot-api/substrate-bindings@0.16.3': + dependencies: + '@noble/hashes': 2.0.1 + '@polkadot-api/utils': 0.2.0 + '@scure/base': 2.0.0 + scale-ts: 1.6.1 + + '@polkadot-api/substrate-bindings@0.17.0': + dependencies: + '@noble/hashes': 2.0.1 + '@polkadot-api/utils': 0.2.0 + '@scure/base': 2.0.0 + scale-ts: 1.6.1 + + '@polkadot-api/substrate-bindings@0.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@polkadot-api/utils': 0.1.0 + '@scure/base': 1.2.6 + scale-ts: 1.6.1 + optional: true + + '@polkadot-api/substrate-client@0.1.4': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.1 + '@polkadot-api/utils': 0.1.0 + optional: true + + '@polkadot-api/substrate-client@0.4.7': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.4 + '@polkadot-api/raw-client': 0.1.1 + '@polkadot-api/utils': 0.2.0 + + '@polkadot-api/utils@0.1.0': + optional: true + + '@polkadot-api/utils@0.2.0': {} + + '@polkadot-api/wasm-executor@0.2.3': {} + + '@polkadot-api/ws-provider@0.6.2': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.4 + '@polkadot-api/json-rpc-provider-proxy': 0.2.4 + '@types/ws': 8.18.1 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@polkadot/api-augment@14.3.1': + dependencies: + '@polkadot/api-base': 14.3.1 + '@polkadot/rpc-augment': 14.3.1 + '@polkadot/types': 14.3.1 + '@polkadot/types-augment': 14.3.1 + '@polkadot/types-codec': 14.3.1 + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/api-augment@16.5.4': + dependencies: + '@polkadot/api-base': 16.5.4 + '@polkadot/rpc-augment': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-augment': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/api-base@14.3.1': + dependencies: + '@polkadot/rpc-core': 14.3.1 + '@polkadot/types': 14.3.1 + '@polkadot/util': 13.5.9 + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/api-base@16.5.4': + dependencies: + '@polkadot/rpc-core': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/util': 14.0.1 + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/api-derive@14.3.1': + dependencies: + '@polkadot/api': 14.3.1 + '@polkadot/api-augment': 14.3.1 + '@polkadot/api-base': 14.3.1 + '@polkadot/rpc-core': 14.3.1 + '@polkadot/types': 14.3.1 + '@polkadot/types-codec': 14.3.1 + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/api-derive@16.5.4': + dependencies: + '@polkadot/api': 16.5.4 + '@polkadot/api-augment': 16.5.4 + '@polkadot/api-base': 16.5.4 + '@polkadot/rpc-core': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/api@14.3.1': + dependencies: + '@polkadot/api-augment': 14.3.1 + '@polkadot/api-base': 14.3.1 + '@polkadot/api-derive': 14.3.1 + '@polkadot/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1))(@polkadot/util@13.5.9) + '@polkadot/rpc-augment': 14.3.1 + '@polkadot/rpc-core': 14.3.1 + '@polkadot/rpc-provider': 14.3.1 + '@polkadot/types': 14.3.1 + '@polkadot/types-augment': 14.3.1 + '@polkadot/types-codec': 14.3.1 + '@polkadot/types-create': 14.3.1 + '@polkadot/types-known': 14.3.1 + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) + eventemitter3: 5.0.4 + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/api@16.5.4': + dependencies: + '@polkadot/api-augment': 16.5.4 + '@polkadot/api-base': 16.5.4 + '@polkadot/api-derive': 16.5.4 + '@polkadot/keyring': 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/rpc-augment': 16.5.4 + '@polkadot/rpc-core': 16.5.4 + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-augment': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/types-create': 16.5.4 + '@polkadot/types-known': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + eventemitter3: 5.0.4 + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/keyring@13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1))(@polkadot/util@13.5.9)': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.1) + tslib: 2.8.1 + + '@polkadot/keyring@13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.1) + tslib: 2.8.1 + + '@polkadot/keyring@14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1)': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + tslib: 2.8.1 + + '@polkadot/networks@13.5.9': + dependencies: + '@polkadot/util': 13.5.9 + '@substrate/ss58-registry': 1.51.0 + tslib: 2.8.1 + + '@polkadot/networks@14.0.1': + dependencies: + '@polkadot/util': 14.0.1 + '@substrate/ss58-registry': 1.51.0 + tslib: 2.8.1 + + '@polkadot/rpc-augment@14.3.1': + dependencies: + '@polkadot/rpc-core': 14.3.1 + '@polkadot/types': 14.3.1 + '@polkadot/types-codec': 14.3.1 + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/rpc-augment@16.5.4': + dependencies: + '@polkadot/rpc-core': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/rpc-core@14.3.1': + dependencies: + '@polkadot/rpc-augment': 14.3.1 + '@polkadot/rpc-provider': 14.3.1 + '@polkadot/types': 14.3.1 + '@polkadot/util': 13.5.9 + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/rpc-core@16.5.4': + dependencies: + '@polkadot/rpc-augment': 16.5.4 + '@polkadot/rpc-provider': 16.5.4 + '@polkadot/types': 16.5.4 + '@polkadot/util': 14.0.1 + rxjs: 7.8.2 + tslib: 2.8.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/rpc-provider@14.3.1': + dependencies: + '@polkadot/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1))(@polkadot/util@13.5.9) + '@polkadot/types': 14.3.1 + '@polkadot/types-support': 14.3.1 + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) + '@polkadot/x-fetch': 13.5.9 + '@polkadot/x-global': 13.5.9 + '@polkadot/x-ws': 13.5.9 + eventemitter3: 5.0.4 + mock-socket: 9.3.1 + nock: 13.5.6 + tslib: 2.8.1 + optionalDependencies: + '@substrate/connect': 0.8.11 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/rpc-provider@16.5.4': + dependencies: + '@polkadot/keyring': 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/types': 16.5.4 + '@polkadot/types-support': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + '@polkadot/x-fetch': 14.0.1 + '@polkadot/x-global': 14.0.1 + '@polkadot/x-ws': 14.0.1 + eventemitter3: 5.0.4 + mock-socket: 9.3.1 + nock: 13.5.6 + tslib: 2.8.1 + optionalDependencies: + '@substrate/connect': 0.8.11 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@polkadot/types-augment@14.3.1': + dependencies: + '@polkadot/types': 14.3.1 + '@polkadot/types-codec': 14.3.1 + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + + '@polkadot/types-augment@16.5.4': + dependencies: + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + + '@polkadot/types-codec@14.3.1': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/x-bigint': 13.5.9 + tslib: 2.8.1 + + '@polkadot/types-codec@16.5.4': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/x-bigint': 14.0.1 + tslib: 2.8.1 + + '@polkadot/types-create@14.3.1': + dependencies: + '@polkadot/types-codec': 14.3.1 + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + + '@polkadot/types-create@16.5.4': + dependencies: + '@polkadot/types-codec': 16.5.4 + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + + '@polkadot/types-known@14.3.1': + dependencies: + '@polkadot/networks': 13.5.9 + '@polkadot/types': 14.3.1 + '@polkadot/types-codec': 14.3.1 + '@polkadot/types-create': 14.3.1 + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + + '@polkadot/types-known@16.5.4': + dependencies: + '@polkadot/networks': 14.0.1 + '@polkadot/types': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/types-create': 16.5.4 + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + + '@polkadot/types-support@14.3.1': + dependencies: + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + + '@polkadot/types-support@16.5.4': + dependencies: + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + + '@polkadot/types@14.3.1': + dependencies: + '@polkadot/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1))(@polkadot/util@13.5.9) + '@polkadot/types-augment': 14.3.1 + '@polkadot/types-codec': 14.3.1 + '@polkadot/types-create': 14.3.1 + '@polkadot/util': 13.5.9 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) + rxjs: 7.8.2 + tslib: 2.8.1 + + '@polkadot/types@16.5.4': + dependencies: + '@polkadot/keyring': 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/types-augment': 16.5.4 + '@polkadot/types-codec': 16.5.4 + '@polkadot/types-create': 16.5.4 + '@polkadot/util': 14.0.1 + '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + rxjs: 7.8.2 + tslib: 2.8.1 + + '@polkadot/util-crypto@13.5.9(@polkadot/util@13.5.9)': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@polkadot/networks': 13.5.9 + '@polkadot/util': 13.5.9 + '@polkadot/wasm-crypto': 7.5.4(@polkadot/util@13.5.9)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/x-bigint': 13.5.9 + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + '@scure/base': 1.2.6 + tslib: 2.8.1 + + '@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1)': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@polkadot/networks': 13.5.9 + '@polkadot/util': 14.0.1 + '@polkadot/wasm-crypto': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-bigint': 13.5.9 + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + '@scure/base': 1.2.6 + tslib: 2.8.1 + + '@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1)': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@polkadot/networks': 14.0.1 + '@polkadot/util': 14.0.1 + '@polkadot/wasm-crypto': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-bigint': 14.0.1 + '@polkadot/x-randomvalues': 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + '@scure/base': 1.2.6 + '@scure/sr25519': 0.2.0 + tslib: 2.8.1 + + '@polkadot/util@13.5.9': + dependencies: + '@polkadot/x-bigint': 13.5.9 + '@polkadot/x-global': 13.5.9 + '@polkadot/x-textdecoder': 13.5.9 + '@polkadot/x-textencoder': 13.5.9 + '@types/bn.js': 5.2.0 + bn.js: 5.2.3 + tslib: 2.8.1 + + '@polkadot/util@14.0.1': + dependencies: + '@polkadot/x-bigint': 14.0.1 + '@polkadot/x-global': 14.0.1 + '@polkadot/x-textdecoder': 14.0.1 + '@polkadot/x-textencoder': 14.0.1 + '@types/bn.js': 5.2.0 + bn.js: 5.2.3 + tslib: 2.8.1 + + '@polkadot/wasm-bridge@7.5.4(@polkadot/util@13.5.9)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-bridge@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-bridge@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-randomvalues': 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-crypto-asmjs@7.5.4(@polkadot/util@13.5.9)': + dependencies: + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + + '@polkadot/wasm-crypto-asmjs@7.5.4(@polkadot/util@14.0.1)': + dependencies: + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + + '@polkadot/wasm-crypto-init@7.5.4(@polkadot/util@13.5.9)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@13.5.9)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-crypto-init@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-crypto-init@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-randomvalues': 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-crypto-wasm@7.5.4(@polkadot/util@13.5.9)': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@13.5.9) + tslib: 2.8.1 + + '@polkadot/wasm-crypto-wasm@7.5.4(@polkadot/util@14.0.1)': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + tslib: 2.8.1 + + '@polkadot/wasm-crypto@7.5.4(@polkadot/util@13.5.9)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@13.5.9)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/wasm-crypto-init': 7.5.4(@polkadot/util@13.5.9)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@13.5.9) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-crypto@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-crypto-init': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-crypto@7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-crypto-init': 7.5.4(@polkadot/util@14.0.1)(@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-randomvalues': 14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)) + tslib: 2.8.1 + + '@polkadot/wasm-util@7.5.4(@polkadot/util@13.5.9)': + dependencies: + '@polkadot/util': 13.5.9 + tslib: 2.8.1 + + '@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)': + dependencies: + '@polkadot/util': 14.0.1 + tslib: 2.8.1 + + '@polkadot/x-bigint@13.5.9': + dependencies: + '@polkadot/x-global': 13.5.9 + tslib: 2.8.1 + + '@polkadot/x-bigint@14.0.1': + dependencies: + '@polkadot/x-global': 14.0.1 + tslib: 2.8.1 + + '@polkadot/x-fetch@13.5.9': + dependencies: + '@polkadot/x-global': 13.5.9 + node-fetch: 3.3.2 + tslib: 2.8.1 + + '@polkadot/x-fetch@14.0.1': + dependencies: + '@polkadot/x-global': 14.0.1 + node-fetch: 3.3.2 + tslib: 2.8.1 + + '@polkadot/x-global@13.5.9': + dependencies: + tslib: 2.8.1 + + '@polkadot/x-global@14.0.1': + dependencies: + tslib: 2.8.1 + + '@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))': + dependencies: + '@polkadot/util': 13.5.9 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-global': 13.5.9 + tslib: 2.8.1 + + '@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-global': 13.5.9 + tslib: 2.8.1 + + '@polkadot/x-randomvalues@14.0.1(@polkadot/util@14.0.1)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1))': + dependencies: + '@polkadot/util': 14.0.1 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/x-global': 14.0.1 + tslib: 2.8.1 + + '@polkadot/x-textdecoder@13.5.9': + dependencies: + '@polkadot/x-global': 13.5.9 + tslib: 2.8.1 + + '@polkadot/x-textdecoder@14.0.1': + dependencies: + '@polkadot/x-global': 14.0.1 + tslib: 2.8.1 + + '@polkadot/x-textencoder@13.5.9': + dependencies: + '@polkadot/x-global': 13.5.9 + tslib: 2.8.1 + + '@polkadot/x-textencoder@14.0.1': + dependencies: + '@polkadot/x-global': 14.0.1 + tslib: 2.8.1 + + '@polkadot/x-ws@13.5.9': + dependencies: + '@polkadot/x-global': 13.5.9 + tslib: 2.8.1 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@polkadot/x-ws@14.0.1': + dependencies: + '@polkadot/x-global': 14.0.1 + tslib: 2.8.1 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@rollup/rollup-android-arm-eabi@4.59.0': + optional: true + + '@rollup/rollup-android-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-x64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.59.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.59.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.59.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.59.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.59.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.59.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.59.0': + optional: true + + '@rx-state/core@0.1.4(rxjs@7.8.2)': + dependencies: + rxjs: 7.8.2 + + '@scure/base@1.1.9': {} + + '@scure/base@1.2.6': {} + + '@scure/base@2.0.0': {} + + '@scure/bip32@1.4.0': + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + + '@scure/bip32@1.6.2': + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.3.0': + dependencies: + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + + '@scure/bip39@1.5.4': + dependencies: + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/sr25519@0.2.0': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + + '@sec-ant/readable-stream@0.4.1': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@sqltools/formatter@1.2.5': {} + + '@standard-schema/spec@1.1.0': {} + + '@substrate/connect-extension-protocol@2.2.2': + optional: true + + '@substrate/connect-known-chains@1.10.3': + optional: true + + '@substrate/connect@0.8.11': + dependencies: + '@substrate/connect-extension-protocol': 2.2.2 + '@substrate/connect-known-chains': 1.10.3 + '@substrate/light-client-extension-helpers': 1.0.0(smoldot@2.0.26) + smoldot: 2.0.26 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + optional: true + + '@substrate/light-client-extension-helpers@1.0.0(smoldot@2.0.26)': + dependencies: + '@polkadot-api/json-rpc-provider': 0.0.1 + '@polkadot-api/json-rpc-provider-proxy': 0.1.0 + '@polkadot-api/observable-client': 0.3.2(@polkadot-api/substrate-client@0.1.4)(rxjs@7.8.2) + '@polkadot-api/substrate-client': 0.1.4 + '@substrate/connect-extension-protocol': 2.2.2 + '@substrate/connect-known-chains': 1.10.3 + rxjs: 7.8.2 + smoldot: 2.0.26 + optional: true + + '@substrate/ss58-registry@1.51.0': {} + + '@tootallnate/once@1.1.2': + optional: true + + '@tsconfig/node10@1.0.12': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/bn.js@5.2.0': + dependencies: + '@types/node': 25.3.5 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/json-bigint@1.0.4': {} + + '@types/long@4.0.2': {} + + '@types/ms@2.1.0': {} + + '@types/node@22.7.5': + dependencies: + undici-types: 6.19.8 + + '@types/node@24.12.0': + dependencies: + undici-types: 7.16.0 + + '@types/node@25.3.5': + dependencies: + undici-types: 7.18.2 + + '@types/normalize-package-data@2.4.4': {} + + '@types/ps-node@0.1.3': {} + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + + '@types/tmp@0.2.6': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 25.3.5 + + '@types/ws@8.5.3': + dependencies: + '@types/node': 25.3.5 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2) + + '@vitest/pretty-format@3.1.3': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/ui@3.1.3(vitest@3.2.4)': + dependencies: + '@vitest/utils': 3.1.3 + fflate: 0.8.2 + flatted: 3.3.4 + pathe: 2.0.3 + sirv: 3.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(@vitest/ui@3.1.3)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2) + + '@vitest/ui@3.2.4(vitest@3.2.4)': + dependencies: + '@vitest/utils': 3.2.4 + fflate: 0.8.2 + flatted: 3.3.4 + pathe: 2.0.3 + sirv: 3.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(@vitest/ui@3.1.3)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2) + + '@vitest/utils@3.1.3': + dependencies: + '@vitest/pretty-format': 3.1.3 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@zombienet/orchestrator@0.0.105(@polkadot/util@14.0.1)(@types/node@25.3.5)(chokidar@3.6.0)': + dependencies: + '@polkadot/api': 14.3.1 + '@polkadot/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.1) + '@zombienet/utils': 0.0.28(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3) + JSONStream: 1.3.5 + chai: 4.5.0 + debug: 4.3.7(supports-color@8.1.1) + execa: 5.1.1 + fs-extra: 11.3.4 + jsdom: 23.2.0 + json-bigint: 1.0.0 + libp2p-crypto: 0.21.2 + minimatch: 9.0.9 + mocha: 10.8.2 + napi-maybe-compressed-blob: 0.0.11 + peer-id: 0.16.0 + tmp-promise: 3.0.3 + typescript: 5.8.3 + yaml: 2.8.2 + transitivePeerDependencies: + - '@polkadot/util' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - canvas + - chokidar + - supports-color + - utf-8-validate + + '@zombienet/orchestrator@0.0.113(@polkadot/util@14.0.1)(@types/node@25.3.5)(chokidar@3.6.0)': + dependencies: + '@polkadot/api': 14.3.1 + '@polkadot/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.1) + '@zombienet/utils': 0.0.30(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3) + JSONStream: 1.3.5 + chai: 4.5.0 + debug: 4.4.3 + execa: 5.1.1 + fs-extra: 11.3.4 + jsdom: 23.2.0 + json-bigint: 1.0.0 + libp2p-crypto: 0.21.2 + minimatch: 9.0.9 + mocha: 10.8.2 + napi-maybe-compressed-blob: 0.0.11 + peer-id: 0.16.0 + tmp-promise: 3.0.3 + typescript: 5.8.3 + yaml: 2.8.2 + transitivePeerDependencies: + - '@polkadot/util' + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - canvas + - chokidar + - supports-color + - utf-8-validate + + '@zombienet/utils@0.0.28(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3)': + dependencies: + cli-table3: 0.6.5 + debug: 4.3.7(supports-color@8.1.1) + mocha: 10.8.2 + nunjucks: 3.2.4(chokidar@3.6.0) + toml: https://codeload.github.com/pepoviola/toml-node/tar.gz/5e17114f1af5b5b70e4f2ec10cd007623c928988 + ts-node: 10.9.2(@types/node@25.3.5)(typescript@5.8.3) + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - chokidar + - supports-color + - typescript + + '@zombienet/utils@0.0.30(@types/node@24.12.0)(chokidar@3.6.0)(typescript@5.8.3)': + dependencies: + cli-table3: 0.6.5 + debug: 4.4.3 + mocha: 10.8.2 + nunjucks: 3.2.4(chokidar@3.6.0) + toml: https://codeload.github.com/pepoviola/toml-node/tar.gz/5e17114f1af5b5b70e4f2ec10cd007623c928988 + ts-node: 10.9.2(@types/node@24.12.0)(typescript@5.8.3) + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - chokidar + - supports-color + - typescript + + '@zombienet/utils@0.0.30(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3)': + dependencies: + cli-table3: 0.6.5 + debug: 4.4.3 + mocha: 10.8.2 + nunjucks: 3.2.4(chokidar@3.6.0) + toml: https://codeload.github.com/pepoviola/toml-node/tar.gz/5e17114f1af5b5b70e4f2ec10cd007623c928988 + ts-node: 10.9.2(@types/node@25.3.5)(typescript@5.8.3) + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - chokidar + - supports-color + - typescript + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + a-sync-waterfall@1.0.1: {} + + abbrev@1.1.1: + optional: true + + abitype@0.7.1(typescript@5.8.3)(zod@3.25.76): + dependencies: + typescript: 5.8.3 + optionalDependencies: + zod: 3.25.76 + + abitype@1.1.0(typescript@5.8.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.8.3 + zod: 3.25.76 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + aes-js@4.0.0-beta.5: {} + + agent-base@6.0.2: + dependencies: + debug: 4.3.7(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + optional: true + + agent-base@7.1.4: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + optional: true + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + optional: true + + ansi-colors@4.1.3: {} + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + ansis@4.2.0: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + app-root-path@3.1.0: {} + + aproba@2.1.0: + optional: true + + are-we-there-yet@3.0.1: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + optional: true + + arg@4.1.3: {} + + argparse@2.0.1: {} + + arkregex@0.0.4: + dependencies: + '@ark/util': 0.56.0 + + asap@2.0.6: {} + + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assertion-error@1.1.0: {} + + assertion-error@2.0.1: {} + + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + auto-bind@5.0.1: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios@1.13.6(debug@4.3.7): + dependencies: + follow-redirects: 1.15.11(debug@4.3.7) + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + before-after-hook@4.0.0: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + + bignumber.js@9.3.1: {} + + binary-extensions@2.3.0: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bn.js@5.2.3: {} + + boolean@3.2.0: {} + + bottleneck@2.19.5: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + optional: true + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browser-stdout@1.3.1: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buildcheck@0.0.7: + optional: true + + bundle-require@5.1.0(esbuild@0.27.3): + dependencies: + esbuild: 0.27.3 + load-tsconfig: 0.2.5 + + cac@6.7.14: {} + + cacache@15.3.0: + dependencies: + '@npmcli/fs': 1.1.1 + '@npmcli/move-file': 1.1.2 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 7.2.3 + infer-owner: 1.0.4 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 8.0.1 + tar: 6.2.1 + unique-filename: 1.1.1 + transitivePeerDependencies: + - bluebird + optional: true + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + camelcase@6.3.0: {} + + cfonts@3.3.1: + dependencies: + supports-color: 8.1.1 + window-size: 1.1.1 + + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + chardet@2.1.1: {} + + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + + check-error@2.1.3: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@1.1.4: {} + + chownr@2.0.0: {} + + class-is@1.1.0: {} + + clean-stack@2.2.0: + optional: true + + clear@0.1.0: {} + + cli-boxes@3.0.0: {} + + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-progress@3.12.0: + dependencies: + string-width: 4.2.3 + + cli-spinners@3.4.0: {} + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.0 + + cli-width@4.1.0: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + code-excerpt@4.0.0: + dependencies: + convert-to-spaces: 2.0.1 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-support@1.1.3: + optional: true + + colorette@2.0.20: {} + + colors@1.4.0: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comlink@4.4.2: {} + + command-exists@1.2.9: {} + + commander@14.0.3: {} + + commander@4.1.1: {} + + commander@5.1.0: {} + + commander@8.3.0: {} + + concat-map@0.0.1: + optional: true + + confbox@0.1.8: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + connected-domain@1.0.0: {} + + consola@3.4.2: {} + + console-control-strings@1.1.0: + optional: true + + convert-to-spaces@2.0.1: {} + + cpu-features@0.0.10: + dependencies: + buildcheck: 0.0.7 + nan: 2.25.0 + optional: true + + crc-32@1.2.2: {} + + create-require@1.1.1: {} + + cross-fetch@4.1.0(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + csstype@3.2.3: {} + + data-uri-to-buffer@4.0.1: {} + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + dateformat@4.6.3: {} + + dayjs@1.11.19: {} + + debug@4.3.7(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@4.0.0: {} + + decimal.js@10.6.0: {} + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + dedent@1.7.2: {} + + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + + deep-eql@5.0.2: {} + + deep-extend@0.6.0: {} + + deepmerge-ts@7.1.5: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + define-property@1.0.0: + dependencies: + is-descriptor: 1.0.3 + + delayed-stream@1.0.0: {} + + delegates@1.0.0: + optional: true + + detect-indent@7.0.2: {} + + detect-libc@2.1.2: {} + + detect-node@2.1.0: {} + + diff-match-patch@1.0.5: {} + + diff@4.0.4: {} + + diff@5.2.2: {} + + docker-modem@5.0.6: + dependencies: + debug: 4.3.7(supports-color@8.1.1) + readable-stream: 3.6.2 + split-ca: 1.0.1 + ssh2: 1.17.0 + transitivePeerDependencies: + - supports-color + + dockerode@4.0.9: + dependencies: + '@balena/dockerignore': 1.0.2 + '@grpc/grpc-js': 1.14.3 + '@grpc/proto-loader': 0.7.15 + docker-modem: 5.0.6 + protobufjs: 7.5.4 + tar-fs: 2.1.4 + uuid: 10.0.0 + transitivePeerDependencies: + - supports-color + + dotenv@16.6.1: {} + + dotenv@17.2.3: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + effect@3.19.19: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + entities@6.0.1: {} + + env-paths@2.2.1: + optional: true + + environment@1.1.0: {} + + err-code@2.0.3: + optional: true + + err-code@3.0.1: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-toolkit@1.45.1: {} + + es6-error@4.1.1: {} + + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 + + escalade@3.2.0: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + ethereum-cryptography@2.2.1: + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + + ethereum-cryptography@3.1.0: + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + + ethers@6.16.0: + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + eventemitter3@5.0.1: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@9.6.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + + expand-template@2.0.3: {} + + expect-type@1.3.0: {} + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + + fast-content-type-parse@3.0.0: {} + + fast-copy@4.0.2: {} + + fast-safe-stringify@2.1.1: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + fflate@0.8.2: {} + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-uri-to-path@1.0.0: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-my-way-ts@0.1.6: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.1 + rollup: 4.59.0 + + flat@5.0.2: {} + + flatted@3.3.4: {} + + follow-redirects@1.15.11(debug@4.3.7): + optionalDependencies: + debug: 4.3.7(supports-color@8.1.1) + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + fs-constants@1.0.0: {} + + fs-extra@11.3.4: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.promises.exists@1.1.4: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gauge@4.0.4: + dependencies: + aproba: 2.1.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + optional: true + + generator-function@2.0.1: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.5.0: {} + + get-func-name@2.0.2: {} + + get-intrinsic@1.3.0: + 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: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + get-tsconfig@4.13.6: + dependencies: + resolve-pkg-maps: 1.0.0 + + github-from-package@0.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + optional: true + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.9 + once: 1.4.0 + + global-agent@3.0.0: + dependencies: + boolean: 3.2.0 + es6-error: 4.1.1 + matcher: 3.0.0 + roarr: 2.15.4 + semver: 7.7.4 + serialize-error: 7.0.1 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + has-unicode@2.0.1: + optional: true + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + help-me@5.0.0: {} + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-cache-semantics@4.2.0: + optional: true + + http-proxy-agent@4.0.1: + dependencies: + '@tootallnate/once': 1.1.2 + agent-base: 6.0.2 + debug: 4.3.7(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + optional: true + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.3.7(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.7(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + optional: true + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.3.7(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + human-signals@8.0.1: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + optional: true + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + idb@8.0.3: {} + + ieee754@1.2.1: {} + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: + optional: true + + indent-string@5.0.0: {} + + index-to-position@1.2.0: {} + + infer-owner@1.0.4: + optional: true + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ink@6.8.0(@types/react@19.2.7)(react@19.2.4): + dependencies: + '@alcalzone/ansi-tokenize': 0.2.5 + ansi-escapes: 7.3.0 + ansi-styles: 6.2.3 + auto-bind: 5.0.1 + chalk: 5.6.2 + cli-boxes: 3.0.0 + cli-cursor: 4.0.0 + cli-truncate: 5.2.0 + code-excerpt: 4.0.0 + es-toolkit: 1.45.1 + indent-string: 5.0.0 + is-in-ci: 2.0.0 + patch-console: 2.0.0 + react: 19.2.4 + react-reconciler: 0.33.0(react@19.2.4) + scheduler: 0.27.0 + signal-exit: 3.0.7 + slice-ansi: 8.0.0 + stack-utils: 2.0.6 + string-width: 8.2.0 + terminal-size: 4.0.1 + type-fest: 5.4.4 + widest-line: 6.0.0 + wrap-ansi: 9.0.2 + ws: 8.19.0 + yoga-layout: 3.2.1 + optionalDependencies: + '@types/react': 19.2.7 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + ip-address@10.1.0: + optional: true + + is-accessor-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-buffer@1.1.6: {} + + is-callable@1.2.7: {} + + is-data-descriptor@1.0.1: + dependencies: + hasown: 2.0.2 + + is-descriptor@1.0.3: + dependencies: + is-accessor-descriptor: 1.0.1 + is-data-descriptor: 1.0.1 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-in-ci@2.0.0: {} + + is-interactive@2.0.0: {} + + is-lambda@1.0.1: + optional: true + + is-number@3.0.0: + dependencies: + kind-of: 3.2.2 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-plain-obj@4.1.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-stream@2.0.1: {} + + is-stream@4.0.1: {} + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@2.1.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iso-random-stream@2.0.2: + dependencies: + events: 3.3.0 + readable-stream: 3.6.2 + + isomorphic-ws@5.0.0(ws@8.19.0): + dependencies: + ws: 8.19.0 + + isows@1.0.7(ws@8.18.3): + dependencies: + ws: 8.18.3 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + joycon@3.1.1: {} + + js-sha3@0.8.0: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdom@23.2.0: + dependencies: + '@asamuzakjp/dom-selector': 2.0.2 + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + form-data: 4.0.5 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 7.3.0 + rrweb-cssom: 0.6.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.19.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-stringify-safe@5.0.1: {} + + json-with-bigint@3.5.7: {} + + jsonc-parser@3.3.1: {} + + jsondiffpatch@0.5.0: + dependencies: + chalk: 3.0.0 + diff-match-patch: 1.0.5 + + jsondiffpatch@0.7.3: + dependencies: + '@dmsnell/diff-match-patch': 1.1.0 + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + + kubernetes-types@1.30.0: {} + + libp2p-crypto@0.21.2: + dependencies: + '@noble/ed25519': 1.7.5 + '@noble/secp256k1': 1.7.2 + err-code: 3.0.1 + iso-random-stream: 2.0.2 + multiformats: 9.9.0 + node-forge: 1.3.3 + protobufjs: 6.11.4 + uint8arrays: 3.1.1 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.camelcase@4.3.0: {} + + lodash@4.17.23: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + + long@4.0.0: {} + + long@5.3.2: {} + + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + + loupe@3.2.1: {} + + lru-cache@10.4.3: {} + + lru-cache@11.2.6: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + optional: true + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-error@1.3.6: {} + + make-fetch-happen@9.1.0: + dependencies: + agentkeepalive: 4.6.0 + cacache: 15.3.0 + http-cache-semantics: 4.2.0 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 1.4.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.4 + promise-retry: 2.0.1 + socks-proxy-agent: 6.2.1 + ssri: 8.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + optional: true + + matcher@3.0.0: + dependencies: + escape-string-regexp: 4.0.0 + + math-intrinsics@1.1.0: {} + + mdn-data@2.0.30: {} + + memorystream@0.3.1: {} + + merge-stream@2.0.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@3.0.0: {} + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + mimic-response@3.1.0: {} + + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.1 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + optional: true + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass-collect@1.0.2: + dependencies: + minipass: 3.3.6 + optional: true + + minipass-fetch@1.4.1: + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 + optional: true + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + optional: true + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + optional: true + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + optional: true + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.3: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp-classic@0.5.3: {} + + mkdirp@1.0.4: {} + + mlkem@2.7.0: {} + + mlly@1.8.1: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.3 + + mocha@10.8.2: + dependencies: + ansi-colors: 4.1.3 + browser-stdout: 1.3.1 + chokidar: 3.6.0 + debug: 4.3.7(supports-color@8.1.1) + diff: 5.2.2 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 8.1.0 + he: 1.2.0 + js-yaml: 4.1.1 + log-symbols: 4.1.0 + minimatch: 5.1.9 + ms: 2.1.3 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.5.1 + yargs: 16.2.0 + yargs-parser: 20.2.9 + yargs-unparser: 2.0.0 + + mock-socket@9.3.1: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.8: + optionalDependencies: + msgpackr-extract: 3.0.3 + + multiformats@9.9.0: {} + + multipasta@0.2.7: {} + + mute-stream@2.0.0: {} + + mute-stream@3.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nan@2.25.0: + optional: true + + nanoid@3.3.11: {} + + napi-build-utils@2.0.0: {} + + napi-maybe-compressed-blob-darwin-arm64@0.0.11: + optional: true + + napi-maybe-compressed-blob-darwin-x64@0.0.11: + optional: true + + napi-maybe-compressed-blob-linux-arm64-gnu@0.0.11: + optional: true + + napi-maybe-compressed-blob-linux-x64-gnu@0.0.11: + optional: true + + napi-maybe-compressed-blob@0.0.11: + optionalDependencies: + napi-maybe-compressed-blob-darwin-arm64: 0.0.11 + napi-maybe-compressed-blob-darwin-x64: 0.0.11 + napi-maybe-compressed-blob-linux-arm64-gnu: 0.0.11 + napi-maybe-compressed-blob-linux-x64-gnu: 0.0.11 + + negotiator@0.6.4: + optional: true + + nock@13.5.6: + dependencies: + debug: 4.3.7(supports-color@8.1.1) + json-stringify-safe: 5.0.1 + propagate: 2.0.1 + transitivePeerDependencies: + - supports-color + + node-abi@3.87.0: + dependencies: + semver: 7.7.4 + + node-addon-api@7.1.1: {} + + node-domexception@1.0.0: {} + + node-fetch@2.7.0(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-forge@1.3.3: {} + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.1.2 + optional: true + + node-gyp@8.4.1: + dependencies: + env-paths: 2.2.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + make-fetch-happen: 9.1.0 + nopt: 5.0.0 + npmlog: 6.0.2 + rimraf: 3.0.2 + semver: 7.7.4 + tar: 6.2.1 + which: 2.0.2 + transitivePeerDependencies: + - bluebird + - supports-color + optional: true + + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + optional: true + + normalize-package-data@6.0.2: + dependencies: + hosted-git-info: 7.0.2 + semver: 7.7.4 + validate-npm-package-license: 3.0.4 + + normalize-path@3.0.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + npmlog@6.0.2: + dependencies: + are-we-there-yet: 3.0.1 + console-control-strings: 1.1.0 + gauge: 4.0.4 + set-blocking: 2.0.0 + optional: true + + nunjucks@3.2.4(chokidar@3.6.0): + dependencies: + a-sync-waterfall: 1.0.1 + asap: 2.0.6 + commander: 5.1.0 + optionalDependencies: + chokidar: 3.6.0 + + object-assign@4.1.1: {} + + object-keys@1.1.1: {} + + on-exit-leak-free@2.1.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + ora@9.3.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.1 + string-width: 8.2.0 + + os-tmpdir@1.0.2: {} + + ox@0.9.6(typescript@5.8.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.8.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - zod + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + optional: true + + package-json-from-dist@1.0.1: {} + + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.29.0 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + parse-ms@4.0.0: {} + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + patch-console@2.0.0: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: + optional: true + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + pathe@2.0.3: {} + + pathval@1.1.1: {} + + pathval@2.0.1: {} + + peer-id@0.16.0: + dependencies: + class-is: 1.1.0 + libp2p-crypto: 0.21.2 + multiformats: 9.9.0 + protobufjs: 6.11.4 + uint8arrays: 3.1.1 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-abstract-transport@3.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.1.3: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 4.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pump: 3.0.4 + secure-json-parse: 4.1.0 + sonic-boom: 4.2.1 + strip-json-comments: 5.0.3 + + pino-std-serializers@7.1.0: {} + + pino@10.3.1: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 4.0.0 + + pino@9.14.0: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 3.1.0 + + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.1 + pathe: 2.0.3 + + pnpm@10.32.1: {} + + polkadot-api@1.19.2(postcss@8.5.8)(rxjs@7.8.2)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@polkadot-api/cli': 0.15.2(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.2) + '@polkadot-api/ink-contracts': 0.4.0 + '@polkadot-api/json-rpc-provider': 0.0.4 + '@polkadot-api/known-chains': 0.9.11 + '@polkadot-api/logs-provider': 0.0.6 + '@polkadot-api/metadata-builders': 0.13.5 + '@polkadot-api/metadata-compatibility': 0.3.6 + '@polkadot-api/observable-client': 0.15.1(rxjs@7.8.2) + '@polkadot-api/pjs-signer': 0.6.15 + '@polkadot-api/polkadot-sdk-compat': 2.3.3 + '@polkadot-api/polkadot-signer': 0.1.6 + '@polkadot-api/signer': 0.2.9 + '@polkadot-api/sm-provider': 0.1.11(@polkadot-api/smoldot@0.3.14) + '@polkadot-api/smoldot': 0.3.14 + '@polkadot-api/substrate-bindings': 0.16.3 + '@polkadot-api/substrate-client': 0.4.7 + '@polkadot-api/utils': 0.2.0 + '@polkadot-api/ws-provider': 0.6.2 + '@rx-state/core': 0.1.4(rxjs@7.8.2) + rxjs: 7.8.2 + transitivePeerDependencies: + - '@microsoft/api-extractor' + - '@swc/core' + - bufferutil + - jiti + - postcss + - supports-color + - tsx + - utf-8-validate + - yaml + + possible-typed-array-names@1.1.0: {} + + postcss-load-config@6.0.1(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.8 + tsx: 4.21.0 + yaml: 2.8.2 + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.87.0 + pump: 3.0.4 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.4 + tunnel-agent: 0.6.0 + + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + process-warning@5.0.0: {} + + promise-inflight@1.0.1: + optional: true + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + optional: true + + propagate@2.0.1: {} + + proto-list@1.2.4: {} + + protobufjs@6.11.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.2 + '@types/node': 25.3.5 + long: 4.0.0 + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 25.3.5 + long: 5.3.2 + + proxy-from-env@1.1.0: {} + + ps-node@0.1.6: + dependencies: + table-parser: 0.1.3 + + psl@1.15.0: + dependencies: + punycode: 2.3.1 + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + pure-rand@6.1.0: {} + + querystringify@2.2.0: {} + + quick-format-unescaped@4.0.4: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-reconciler@0.33.0(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react@19.2.4: {} + + read-pkg@9.0.1: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 6.0.2 + parse-json: 8.3.0 + type-fest: 4.41.0 + unicorn-magic: 0.1.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + real-require@0.2.0: {} + + reflect-metadata@0.2.2: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + retry@0.12.0: + optional: true + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + optional: true + + rlp@3.0.0: {} + + roarr@2.15.4: + dependencies: + boolean: 3.2.0 + detect-node: 2.1.0 + globalthis: 1.0.4 + json-stringify-safe: 5.0.1 + semver-compare: 1.0.0 + sprintf-js: 1.1.3 + + rollup@4.59.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 + fsevents: 2.3.3 + + rrweb-cssom@0.6.0: {} + + rrweb-cssom@0.8.0: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.2.1: {} + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scale-ts@1.6.1: {} + + scheduler@0.27.0: {} + + secure-json-parse@4.1.0: {} + + semver-compare@1.0.0: {} + + semver@5.7.2: {} + + semver@7.7.4: {} + + serialize-error@7.0.1: + dependencies: + type-fest: 0.13.1 + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + set-blocking@2.0.0: + optional: true + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + setimmediate@1.0.5: {} + + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smart-buffer@4.2.0: + optional: true + + smoldot@2.0.26: + dependencies: + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + optional: true + + smoldot@2.0.39: + dependencies: + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + socks-proxy-agent@6.2.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.7(supports-color@8.1.1) + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + optional: true + + socks@2.8.7: + dependencies: + ip-address: 10.1.0 + smart-buffer: 4.2.0 + optional: true + + solc@0.8.21(debug@4.3.7): + dependencies: + command-exists: 1.2.9 + commander: 8.3.0 + follow-redirects: 1.15.11(debug@4.3.7) + js-sha3: 0.8.0 + memorystream: 0.3.1 + semver: 5.7.2 + tmp: 0.0.33 + transitivePeerDependencies: + - debug + + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + + sort-keys@5.1.0: + dependencies: + is-plain-obj: 4.1.0 + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.23 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.23 + + spdx-license-ids@3.0.23: {} + + split-ca@1.0.1: {} + + split2@4.2.0: {} + + sprintf-js@1.1.3: {} + + sql-highlight@6.1.0: {} + + sqlite3@5.1.7: + dependencies: + bindings: 1.5.0 + node-addon-api: 7.1.1 + prebuild-install: 7.1.3 + tar: 6.2.1 + optionalDependencies: + node-gyp: 8.4.1 + transitivePeerDependencies: + - bluebird + - supports-color + + ssh2@1.17.0: + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.10 + nan: 2.25.0 + + ssri@8.0.1: + dependencies: + minipass: 3.3.6 + optional: true + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + stdin-discarder@0.3.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string-width@8.2.0: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-final-newline@2.0.0: {} + + strip-final-newline@4.0.0: {} + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + strip-json-comments@5.0.3: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.15 + ts-interface-checker: 0.1.13 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + symbol-tree@3.2.4: {} + + table-parser@0.1.3: + dependencies: + connected-domain: 1.0.0 + + tagged-tag@1.0.0: {} + + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.4 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + terminal-size@4.0.1: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + thread-stream@4.0.0: + dependencies: + real-require: 0.2.0 + + through@2.3.8: {} + + tiny-invariant@1.3.3: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + tmp-promise@3.0.3: + dependencies: + tmp: 0.2.5 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tmp@0.2.5: {} + + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toml@3.0.0: {} + + toml@https://codeload.github.com/pepoviola/toml-node/tar.gz/5e17114f1af5b5b70e4f2ec10cd007623c928988: {} + + totalist@3.0.1: {} + + tough-cookie@4.1.4: + dependencies: + psl: 1.15.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + + tr46@0.0.3: {} + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + tree-kill@1.2.2: {} + + ts-interface-checker@0.1.13: {} + + ts-node@10.9.2(@types/node@24.12.0)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 24.12.0 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 25.3.5 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tsc-prog@2.3.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.7.0: {} + + tslib@2.8.1: {} + + tsup@8.5.1(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): + dependencies: + bundle-require: 5.1.0(esbuild@0.27.3) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.3 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.8)(tsx@4.21.0)(yaml@2.8.2) + resolve-from: 5.0.0 + rollup: 4.59.0 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.8 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + + tsx@4.21.0: + dependencies: + esbuild: 0.27.3 + get-tsconfig: 4.13.6 + optionalDependencies: + fsevents: 2.3.3 + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + tweetnacl@0.14.5: {} + + type-detect@4.1.0: {} + + type-fest@0.13.1: {} + + type-fest@4.41.0: {} + + type-fest@5.4.4: + dependencies: + tagged-tag: 1.0.0 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typeorm@0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3)): + dependencies: + '@sqltools/formatter': 1.2.5 + ansis: 4.2.0 + app-root-path: 3.1.0 + buffer: 6.0.3 + dayjs: 1.11.19 + debug: 4.4.3 + dedent: 1.7.2 + dotenv: 16.6.1 + glob: 10.5.0 + reflect-metadata: 0.2.2 + sha.js: 2.4.12 + sql-highlight: 6.1.0 + tslib: 2.8.1 + uuid: 11.1.0 + yargs: 17.7.2 + optionalDependencies: + sqlite3: 5.1.7 + ts-node: 10.9.2(@types/node@25.3.5)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + typescript@5.8.3: {} + + typescript@5.9.3: {} + + ufo@1.6.3: {} + + uint8arrays@3.1.1: + dependencies: + multiformats: 9.9.0 + + undici-types@6.19.8: {} + + undici-types@7.16.0: {} + + undici-types@7.18.2: {} + + undici@7.22.0: {} + + unicorn-magic@0.1.0: {} + + unicorn-magic@0.3.0: {} + + unique-filename@1.1.1: + dependencies: + unique-slug: 2.0.2 + optional: true + + unique-slug@2.0.2: + dependencies: + imurmurhash: 0.1.4 + optional: true + + universal-user-agent@7.0.3: {} + + universalify@0.2.0: {} + + universalify@2.0.1: {} + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + util-deprecate@1.0.2: {} + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.20 + + uuid@10.0.0: {} + + uuid@11.1.0: {} + + v8-compile-cache-lib@3.0.1: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + viem@2.38.0(typescript@5.8.3)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.8.3)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3) + ox: 0.9.6(typescript@5.8.3)(zod@3.25.76) + ws: 8.18.3 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.41.2(typescript@5.8.3)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.8.3)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3) + ox: 0.9.6(typescript@5.8.3)(zod@3.25.76) + ws: 8.18.3 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + vite-node@3.2.4(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.1(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-node@3.2.4(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@7.3.1(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.12.0 + fsevents: 2.3.3 + tsx: 4.21.0 + yaml: 2.8.2 + + vite@7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.3.5 + fsevents: 2.3.3 + tsx: 4.21.0 + yaml: 2.8.2 + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 24.12.0 + '@vitest/ui': 3.2.4(vitest@3.2.4) + jsdom: 23.2.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(@vitest/ui@3.1.3)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 25.3.5 + '@vitest/ui': 3.1.3(vitest@3.2.4) + jsdom: 23.2.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.3.5)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 25.3.5 + '@vitest/ui': 3.2.4(vitest@3.2.4) + jsdom: 23.2.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + web-streams-polyfill@3.3.3: {} + + web3-core@4.7.1(encoding@0.1.13): + dependencies: + web3-errors: 1.3.1 + web3-eth-accounts: 4.3.1 + web3-eth-iban: 4.0.7 + web3-providers-http: 4.2.0(encoding@0.1.13) + web3-providers-ws: 4.0.8 + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + optionalDependencies: + web3-providers-ipc: 4.0.7 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + web3-errors@1.3.1: + dependencies: + web3-types: 1.10.0 + + web3-eth-abi@4.4.1(typescript@5.8.3)(zod@3.25.76): + dependencies: + abitype: 0.7.1(typescript@5.8.3)(zod@3.25.76) + web3-errors: 1.3.1 + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - typescript + - zod + + web3-eth-accounts@4.3.1: + dependencies: + '@ethereumjs/rlp': 4.0.1 + crc-32: 1.2.2 + ethereum-cryptography: 2.2.1 + web3-errors: 1.3.1 + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + + web3-eth-contract@4.7.2(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76): + dependencies: + '@ethereumjs/rlp': 5.0.2 + web3-core: 4.7.1(encoding@0.1.13) + web3-errors: 1.3.1 + web3-eth: 4.11.1(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-abi: 4.4.1(typescript@5.8.3)(zod@3.25.76) + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + web3-eth-ens@4.4.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + web3-core: 4.7.1(encoding@0.1.13) + web3-errors: 1.3.1 + web3-eth: 4.11.1(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-contract: 4.7.2(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-net: 4.1.0(encoding@0.1.13) + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + web3-eth-iban@4.0.7: + dependencies: + web3-errors: 1.3.1 + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + + web3-eth-personal@4.1.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76): + dependencies: + web3-core: 4.7.1(encoding@0.1.13) + web3-eth: 4.11.1(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-rpc-methods: 1.3.0(encoding@0.1.13) + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + web3-eth@4.11.1(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76): + dependencies: + setimmediate: 1.0.5 + web3-core: 4.7.1(encoding@0.1.13) + web3-errors: 1.3.1 + web3-eth-abi: 4.4.1(typescript@5.8.3)(zod@3.25.76) + web3-eth-accounts: 4.3.1 + web3-net: 4.1.0(encoding@0.1.13) + web3-providers-ws: 4.0.8 + web3-rpc-methods: 1.3.0(encoding@0.1.13) + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + web3-net@4.1.0(encoding@0.1.13): + dependencies: + web3-core: 4.7.1(encoding@0.1.13) + web3-rpc-methods: 1.3.0(encoding@0.1.13) + web3-types: 1.10.0 + web3-utils: 4.3.3 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + web3-providers-http@4.2.0(encoding@0.1.13): + dependencies: + cross-fetch: 4.1.0(encoding@0.1.13) + web3-errors: 1.3.1 + web3-types: 1.10.0 + web3-utils: 4.3.3 + transitivePeerDependencies: + - encoding + + web3-providers-ipc@4.0.7: + dependencies: + web3-errors: 1.3.1 + web3-types: 1.10.0 + web3-utils: 4.3.3 + optional: true + + web3-providers-ws@4.0.8: + dependencies: + '@types/ws': 8.5.3 + isomorphic-ws: 5.0.0(ws@8.19.0) + web3-errors: 1.3.1 + web3-types: 1.10.0 + web3-utils: 4.3.3 + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + web3-rpc-methods@1.3.0(encoding@0.1.13): + dependencies: + web3-core: 4.7.1(encoding@0.1.13) + web3-types: 1.10.0 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + web3-rpc-providers@1.0.0-rc.4(encoding@0.1.13): + dependencies: + web3-errors: 1.3.1 + web3-providers-http: 4.2.0(encoding@0.1.13) + web3-providers-ws: 4.0.8 + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + web3-types@1.10.0: {} + + web3-utils@4.3.3: + dependencies: + ethereum-cryptography: 2.2.1 + eventemitter3: 5.0.4 + web3-errors: 1.3.1 + web3-types: 1.10.0 + web3-validator: 2.0.6 + + web3-validator@2.0.6: + dependencies: + ethereum-cryptography: 2.2.1 + util: 0.12.5 + web3-errors: 1.3.1 + web3-types: 1.10.0 + zod: 3.25.76 + + web3@4.15.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76): + dependencies: + web3-core: 4.7.1(encoding@0.1.13) + web3-errors: 1.3.1 + web3-eth: 4.11.1(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-abi: 4.4.1(typescript@5.8.3)(zod@3.25.76) + web3-eth-accounts: 4.3.1 + web3-eth-contract: 4.7.2(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-ens: 4.4.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-iban: 4.0.7 + web3-eth-personal: 4.1.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-net: 4.1.0(encoding@0.1.13) + web3-providers-http: 4.2.0(encoding@0.1.13) + web3-providers-ws: 4.0.8 + web3-rpc-methods: 1.3.0(encoding@0.1.13) + web3-rpc-providers: 1.0.0-rc.4(encoding@0.1.13) + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + web3@4.16.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76): + dependencies: + web3-core: 4.7.1(encoding@0.1.13) + web3-errors: 1.3.1 + web3-eth: 4.11.1(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-abi: 4.4.1(typescript@5.8.3)(zod@3.25.76) + web3-eth-accounts: 4.3.1 + web3-eth-contract: 4.7.2(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-ens: 4.4.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-eth-iban: 4.0.7 + web3-eth-personal: 4.1.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) + web3-net: 4.1.0(encoding@0.1.13) + web3-providers-http: 4.2.0(encoding@0.1.13) + web3-providers-ws: 4.0.8 + web3-rpc-methods: 1.3.0(encoding@0.1.13) + web3-rpc-providers: 1.0.0-rc.4(encoding@0.1.13) + web3-types: 1.10.0 + web3-utils: 4.3.3 + web3-validator: 2.0.6 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-typed-array@1.1.20: + 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 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + optional: true + + widest-line@6.0.0: + dependencies: + string-width: 8.2.0 + + window-size@1.1.1: + dependencies: + define-property: 1.0.0 + is-number: 3.0.0 + + workerpool@6.5.1: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + write-json-file@6.0.0: + dependencies: + detect-indent: 7.0.2 + is-plain-obj: 4.1.0 + sort-keys: 5.1.0 + write-file-atomic: 5.0.1 + + write-package@7.2.0: + dependencies: + deepmerge-ts: 7.1.5 + read-pkg: 9.0.1 + sort-keys: 5.1.0 + type-fest: 4.41.0 + write-json-file: 6.0.0 + + ws@8.17.1: {} + + ws@8.18.3: {} + + ws@8.19.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yaml@2.8.2: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs-parser@22.0.0: {} + + yargs-unparser@2.0.0: + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yoctocolors-cjs@2.1.3: {} + + yoctocolors@2.1.2: {} + + yoga-layout@3.2.1: {} + + zod@3.25.76: {} diff --git a/ts-tests/pnpm-workspace.yaml b/ts-tests/pnpm-workspace.yaml new file mode 100644 index 0000000000..856299a3ed --- /dev/null +++ b/ts-tests/pnpm-workspace.yaml @@ -0,0 +1,15 @@ +packages: + - "**" + +onlyBuiltDependencies: + - '@biomejs/biome' + - '@chainsafe/blst' + - '@parcel/watcher' + - bigint-buffer + - classic-level + - cpu-features + - esbuild + - msgpackr-extract + - protobufjs + - sqlite3 + - ssh2 diff --git a/ts-tests/scripts/build-spec.sh b/ts-tests/scripts/build-spec.sh new file mode 100755 index 0000000000..8ef4e40b96 --- /dev/null +++ b/ts-tests/scripts/build-spec.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -e + +cd $(dirname $0)/.. + +mkdir -p specs + +../target/release/node-subtensor build-spec --disable-default-bootnode --raw --chain local > specs/chain-spec.json \ No newline at end of file diff --git a/ts-tests/scripts/generate-types.sh b/ts-tests/scripts/generate-types.sh new file mode 100755 index 0000000000..eadb28324b --- /dev/null +++ b/ts-tests/scripts/generate-types.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# (Re)generate polkadot-api type descriptors using a running node. +# Checks that the node binary exists before running. +# Generates types only if they are missing or empty. +# +# Usage: +# ./generate-types.sh +# +set -e + +BASE_DIR="./tmp" +mkdir -p "$BASE_DIR" + +BINARY="${BINARY_PATH:-../target/release/node-subtensor}" +NODE_LOG="${BASE_DIR}/node.log" + +if [ ! -f "$BINARY" ]; then + echo "ERROR: Node binary not found at $BINARY" + echo "Please build it first, e.g.: pnpm build-node:debug" + exit 1 +fi + +DESCRIPTORS_DIR="./.papi/descriptors" +GENERATE_TYPES=false +if [ ! -d "$DESCRIPTORS_DIR" ] || [ -z "$(ls -A "$DESCRIPTORS_DIR" 2>/dev/null)" ]; then + echo "==> Type descriptors not found or empty, will generate..." + GENERATE_TYPES=true +else + echo "==> Type descriptors already exist, skipping generation." +fi + +if [ "$GENERATE_TYPES" = true ]; then + echo "==> Starting dev node (logs at $NODE_LOG)..." + "$BINARY" --one --dev &>"$NODE_LOG" & + NODE_PID=$! + trap "kill $NODE_PID 2>/dev/null; wait $NODE_PID 2>/dev/null; exit 0" EXIT + + TIMEOUT=60 + ELAPSED=0 + echo "==> Waiting for node to be ready (timeout: ${TIMEOUT}s)..." + until curl -sf -o /dev/null \ + -H "Content-Type: application/json" \ + -d '{"id":1,"jsonrpc":"2.0","method":"system_health","params":[]}' \ + http://localhost:9944; do + sleep 1 + ELAPSED=$((ELAPSED + 1)) + if [ "$ELAPSED" -ge "$TIMEOUT" ]; then + echo "ERROR: Node failed to start within ${TIMEOUT}s. Check $NODE_LOG" + exit 1 + fi + done + + echo "==> Generating papi types..." + pnpm generate-types + + echo "==> Done generating types." + exit 0 +else + echo "==> Types are up-to-date, nothing to do." +fi \ No newline at end of file diff --git a/ts-tests/suites/dev/subtensor/staking/test-add-staking.ts b/ts-tests/suites/dev/subtensor/staking/test-add-staking.ts new file mode 100644 index 0000000000..8d8e13cfb6 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/staking/test-add-staking.ts @@ -0,0 +1,77 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { BN } from "@polkadot/util"; + +describeSuite({ + id: "DEV_SUB_STAKING_ADD_STAKING_01", + title: "Add staking test suite", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let netuid1: number; + + let alice: KeyringPair; + let bob: KeyringPair; + + beforeAll(() => { + polkadotJs = context.polkadotJs(); + + alice = context.keyring.alice; + bob = context.keyring.bob; + }); + + it({ + id: "T01", + title: "Add stake payable", + test: async () => { + const alice = context.keyring.alice; + const bob = context.keyring.bob; + const appFees = new BN(100_000); + + // Register network + let tx = polkadotJs.tx.subtensorModule.registerNetwork(bob.address); + await context.createBlock([await tx.signAsync(alice)]); + + let events = await polkadotJs.query.system.events(); + const event = events.filter((a) => { + return a.event.method === "NetworkAdded"; + }); + expect(event.length).to.be.equal(1); + netuid1 = event[0].event.data[0]; + + // Enabling subtokens + const tx1 = polkadotJs.tx.adminUtils.sudoSetSubtokenEnabled(netuid1, true); + await context.createBlock([await polkadotJs.tx.sudo.sudo(tx1).signAsync(alice)]); + + // Adding stake + tx = polkadotJs.tx.subtensorModule.addStake(bob.address, netuid1, 1000_000_000); + await context.createBlock([await tx.signAsync(alice)]); + + events = await polkadotJs.query.system.events(); + const stakeAddedEvent = events.filter((a) => { + return a.event.method === "StakeAdded"; + }); + + expect(stakeAddedEvent.length).to.be.equal(1); + }, + }); + + it({ + id: "T02", + title: "Remove stake payable", + test: async () => { + // Removing stake + const tx = polkadotJs.tx.subtensorModule.removeStake(bob.address, netuid1, 500_000_000); + await context.createBlock([await tx.signAsync(alice)]); + + const events = await polkadotJs.query.system.events(); + const removeAddedEvent = events.filter((a) => { + return a.event.method === "StakeRemoved"; + }); + + expect(removeAddedEvent.length).to.be.equal(1); + }, + }); + }, +}); diff --git a/ts-tests/suites/smoke/test-block-finalization.ts b/ts-tests/suites/smoke/test-block-finalization.ts new file mode 100644 index 0000000000..0f16177e71 --- /dev/null +++ b/ts-tests/suites/smoke/test-block-finalization.ts @@ -0,0 +1,32 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import { getBlockTime } from "@moonwall/util"; + +describeSuite({ + id: "S01", + title: "Smoke test - test block finalization", + foundationMethods: "read_only", + testCases: ({ it, context, log }) => { + let api: ApiPromise; + + beforeAll(() => { + api = context.polkadotJs(); + }); + + it({ + id: "C01", + title: "Blocks should be finalized", + test: async () => { + const head = await api.rpc.chain.getFinalizedHead(); + const block = await api.rpc.chain.getBlock(head); + const diff = Date.now() - getBlockTime(block); + + log(`Current head block number: ${block.block.header.number.toNumber()}`); + log(`Last finalized block was ${diff / 1000} seconds ago`); + + expect(diff).to.be.lessThanOrEqual(10 * 60 * 1000); // 10 minutes in milliseconds + expect(api.consts.system.version.specVersion.toNumber()).to.be.greaterThan(0); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_shield/00.00-basic.test.ts b/ts-tests/suites/zombienet_shield/00.00-basic.test.ts new file mode 100644 index 0000000000..54a823a9fe --- /dev/null +++ b/ts-tests/suites/zombienet_shield/00.00-basic.test.ts @@ -0,0 +1,63 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { checkRuntime, getCurrentKey, getNextKey, waitForFinalizedBlocks } from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "00.00_basic", + title: "MEV Shield — key rotation", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + + await checkRuntime(api); + + await waitForFinalizedBlocks(api, 3); + }, 120000); + + it({ + id: "T01", + title: "NextKey and CurrentKey are populated and rotate across blocks", + test: async () => { + const nextKey1 = await getNextKey(api); + expect(nextKey1).toBeDefined(); + expect(nextKey1.length).toBe(1184); // ML-KEM-768 public key + + const currentKey1 = await getCurrentKey(api); + expect(currentKey1).toBeDefined(); + expect(currentKey1.length).toBe(1184); + + await waitForFinalizedBlocks(api, 2); + + const nextKey2 = await getNextKey(api); + expect(nextKey2).toBeDefined(); + // Keys should have rotated — nextKey changes each block. + expect(nextKey2).not.toEqual(nextKey1); + + const currentKey2 = await getCurrentKey(api); + expect(currentKey2).toBeDefined(); + expect(currentKey2).not.toEqual(currentKey1); + }, + }); + + it({ + id: "T02", + title: "AuthorKeys stores per-author keys", + test: async () => { + const authorities = await api.query.Aura.Authorities.getValue(); + expect(authorities.length).toBeGreaterThan(0); + + let foundKeys = 0; + for (const authority of authorities) { + const key = await api.query.MevShield.AuthorKeys.getValue(authority); + if (key) foundKeys++; + } + + expect(foundKeys).toBeGreaterThan(0); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_shield/00.01-basic.test.ts b/ts-tests/suites/zombienet_shield/00.01-basic.test.ts new file mode 100644 index 0000000000..7801019e79 --- /dev/null +++ b/ts-tests/suites/zombienet_shield/00.01-basic.test.ts @@ -0,0 +1,225 @@ +import { expect, beforeAll, describeSuite } from "@moonwall/cli"; +import { + checkRuntime, + encryptTransaction, + getAccountNonce, + getBalance, + getNextKey, + getSignerFromKeypair, + submitEncrypted, + waitForFinalizedBlocks, +} from "../../utils"; +import { Binary } from "@polkadot-api/substrate-bindings"; +import { Keyring } from "@polkadot/keyring"; +import type { KeyringPair } from "@moonwall/util"; +import { hexToU8a } from "@polkadot/util"; +import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; +import type { PolkadotClient, TypedApi } from "polkadot-api"; + +describeSuite({ + id: "00.01_basic", + title: "MEV Shield — encrypted transactions", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let client: PolkadotClient; + let api: TypedApi<typeof subtensor>; + let alice: KeyringPair; + let bob: KeyringPair; + let charlie: KeyringPair; + + beforeAll(async () => { + client = context.papi("Node"); + api = client.getTypedApi(subtensor); + + const keyring = new Keyring({ type: "sr25519" }); + alice = keyring.addFromUri("//Alice"); + bob = keyring.addFromUri("//Bob"); + charlie = keyring.addFromUri("//Charlie"); + + await checkRuntime(api); + + await waitForFinalizedBlocks(api, 3); + }, 120000); + + it({ + id: "T01", + title: "Happy path: wrapper and inner tx are included in the same block", + test: async () => { + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 10_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + + it({ + id: "T02", + title: "Failed inner tx: wrapper succeeds but inner transfer has no effect", + test: async () => { + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + // Encrypt a transfer of more than Alice has. + // The wrapper is valid (correct key_hash, valid encryption), but the + // inner transfer should fail at dispatch with InsufficientBalance. + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 9_000_000_000_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + // The inner transfer failed, so bob's balance should not increase. + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBe(balanceBefore); + }, + }); + + it({ + id: "T03", + title: "Malformed ciphertext is rejected at pool level", + test: async () => { + const nonce = await getAccountNonce(api, alice.address); + + // 5 bytes of garbage — not valid ciphertext at all. + const garbage = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]); + + const tx = api.tx.MevShield.submit_encrypted({ + ciphertext: Binary.fromBytes(garbage), + }); + + // Pool validation rejects with FailedShieldedTxParsing (Custom code 23). + await expect( + tx.signAndSubmit(getSignerFromKeypair(alice), { nonce, mortality: { mortal: true, period: 8 } }) + ).rejects.toThrow(); + }, + }); + + it({ + id: "T04", + title: "Multiple encrypted txs in same block", + test: async () => { + // Use different signers to avoid nonce ordering issues between + // the outer wrappers and decrypted inner transactions. + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, charlie.address); + + const senders = [alice, bob]; + const amount = 1_000_000_000n; + const txPromises = []; + + for (const sender of senders) { + const nonce = await getAccountNonce(api, sender.address); + + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(charlie.address), + value: amount, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + txPromises.push(submitEncrypted(api, sender, hexToU8a(innerTxHex), nextKey, nonce)); + } + + await Promise.all(txPromises); + + const balanceAfter = await getBalance(api, charlie.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + + it({ + id: "T05", + title: "Wrong key hash is not included by the block proposer", + test: async () => { + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 1_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + const ciphertext = await encryptTransaction(hexToU8a(innerTxHex), nextKey!); + + // Tamper the first 16 bytes (key_hash). + const tampered = new Uint8Array(ciphertext); + for (let i = 0; i < 16; i++) tampered[i] = 0xff; + + const tx = api.tx.MevShield.submit_encrypted({ + ciphertext: Binary.fromBytes(tampered), + }); + const signedHex = await tx.sign(getSignerFromKeypair(alice), { + nonce, + mortality: { mortal: true, period: 8 }, + }); + // Send without waiting — the tx enters the pool but the block + // proposer will skip it because the key_hash doesn't match. + client.submit(signedHex).catch(() => {}); + + await waitForFinalizedBlocks(api, 3); + + // The inner transfer should NOT have executed. + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBe(balanceBefore); + }, + }); + + it({ + id: "T06", + title: "Stale key is not included after rotation", + test: async () => { + const staleKey = await getNextKey(api); + expect(staleKey).toBeDefined(); + + // Wait for enough blocks that the key has rotated past both + // currentKey and nextKey positions. + await waitForFinalizedBlocks(api, 3); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 1_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + const ciphertext = await encryptTransaction(hexToU8a(innerTxHex), staleKey!); + + const tx = api.tx.MevShield.submit_encrypted({ + ciphertext: Binary.fromBytes(ciphertext), + }); + const signedHex = await tx.sign(getSignerFromKeypair(alice), { + nonce, + mortality: { mortal: true, period: 8 }, + }); + // Send without waiting — the block proposer will reject because + // key_hash no longer matches currentKey or nextKey. + client.submit(signedHex).catch(() => {}); + + await waitForFinalizedBlocks(api, 3); + + // The inner transfer should NOT have executed. + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBe(balanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_shield/01-scaling.test.ts b/ts-tests/suites/zombienet_shield/01-scaling.test.ts new file mode 100644 index 0000000000..d6072158f7 --- /dev/null +++ b/ts-tests/suites/zombienet_shield/01-scaling.test.ts @@ -0,0 +1,122 @@ +import { expect, beforeAll } from "vitest"; +import type { PolkadotClient, TypedApi } from "polkadot-api"; +import { hexToU8a } from "@polkadot/util"; +import { describeSuite } from "@moonwall/cli"; +import { + checkRuntime, + getAccountNonce, + getBalance, + getNextKey, + getSignerFromKeypair, + submitEncrypted, + waitForFinalizedBlocks, +} from "../../utils"; +import type { KeyringPair } from "@moonwall/util"; +import { Keyring } from "@polkadot/keyring"; +import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; + +describeSuite({ + id: "01_scaling", + title: "MEV Shield — 6 node scaling", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi<typeof subtensor>; + let client: PolkadotClient; + + let alice: KeyringPair; + let bob: KeyringPair; + let charlie: KeyringPair; + + beforeAll(async () => { + const keyring = new Keyring({ type: "sr25519" }); + alice = keyring.addFromUri("//Alice"); + bob = keyring.addFromUri("//Bob"); + charlie = keyring.addFromUri("//Charlie"); + + client = context.papi("Node"); + api = client.getTypedApi(subtensor); + + await checkRuntime(api); + }, 120000); + + it({ + id: "T01", + title: "Network scales to 6 nodes with full peering", + test: async () => { + // We run 6 nodes: 3 validators and 3 full nodes (5 peers + self) + expect((await client._request("system_peers", [])).length + 1).toBe(6); + + // Verify the network is healthy by checking finalization continues. + await waitForFinalizedBlocks(api, 2); + }, + }); + + it({ + id: "T02", + title: "Key rotation continues with more peers", + test: async () => { + const key1 = await getNextKey(api); + expect(key1).toBeDefined(); + + await waitForFinalizedBlocks(api, 2); + + const key2 = await getNextKey(api); + expect(key2).toBeDefined(); + expect(key2.length).toBe(1184); + }, + }); + + it({ + id: "T03", + title: "Encrypted tx works with 6 nodes", + test: async () => { + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 5_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + + it({ + id: "T04", + title: "Multiple encrypted txs in same block with 6 nodes", + test: async () => { + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, charlie.address); + + const senders = [alice, bob]; + const amount = 1_000_000_000n; + const txPromises = []; + + for (const sender of senders) { + const nonce = await getAccountNonce(api, sender.address); + + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(charlie.address), + value: amount, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + txPromises.push(submitEncrypted(api, sender, hexToU8a(innerTxHex), nextKey, nonce)); + } + + await Promise.all(txPromises); + + const balanceAfter = await getBalance(api, charlie.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_shield/02-edge-cases.test.ts b/ts-tests/suites/zombienet_shield/02-edge-cases.test.ts new file mode 100644 index 0000000000..951c939b97 --- /dev/null +++ b/ts-tests/suites/zombienet_shield/02-edge-cases.test.ts @@ -0,0 +1,96 @@ +import { expect, beforeAll } from "vitest"; +import type { TypedApi } from "polkadot-api"; +import { hexToU8a } from "@polkadot/util"; +import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; +import { describeSuite } from "@moonwall/cli"; +import type { KeyringPair } from "@moonwall/util"; +import { Keyring } from "@polkadot/keyring"; +import { + checkRuntime, + getAccountNonce, + getBalance, + getNextKey, + getSignerFromKeypair, + submitEncrypted, + waitForFinalizedBlocks, +} from "../../utils"; + +describeSuite({ + id: "02_edge_cases", + title: "MEV Shield — edge cases", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi<typeof subtensor>; + + let alice: KeyringPair; + let bob: KeyringPair; + + beforeAll(async () => { + const keyring = new Keyring({ type: "sr25519" }); + alice = keyring.addFromUri("//Alice"); + bob = keyring.addFromUri("//Bob"); + + api = context.papi("Node").getTypedApi(subtensor); + + await checkRuntime(api); + + await waitForFinalizedBlocks(api, 2); + }, 120000); + + it({ + id: "T01", + title: "Encrypted tx persists across blocks (CurrentKey fallback)", + test: async () => { + // The idea: submit an encrypted tx right at a block boundary. + // Even if the key rotates (NextKey changes), the old key becomes + // CurrentKey, so the extension still accepts it. + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 2_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + // Submit and wait for finalization — the tx may land in the next block + // or the one after, where CurrentKey = the old NextKey. + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + + it({ + id: "T02", + title: "Valid ciphertext with invalid inner call", + test: async () => { + // Encrypt garbage bytes (not a valid extrinsic) using a valid NextKey. + // The wrapper tx should be included in a block because: + // - The ciphertext is well-formed (key_hash, kem_ct, nonce, aead_ct) + // - The key_hash matches a known key + // But the inner decrypted bytes won't decode as a valid extrinsic, + // so no inner transaction should execute. + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + // Garbage "inner transaction" bytes — not a valid extrinsic at all. + const garbageInner = new Uint8Array(64); + for (let i = 0; i < 64; i++) garbageInner[i] = (i * 7 + 13) & 0xff; + + const nonce = await getAccountNonce(api, alice.address); + + await submitEncrypted(api, alice, garbageInner, nextKey, nonce); + + // No balance change — the garbage inner call could not have been a valid transfer. + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBe(balanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_shield/03-timing.test.ts b/ts-tests/suites/zombienet_shield/03-timing.test.ts new file mode 100644 index 0000000000..b100e02e85 --- /dev/null +++ b/ts-tests/suites/zombienet_shield/03-timing.test.ts @@ -0,0 +1,148 @@ +import { expect, beforeAll } from "vitest"; +import type { TypedApi } from "polkadot-api"; +import { hexToU8a } from "@polkadot/util"; +import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; +import { describeSuite } from "@moonwall/cli"; +import type { KeyringPair } from "@moonwall/util"; +import { Keyring } from "@polkadot/keyring"; +import { + checkRuntime, + getAccountNonce, + getBalance, + getNextKey, + getSignerFromKeypair, + submitEncrypted, + waitForFinalizedBlocks, +} from "../../utils"; +import { sleep } from "@zombienet/utils"; + +describeSuite({ + id: "03_timing", + title: "MEV Shield — timing boundaries", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi<typeof subtensor>; + + let alice: KeyringPair; + let bob: KeyringPair; + + beforeAll(async () => { + const keyring = new Keyring({ type: "sr25519" }); + alice = keyring.addFromUri("//Alice"); + bob = keyring.addFromUri("//Bob"); + + api = context.papi("Node").getTypedApi(subtensor); + + await checkRuntime(api); + }, 120000); + + it({ + id: "T01", + title: "Submit immediately after a new block", + test: async () => { + // Wait for a fresh finalized block, then immediately read NextKey and submit. + // This tests the "just after block" boundary where keys just rotated. + await waitForFinalizedBlocks(api, 1); + + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 1_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + + it({ + id: "T02", + title: "Submit mid-block (~6s after block)", + test: async () => { + // Wait for a block, then sleep 6s (half of 12s slot) before submitting. + // The key should still be valid — the same NextKey applies until the next block. + await waitForFinalizedBlocks(api, 1); + await sleep(6_000); + + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 1_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + + it({ + id: "T03", + title: "Submit just before next block (~11s after block)", + test: async () => { + // Wait for a block, then sleep ~11s to submit right before the next slot. + // The tx enters the pool just as the next block is about to be produced. + // It should still be included because the N+2 author hasn't changed yet, + // and PendingKey will match on the next block's proposer check. + await waitForFinalizedBlocks(api, 1); + await sleep(11_000); + + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 1_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + + it({ + id: "T04", + title: "Read key, wait full slot (12s), then submit", + test: async () => { + // Read NextKey, wait a full slot duration, then submit. + // After one full slot, the key rotates: old NextKey becomes PendingKey. + // The tx should still be included by the target N+2 author. + const nextKey = await getNextKey(api); + expect(nextKey).toBeDefined(); + + await sleep(12_000); + + const balanceBefore = await getBalance(api, bob.address); + + const nonce = await getAccountNonce(api, alice.address); + const innerTxHex = await api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 1_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + await submitEncrypted(api, alice, hexToU8a(innerTxHex), nextKey, nonce); + + const balanceAfter = await getBalance(api, bob.address); + expect(balanceAfter).toBeGreaterThan(balanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_shield/04-mortality.test.ts b/ts-tests/suites/zombienet_shield/04-mortality.test.ts new file mode 100644 index 0000000000..2f61db0daf --- /dev/null +++ b/ts-tests/suites/zombienet_shield/04-mortality.test.ts @@ -0,0 +1,138 @@ +import { expect, beforeAll } from "vitest"; +import type { PolkadotClient, TypedApi } from "polkadot-api"; +import { Binary } from "polkadot-api"; +import { hexToU8a } from "@polkadot/util"; +import { subtensor, MultiAddress } from "@polkadot-api/descriptors"; +import type { KeyringPair } from "@moonwall/util"; +import { Keyring } from "@polkadot/keyring"; +import { + checkRuntime, + encryptTransaction, + getAccountNonce, + getBalance, + getNextKey, + getSignerFromKeypair, + waitForFinalizedBlocks, +} from "../../utils"; +import { describeSuite } from "@moonwall/cli"; +import { sleep } from "@zombienet/utils"; + +// MAX_SHIELD_ERA_PERIOD is 8 blocks. With 12s slots, that's ~96s. +const MAX_ERA_BLOCKS = 8; +const SLOT_DURATION_MS = 12_000; +const POLL_INTERVAL_MS = 3_000; + +describeSuite({ + id: "04_mortality", + title: "MEV Shield — mortality eviction", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let apiAuthority: TypedApi<typeof subtensor>; + + let apiFull: TypedApi<typeof subtensor>; + let clientFull: PolkadotClient; + + let alice: KeyringPair; + let bob: KeyringPair; + + beforeAll( + async () => { + const keyring = new Keyring({ type: "sr25519" }); + alice = keyring.addFromUri("//Alice"); + bob = keyring.addFromUri("//Bob"); + + apiAuthority = context.papi("Node").getTypedApi(subtensor); + + clientFull = context.papi("NodeFull"); + apiFull = clientFull.getTypedApi(subtensor); + + await checkRuntime(apiAuthority); + + // Wait for a fresh finalized block, then immediately read NextKey and submit. + // This tests the "just after block" boundary where keys just rotated. + await waitForFinalizedBlocks(apiAuthority, 1); + }, + (MAX_ERA_BLOCKS + 8) * SLOT_DURATION_MS + ); + + it({ + id: "T01", + title: "Tx with tampered key_hash submitted to non-authority is evicted within mortality window", + test: async () => { + // Read a valid NextKey from an authority node, encrypt a real inner tx. + const nextKey = await getNextKey(apiAuthority); + expect(nextKey).toBeDefined(); + + const balanceBefore = await getBalance(apiFull, bob.address); + + const nonce = await getAccountNonce(apiFull, alice.address); + const innerTxHex = await apiFull.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(bob.address), + value: 1_000_000_000n, + }).sign(getSignerFromKeypair(alice), { nonce: nonce + 1 }); + + // Encrypt with valid key, then tamper the key_hash so no proposer will include it. + const ciphertext = await encryptTransaction(hexToU8a(innerTxHex), nextKey); + const tampered = new Uint8Array(ciphertext); + for (let i = 0; i < 16; i++) tampered[i] = 0xff; + + const tx = apiFull.tx.MevShield.submit_encrypted({ + ciphertext: Binary.fromBytes(tampered), + }); + + // Sign with short mortality (must be ≤ MAX_SHIELD_ERA_PERIOD=8 to pass + // CheckMortality validation). The tx enters the pool but no proposer + // will include it (tampered key_hash doesn't match PendingKey). + const signedHex = await tx.sign(getSignerFromKeypair(alice), { + nonce, + mortality: { mortal: true, period: 8 }, + }); + + // Submit via raw RPC to get immediate feedback on pool acceptance. + let txHash: string; + try { + txHash = await clientFull._request("author_submitExtrinsic", [signedHex]); + log(`Tx submitted successfully, hash: ${txHash}`); + } catch (err: unknown) { + throw new Error(`Tx rejected at pool entry: ${err}`); + } + + // Verify it's in the pool. + await sleep(1_000); + const pending: string[] = await clientFull._request("author_pendingExtrinsics", []); + log(`Pool has ${pending.length} pending tx(s)`); + + // Now poll until the tx disappears (mortality eviction). + const start = Date.now(); + const maxPollMs = (MAX_ERA_BLOCKS + 4) * SLOT_DURATION_MS; + let evicted = false; + + log(`Waiting for mortality eviction (up to ${maxPollMs / 1000}s)...`); + + while (Date.now() - start < maxPollMs) { + await sleep(POLL_INTERVAL_MS); + + const pending: string[] = await clientFull._request("author_pendingExtrinsics", []); + + if (pending.length === 0) { + evicted = true; + break; + } + } + + const elapsed = Date.now() - start; + log(`Tx ${evicted ? "evicted" : "still in pool"} after ${(elapsed / 1000).toFixed(1)}s`); + + expect(evicted).toBe(true); + + // Eviction should happen within the mortality window plus margin. + const maxExpectedMs = (MAX_ERA_BLOCKS + 2) * SLOT_DURATION_MS; + expect(elapsed).toBeLessThan(maxExpectedMs); + + // The inner transfer should NOT have executed. + const balanceAfter = await getBalance(apiFull, bob.address); + expect(balanceAfter).toBe(balanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/00-add-stake.test.ts b/ts-tests/suites/zombienet_staking/00-add-stake.test.ts new file mode 100644 index 0000000000..d2ae779696 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/00-add-stake.test.ts @@ -0,0 +1,61 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + forceSetBalance, + generateKeyringPair, + getStake, + startCall, + sudoSetLockReductionInterval, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "00_add_stake", + title: "▶ add_stake extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + const hotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkeyAddress = hotkey.address; + const coldkeyAddress = coldkey.address; + let netuid: number; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + + // Set lock reduction interval to 1 block to make network registration lock cost decay instantly. + // By default, the lock cost doubles with each subnet registration and decays over 14 days (100,800 blocks). + // Without this, tests creating multiple subnets would fail with CannotAffordLockCost. + await sudoSetLockReductionInterval(api, 1); + + await forceSetBalance(api, hotkeyAddress); + await forceSetBalance(api, coldkeyAddress); + netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + }); + + it({ + id: "T01", + title: "Add staking payable", + test: async () => { + // Get initial stake + const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + + // Add stake + const stakeAmount = tao(100); + await addStake(api, coldkey, hotkeyAddress, netuid, stakeAmount); + + // Verify stake increased + const stakeAfter = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + expect(stakeAfter, "Stake should increase after adding stake").toBeGreaterThan(stakeBefore); + + log("✅ Successfully added stake."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/01-add-stake-limit.test.ts b/ts-tests/suites/zombienet_staking/01-add-stake-limit.test.ts new file mode 100644 index 0000000000..9dd5ff6d55 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/01-add-stake-limit.test.ts @@ -0,0 +1,78 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStakeLimit, + forceSetBalance, + generateKeyringPair, + getStake, + startCall, + sudoSetLockReductionInterval, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "01_add_stake_limit", + title: "▶ add_stake_limit extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + const hotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkeyAddress = hotkey.address; + const coldkeyAddress = coldkey.address; + let netuid: number; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await forceSetBalance(api, hotkeyAddress); + await forceSetBalance(api, coldkeyAddress); + await sudoSetLockReductionInterval(api, 1); + netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + }); + + it({ + id: "T01", + title: "should add stake with price limit (allow partial)", + test: async () => { + // Get initial stake + const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + + // Add stake with limit price and allow partial fills, limit_price is MAX TAO per Alpha willing to pay. + const stakeAmount = tao(44); + const limitPrice = tao(6); + await addStakeLimit(api, coldkey, hotkeyAddress, netuid, stakeAmount, limitPrice, true); + + // Verify stake increased + const stakeAfter = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + expect(stakeAfter, "Stake should increase").toBeGreaterThan(stakeBefore); + + log("✅ Successfully added stake with limit (allow partial)."); + }, + }); + + it({ + id: "T02", + title: "should add stake with price limit (fill or kill)", + test: async () => { + // Get initial stake + const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + + // Add stake with limit price (fill or kill mode), limit_price is MAX TAO per Alpha willing to pay + const stakeAmount = tao(44); + const limitPrice = tao(6); + await addStakeLimit(api, coldkey, hotkeyAddress, netuid, stakeAmount, limitPrice, false); + + // Verify stake increased + const stakeAfter = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + expect(stakeAfter, "Stake should increase").toBeGreaterThan(stakeBefore); + + log("✅ Successfully added stake with limit (fill or kill)."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/02.00-claim-root.test.ts b/ts-tests/suites/zombienet_staking/02.00-claim-root.test.ts new file mode 100644 index 0000000000..0bdfa2011b --- /dev/null +++ b/ts-tests/suites/zombienet_staking/02.00-claim-root.test.ts @@ -0,0 +1,108 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + forceSetBalance, + generateKeyringPair, + getRootClaimType, + setRootClaimType, + sudoSetLockReductionInterval, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "02_set_root_claim_type", + title: "▶ set_root_claim_type extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + }); + + it({ + id: "T0101", + title: "should set root claim type to Keep", + test: async () => { + const coldkey = generateKeyringPair("sr25519"); + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, coldkeyAddress); + + // Check initial claim type (default is "Swap") + const claimTypeBefore = await getRootClaimType(api, coldkeyAddress); + log(`Root claim type before: ${claimTypeBefore}`); + + // Set root claim type to Keep + await setRootClaimType(api, coldkey, "Keep"); + + // Verify claim type changed + const claimTypeAfter = await getRootClaimType(api, coldkeyAddress); + log(`Root claim type after: ${claimTypeAfter}`); + + expect(claimTypeAfter).toBe("Keep"); + + log("✅ Successfully set root claim type to Keep."); + }, + }); + + it({ + id: "T0102", + title: "should set root claim type to Swap", + test: async () => { + const coldkey = generateKeyringPair("sr25519"); + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, coldkeyAddress); + + // First set to Keep so we can verify the change to Swap + await setRootClaimType(api, coldkey, "Keep"); + const claimTypeBefore = await getRootClaimType(api, coldkeyAddress); + log(`Root claim type before: ${claimTypeBefore}`); + expect(claimTypeBefore).toBe("Keep"); + + // Set root claim type to Swap + await setRootClaimType(api, coldkey, "Swap"); + + // Verify claim type changed + const claimTypeAfter = await getRootClaimType(api, coldkeyAddress); + log(`Root claim type after: ${claimTypeAfter}`); + + expect(claimTypeAfter).toBe("Swap"); + + log("✅ Successfully set root claim type to Swap."); + }, + }); + + it({ + id: "T0103", + title: "should set root claim type to KeepSubnets", + test: async () => { + const coldkey = generateKeyringPair("sr25519"); + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, coldkeyAddress); + + // Check initial claim type (default is "Swap") + const claimTypeBefore = await getRootClaimType(api, coldkeyAddress); + log(`Root claim type before: ${JSON.stringify(claimTypeBefore)}`); + + // Set root claim type to KeepSubnets with specific subnets + const subnetsToKeep = [1, 2]; + await setRootClaimType(api, coldkey, { type: "KeepSubnets", subnets: subnetsToKeep }); + + // Verify claim type changed + const claimTypeAfter = await getRootClaimType(api, coldkeyAddress); + log(`Root claim type after: ${JSON.stringify(claimTypeAfter)}`); + + expect(typeof claimTypeAfter).toBe("object"); + expect((claimTypeAfter as { type: string }).type).toBe("KeepSubnets"); + expect((claimTypeAfter as { subnets: number[] }).subnets).toEqual(subnetsToKeep); + + log("✅ Successfully set root claim type to KeepSubnets."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/02.01-claim-root.test.ts b/ts-tests/suites/zombienet_staking/02.01-claim-root.test.ts new file mode 100644 index 0000000000..1a86fa4131 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/02.01-claim-root.test.ts @@ -0,0 +1,41 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { getNumRootClaims, sudoSetLockReductionInterval, sudoSetNumRootClaims } from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "0201_sudo_set_num_root_claims", + title: "▶ sudo_set_num_root_claims extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + }); + + it({ + id: "T0201", + title: "", + test: async () => { + // Get initial value + const numClaimsBefore = await getNumRootClaims(api); + log(`Num root claims before: ${numClaimsBefore}`); + + // Set new value (different from current) + const newValue = numClaimsBefore + 5n; + await sudoSetNumRootClaims(api, newValue); + + // Verify value changed + const numClaimsAfter = await getNumRootClaims(api); + log(`Num root claims after: ${numClaimsAfter}`); + + expect(numClaimsAfter).toBe(newValue); + + log("✅ Successfully set num root claims."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/02.02-claim-root.test.ts b/ts-tests/suites/zombienet_staking/02.02-claim-root.test.ts new file mode 100644 index 0000000000..ff0e3892bf --- /dev/null +++ b/ts-tests/suites/zombienet_staking/02.02-claim-root.test.ts @@ -0,0 +1,64 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + forceSetBalance, + generateKeyringPair, + getRootClaimThreshold, + startCall, + sudoSetLockReductionInterval, + sudoSetRootClaimThreshold, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "0202_sudo_set_root_claim_threshold", + title: "▶ sudo_set_root_claim_threshold extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + }); + + it({ + id: "T0301", + title: "should set root claim threshold for subnet", + test: async () => { + // Create a subnet to test with + const hotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkeyAddress = hotkey.address; + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, hotkeyAddress); + await forceSetBalance(api, coldkeyAddress); + + const netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + + // Get initial threshold + const thresholdBefore = await getRootClaimThreshold(api, netuid); + log(`Root claim threshold before: ${thresholdBefore}`); + + // Set new threshold value (MAX_ROOT_CLAIM_THRESHOLD is 10_000_000) + // The value is stored as I96F32 fixed-point with 32 fractional bits + const newThreshold = 1_000_000n; + await sudoSetRootClaimThreshold(api, netuid, newThreshold); + + // Verify threshold changed + // I96F32 encoding: newThreshold * 2^32 = 1_000_000 * 4294967296 = 4294967296000000 + const thresholdAfter = await getRootClaimThreshold(api, netuid); + log(`Root claim threshold after: ${thresholdAfter}`); + + const expectedStoredValue = newThreshold * (1n << 32n); // I96F32 encoding + expect(thresholdAfter).toBe(expectedStoredValue); + + log("✅ Successfully set root claim threshold."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/02.03-claim-root.test.ts b/ts-tests/suites/zombienet_staking/02.03-claim-root.test.ts new file mode 100644 index 0000000000..37a10bfe65 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/02.03-claim-root.test.ts @@ -0,0 +1,357 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + claimRoot, + forceSetBalance, + generateKeyringPair, + getPendingRootAlphaDivs, + getRootClaimable, + getRootClaimed, + getRootClaimType, + getStake, + getSubnetAlphaIn, + getSubnetMovingPrice, + getSubnetTAO, + getTaoWeight, + getTotalHotkeyAlpha, + isSubtokenEnabled, + setRootClaimType, + startCall, + sudoSetAdminFreezeWindow, + sudoSetEmaPriceHalvingPeriod, + sudoSetLockReductionInterval, + sudoSetRootClaimThreshold, + sudoSetSubnetMovingAlpha, + sudoSetSubtokenEnabled, + sudoSetTempo, + tao, + waitForBlocks, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "0203_claim_root", + title: "▶ claim_root extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + const ROOT_NETUID = 0; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + }); + + it({ + id: "T0401", + title: "should claim root dividends with Keep type (stake to dynamic subnet)", + test: async () => { + // Setup accounts + // - owner1Hotkey/owner1Coldkey: subnet 1 owner + // - owner2Hotkey/owner2Coldkey: subnet 2 owner (needed for root_sell_flag) + // - stakerColdkey: the coldkey that will stake on root and claim dividends + const owner1Hotkey = generateKeyringPair("sr25519"); + const owner1Coldkey = generateKeyringPair("sr25519"); + const owner2Hotkey = generateKeyringPair("sr25519"); + const owner2Coldkey = generateKeyringPair("sr25519"); + const stakerColdkey = generateKeyringPair("sr25519"); + const owner1HotkeyAddress = owner1Hotkey.address; + const owner1ColdkeyAddress = owner1Coldkey.address; + const owner2HotkeyAddress = owner2Hotkey.address; + const owner2ColdkeyAddress = owner2Coldkey.address; + const stakerColdkeyAddress = stakerColdkey.address; + + // Fund all accounts + await forceSetBalance(api, owner1HotkeyAddress); + await forceSetBalance(api, owner1ColdkeyAddress); + await forceSetBalance(api, owner2HotkeyAddress); + await forceSetBalance(api, owner2ColdkeyAddress); + await forceSetBalance(api, stakerColdkeyAddress); + + // Disable admin freeze window to allow enabling subtoken for ROOT + await sudoSetAdminFreezeWindow(api, 0); + log("Admin freeze window set to 0"); + + // Enable subtoken for ROOT subnet (required for staking on root) + const subtokenEnabledBefore = await isSubtokenEnabled(api, ROOT_NETUID); + if (!subtokenEnabledBefore) { + await sudoSetSubtokenEnabled(api, ROOT_NETUID, true); + const subtokenEnabledAfter = await isSubtokenEnabled(api, ROOT_NETUID); + log(`ROOT subtoken enabled: ${subtokenEnabledAfter}`); + expect(subtokenEnabledAfter).toBe(true); + } + + // Create TWO dynamic subnets - needed for root_sell_flag to become true + // root_sell_flag = sum(moving_prices) > 1.0 + // Each subnet's moving price approaches 1.0 via EMA, so 2 subnets can exceed threshold + const netuid1 = await addNewSubnetwork(api, owner1Hotkey, owner1Coldkey); + await startCall(api, netuid1, owner1Coldkey); + log(`Created subnet 1 with netuid: ${netuid1}`); + + const netuid2 = await addNewSubnetwork(api, owner2Hotkey, owner2Coldkey); + await startCall(api, netuid2, owner2Coldkey); + log(`Created subnet 2 with netuid: ${netuid2}`); + + // Set short tempo for faster emission distribution + await sudoSetTempo(api, netuid1, 1); + await sudoSetTempo(api, netuid2, 1); + log("Set tempo to 1 for both subnets"); + + // Set EMA price halving period to 1 for fast moving price convergence + // Formula: alpha = SubnetMovingAlpha * blocks/(blocks + halving_time) + // With halving_time=1: after 10 blocks, alpha ≈ 0.91, moving price ≈ 0.91 + // With 2 subnets at ~0.9 each, total > 1.0 enabling root_sell_flag + await sudoSetEmaPriceHalvingPeriod(api, netuid1, 1); + await sudoSetEmaPriceHalvingPeriod(api, netuid2, 1); + log("Set EMA halving period to 1 for fast price convergence"); + + // Set SubnetMovingAlpha to 1.0 (default is 0.000003 which is way too slow) + // I96F32 encoding: 1.0 * 2^32 = 4294967296 + const movingAlpha = BigInt(4294967296); // 1.0 in I96F32 + await sudoSetSubnetMovingAlpha(api, movingAlpha); + log("Set SubnetMovingAlpha to 1.0 for fast EMA convergence"); + + // Set threshold to 0 to allow claiming any amount + await sudoSetRootClaimThreshold(api, netuid1, 0n); + await sudoSetRootClaimThreshold(api, netuid2, 0n); + + // Add stake to ROOT subnet for the staker (makes them eligible for root dividends) + const rootStakeAmount = tao(100); + await addStake(api, stakerColdkey, owner1HotkeyAddress, ROOT_NETUID, rootStakeAmount); + log(`Added ${rootStakeAmount} stake to root subnet for staker`); + + // Verify root stake was added + const rootStake = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, ROOT_NETUID); + log(`Root stake: ${rootStake}`); + expect(rootStake, "Should have stake on root subnet").toBeGreaterThan(0n); + + // Add stake to both dynamic subnets (owner stake to enable emissions flow) + const subnetStakeAmount = tao(50); + await addStake(api, owner1Coldkey, owner1HotkeyAddress, netuid1, subnetStakeAmount); + await addStake(api, owner2Coldkey, owner2HotkeyAddress, netuid2, subnetStakeAmount); + log(`Added ${subnetStakeAmount} owner stake to subnets ${netuid1} and ${netuid2}`); + + // Get initial stake on subnet 1 for the staker (should be 0) + const stakerSubnetStakeBefore = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, netuid1); + log(`Staker subnet stake before claim: ${stakerSubnetStakeBefore}`); + + // Set root claim type to Keep (keep alpha on subnet instead of swapping to TAO) + await setRootClaimType(api, stakerColdkey, "Keep"); + const claimType = await getRootClaimType(api, stakerColdkeyAddress); + log(`Root claim type: ${claimType}`); + expect(claimType).toBe("Keep"); + + // Wait for blocks to: + // 1. Allow moving prices to converge (need sum > 1.0 for root_sell_flag) + // 2. Accumulate PendingRootAlphaDivs + // 3. Distribute emissions at tempo boundary + const blocksToWait = 25; + log(`Waiting for ${blocksToWait} blocks for moving prices to converge and emissions to accumulate...`); + await waitForBlocks(api, blocksToWait); + + // Debug: Check key storage values + const subnetTaoRoot = await getSubnetTAO(api, ROOT_NETUID); + const subnetTao1 = await getSubnetTAO(api, netuid1); + const subnetTao2 = await getSubnetTAO(api, netuid2); + log(`SubnetTAO - ROOT: ${subnetTaoRoot}, netuid1: ${subnetTao1}, netuid2: ${subnetTao2}`); + + const movingPrice1 = await getSubnetMovingPrice(api, netuid1); + const movingPrice2 = await getSubnetMovingPrice(api, netuid2); + log(`SubnetMovingPrice - netuid1: ${movingPrice1}, netuid2: ${movingPrice2}`); + // Note: Moving price is I96F32, so divide by 2^32 to get actual value + const mp1Float = Number(movingPrice1) / 2 ** 32; + const mp2Float = Number(movingPrice2) / 2 ** 32; + log( + `SubnetMovingPrice (float) - netuid1: ${mp1Float}, netuid2: ${mp2Float}, sum: ${mp1Float + mp2Float}` + ); + + const pendingDivs1 = await getPendingRootAlphaDivs(api, netuid1); + const pendingDivs2 = await getPendingRootAlphaDivs(api, netuid2); + log(`PendingRootAlphaDivs - netuid1: ${pendingDivs1}, netuid2: ${pendingDivs2}`); + + const taoWeight = await getTaoWeight(api); + log(`TaoWeight: ${taoWeight}`); + + const alphaIn1 = await getSubnetAlphaIn(api, netuid1); + const alphaIn2 = await getSubnetAlphaIn(api, netuid2); + log(`SubnetAlphaIn - netuid1: ${alphaIn1}, netuid2: ${alphaIn2}`); + + const totalHotkeyAlpha1 = await getTotalHotkeyAlpha(api, owner1HotkeyAddress, netuid1); + log(`TotalHotkeyAlpha for hotkey1 on netuid1: ${totalHotkeyAlpha1}`); + + // Check if there are any claimable dividends + const claimable = await getRootClaimable(api, owner1HotkeyAddress); + const claimableStr = [...claimable.entries()].map(([k, v]) => `[${k}: ${v.toString()}]`).join(", "); + log(`RootClaimable entries for hotkey1: ${claimableStr || "(none)"}`); + + // Call claim_root to claim dividends for subnet 1 + await claimRoot(api, stakerColdkey, [netuid1]); + log("Called claim_root"); + + // Get stake on subnet 1 after claim + const stakerSubnetStakeAfter = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, netuid1); + log(`Staker subnet stake after claim: ${stakerSubnetStakeAfter}`); + + // Check RootClaimed value + const rootClaimed = await getRootClaimed(api, netuid1, owner1HotkeyAddress, stakerColdkeyAddress); + log(`RootClaimed value: ${rootClaimed}`); + + // Verify dividends were claimed + expect(stakerSubnetStakeAfter, "Stake should increase after claiming root dividends").toBeGreaterThan( + stakerSubnetStakeBefore + ); + log( + `✅ Root claim successful: stake increased from ${stakerSubnetStakeBefore} to ${stakerSubnetStakeAfter}` + ); + }, + }); + + it({ + id: "T0402", + title: "should claim root dividends with Swap type (swap to TAO on ROOT)", + test: async () => { + // Setup accounts + // - owner1Hotkey/owner1Coldkey: subnet 1 owner + // - owner2Hotkey/owner2Coldkey: subnet 2 owner (needed for root_sell_flag) + // - stakerColdkey: the coldkey that will stake on root and claim dividends + const owner1Hotkey = generateKeyringPair("sr25519"); + const owner1Coldkey = generateKeyringPair("sr25519"); + const owner2Hotkey = generateKeyringPair("sr25519"); + const owner2Coldkey = generateKeyringPair("sr25519"); + const stakerColdkey = generateKeyringPair("sr25519"); + const owner1HotkeyAddress = owner1Hotkey.address; + const owner1ColdkeyAddress = owner1Coldkey.address; + const owner2HotkeyAddress = owner2Hotkey.address; + const owner2ColdkeyAddress = owner2Coldkey.address; + const stakerColdkeyAddress = stakerColdkey.address; + + // Fund all accounts + await forceSetBalance(api, owner1HotkeyAddress); + await forceSetBalance(api, owner1ColdkeyAddress); + await forceSetBalance(api, owner2HotkeyAddress); + await forceSetBalance(api, owner2ColdkeyAddress); + await forceSetBalance(api, stakerColdkeyAddress); + + // Disable admin freeze window to allow enabling subtoken for ROOT + await sudoSetAdminFreezeWindow(api, 0); + log("Admin freeze window set to 0"); + + // Create TWO dynamic subnets + const netuid1 = await addNewSubnetwork(api, owner1Hotkey, owner1Coldkey); + await startCall(api, netuid1, owner1Coldkey); + log(`Created subnet 1 with netuid: ${netuid1}`); + + const netuid2 = await addNewSubnetwork(api, owner2Hotkey, owner2Coldkey); + await startCall(api, netuid2, owner2Coldkey); + log(`Created subnet 2 with netuid: ${netuid2}`); + + // Set short tempo for faster emission distribution + await sudoSetTempo(api, netuid1, 1); + await sudoSetTempo(api, netuid2, 1); + log("Set tempo to 1 for both subnets"); + + // Set EMA price halving period to 1 for fast moving price convergence + await sudoSetEmaPriceHalvingPeriod(api, netuid1, 1); + await sudoSetEmaPriceHalvingPeriod(api, netuid2, 1); + log("Set EMA halving period to 1 for fast price convergence"); + + // Set SubnetMovingAlpha to 1.0 (default is 0.000003 which is way too slow) + // I96F32 encoding: 1.0 * 2^32 = 4294967296 + const movingAlpha = BigInt(4294967296); // 1.0 in I96F32 + await sudoSetSubnetMovingAlpha(api, movingAlpha); + log("Set SubnetMovingAlpha to 1.0 for fast EMA convergence"); + + // Set threshold to 0 to allow claiming any amount + await sudoSetRootClaimThreshold(api, netuid1, 0n); + await sudoSetRootClaimThreshold(api, netuid2, 0n); + + // Add stake to ROOT subnet for the staker + const rootStakeAmount = tao(100); + await addStake(api, stakerColdkey, owner1HotkeyAddress, ROOT_NETUID, rootStakeAmount); + log(`Added ${rootStakeAmount} stake to root subnet for staker`); + + // Get initial ROOT stake + const rootStakeBefore = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, ROOT_NETUID); + log(`Root stake before: ${rootStakeBefore}`); + + // Add stake to both dynamic subnets (owner stake to enable emissions flow) + const subnetStakeAmount = tao(50); + await addStake(api, owner1Coldkey, owner1HotkeyAddress, netuid1, subnetStakeAmount); + await addStake(api, owner2Coldkey, owner2HotkeyAddress, netuid2, subnetStakeAmount); + log(`Added ${subnetStakeAmount} owner stake to subnets ${netuid1} and ${netuid2}`); + + // Set root claim type to Swap (swap alpha to TAO and add to ROOT stake) + await setRootClaimType(api, stakerColdkey, "Swap"); + const claimType = await getRootClaimType(api, stakerColdkeyAddress); + log(`Root claim type: ${claimType}`); + expect(claimType).toBe("Swap"); + + // Wait for blocks + const blocksToWait = 25; + log(`Waiting for ${blocksToWait} blocks for emissions to accumulate...`); + await waitForBlocks(api, blocksToWait); + + // Debug: Check moving prices + const movingPrice1 = await getSubnetMovingPrice(api, netuid1); + const movingPrice2 = await getSubnetMovingPrice(api, netuid2); + const mp1Float = Number(movingPrice1) / 2 ** 32; + const mp2Float = Number(movingPrice2) / 2 ** 32; + log( + `SubnetMovingPrice (float) - netuid1: ${mp1Float}, netuid2: ${mp2Float}, sum: ${mp1Float + mp2Float}` + ); + + const pendingDivs1 = await getPendingRootAlphaDivs(api, netuid1); + log(`PendingRootAlphaDivs netuid1: ${pendingDivs1}`); + + // Check claimable + const claimable = await getRootClaimable(api, owner1HotkeyAddress); + const claimableStr = [...claimable.entries()].map(([k, v]) => `[${k}: ${v.toString()}]`).join(", "); + log(`RootClaimable entries for hotkey1: ${claimableStr || "(none)"}`); + + // Call claim_root - with Swap type, dividends are swapped to TAO and added to ROOT stake + await claimRoot(api, stakerColdkey, [netuid1]); + log("Called claim_root with Swap type"); + + // Get ROOT stake after claim + const rootStakeAfter = await getStake(api, owner1HotkeyAddress, stakerColdkeyAddress, ROOT_NETUID); + log(`Root stake after claim: ${rootStakeAfter}`); + + // Check RootClaimed value + const rootClaimed = await getRootClaimed(api, netuid1, owner1HotkeyAddress, stakerColdkeyAddress); + log(`RootClaimed value: ${rootClaimed}`); + + // With Swap type, ROOT stake should increase (not dynamic subnet stake) + expect(rootStakeAfter, "ROOT stake should increase after claiming with Swap type").toBeGreaterThan( + rootStakeBefore + ); + log( + `✅ Root claim with Swap successful: ROOT stake increased from ${rootStakeBefore} to ${rootStakeAfter}` + ); + }, + }); + + it({ + id: "T0403", + title: "should handle claim_root when no dividends are available", + test: async () => { + // Setup accounts + const coldkey = generateKeyringPair("sr25519"); + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, coldkeyAddress); + + // Set root claim type to Keep + await setRootClaimType(api, coldkey, "Keep"); + + // Try to claim on a non-existent subnet (should succeed but be a no-op) + // According to Rust tests, claiming on unrelated subnets returns Ok but does nothing + await claimRoot(api, coldkey, [1]); + + log("✅ claim_root with no dividends executed successfully (no-op)."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/03-move-stake.test.ts b/ts-tests/suites/zombienet_staking/03-move-stake.test.ts new file mode 100644 index 0000000000..01aad65c72 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/03-move-stake.test.ts @@ -0,0 +1,155 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + burnedRegister, + forceSetBalance, + generateKeyringPair, + getStake, + moveStake, + startCall, + sudoSetLockReductionInterval, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "03_move_stake", + title: "▶ move_stake extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + }); + + it({ + id: "T01", + title: "should move stake to another hotkey across subnets", + test: async () => { + // Setup accounts + const originHotkey = generateKeyringPair("sr25519"); + const destinationHotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const originHotkeyAddress = originHotkey.address; + const destinationHotkeyAddress = destinationHotkey.address; + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, originHotkeyAddress); + await forceSetBalance(api, destinationHotkeyAddress); + await forceSetBalance(api, coldkeyAddress); + + // Create first subnet with origin hotkey + const netuid1 = await addNewSubnetwork(api, originHotkey, coldkey); + await startCall(api, netuid1, coldkey); + + // Create second subnet with destination hotkey + const netuid2 = await addNewSubnetwork(api, destinationHotkey, coldkey); + await startCall(api, netuid2, coldkey); + + // Add stake to origin hotkey on first subnet + await addStake(api, coldkey, originHotkeyAddress, netuid1, tao(200)); + + // Get initial stakes (converted from U64F64 for display) + const originStakeBefore = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid1); + const destStakeBefore = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid2); + expect(originStakeBefore, "Origin hotkey should have stake before move").toBeGreaterThan(0n); + + log( + `Origin stake (netuid1) before: ${originStakeBefore}, Destination stake (netuid2) before: ${destStakeBefore}` + ); + + // Move stake to destination hotkey on different subnet + // Use raw U64F64 value for the extrinsic + const originStake = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid1); + const moveAmount = originStake / 2n; + await moveStake( + api, + coldkey, + originHotkeyAddress, + destinationHotkeyAddress, + netuid1, + netuid2, + moveAmount + ); + + // Verify stakes changed + const originStakeAfter = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid1); + const destStakeAfter = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid2); + + log( + `Origin stake (netuid1) after: ${originStakeAfter}, Destination stake (netuid2) after: ${destStakeAfter}` + ); + + expect(originStakeAfter, "Origin stake should decrease").toBeLessThan(originStakeBefore); + expect(destStakeAfter, "Destination stake should increase").toBeGreaterThan(destStakeBefore); + + log("✅ Successfully moved stake to another hotkey across subnets."); + }, + }); + + it({ + id: "T02", + title: "should move stake to another hotkey on the same subnet", + test: async () => { + // Setup accounts + const originHotkey = generateKeyringPair("sr25519"); + const destinationHotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const originHotkeyAddress = originHotkey.address; + const destinationHotkeyAddress = destinationHotkey.address; + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, originHotkeyAddress); + await forceSetBalance(api, destinationHotkeyAddress); + await forceSetBalance(api, coldkeyAddress); + + // Create subnet with origin hotkey + const netuid = await addNewSubnetwork(api, originHotkey, coldkey); + await startCall(api, netuid, coldkey); + + // Register destination hotkey on the same subnet + await burnedRegister(api, netuid, destinationHotkeyAddress, coldkey); + + // Add stake to origin hotkey + await addStake(api, coldkey, originHotkeyAddress, netuid, tao(200)); + + // Get initial stakes (converted from U64F64 for display) + const originStakeBefore = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid); + const destStakeBefore = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid); + expect(originStakeBefore, "Origin hotkey should have stake before move").toBeGreaterThan(0n); + + log(`Origin stake before: ${originStakeBefore}, Destination stake before: ${destStakeBefore}`); + + // Move stake to destination hotkey on the same subnet + // Use raw U64F64 value for the extrinsic + const originStake = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid); + const moveAmount = originStake / 2n; + await moveStake( + api, + coldkey, + originHotkeyAddress, + destinationHotkeyAddress, + netuid, + netuid, + moveAmount + ); + + // Verify stakes changed + const originStakeAfter = await getStake(api, originHotkeyAddress, coldkeyAddress, netuid); + const destStakeAfter = await getStake(api, destinationHotkeyAddress, coldkeyAddress, netuid); + + log(`Origin stake after: ${originStakeAfter}, Destination stake after: ${destStakeAfter}`); + + expect(originStakeAfter, "Origin stake should decrease").toBeLessThan(originStakeBefore); + expect(destStakeAfter, "Destination stake should increase").toBeGreaterThan(destStakeBefore); + + log("✅ Successfully moved stake to another hotkey on the same subnet."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/04-remove-stake.test.ts b/ts-tests/suites/zombienet_staking/04-remove-stake.test.ts new file mode 100644 index 0000000000..fb3426a03c --- /dev/null +++ b/ts-tests/suites/zombienet_staking/04-remove-stake.test.ts @@ -0,0 +1,65 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + forceSetBalance, + generateKeyringPair, + getBalance, + getStake, + removeStake, + startCall, + sudoSetLockReductionInterval, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "04_remove_stake", + title: "▶ remove_stake extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + let netuid: number; + const hotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkeyAddress = hotkey.address; + const coldkeyAddress = coldkey.address; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + + await forceSetBalance(api, hotkeyAddress); + await forceSetBalance(api, coldkeyAddress); + await sudoSetLockReductionInterval(api, 1); + netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + }); + + it({ + id: "T01", + title: "should remove stake from a hotkey", + test: async () => { + // Add stake first + await addStake(api, coldkey, hotkeyAddress, netuid, tao(200)); + + // Get initial stake and balance (converted from U64F64 for display) + const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + const balanceBefore = await getBalance(api, coldkeyAddress); + expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); + + // Remove stake (amount is in alpha units - use raw U64F64 value) + const stake = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + const unstakeAmount = stake / 2n; + await removeStake(api, coldkey, hotkeyAddress, netuid, unstakeAmount); + + // Verify balance increased (received TAO from unstaking) + const balanceAfter = await getBalance(api, coldkeyAddress); + expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); + + log("✅ Successfully removed stake."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/05-remove-stake-full-limit.test.ts b/ts-tests/suites/zombienet_staking/05-remove-stake-full-limit.test.ts new file mode 100644 index 0000000000..77008780b3 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/05-remove-stake-full-limit.test.ts @@ -0,0 +1,113 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + burnedRegister, + forceSetBalance, + generateKeyringPair, + getBalance, + getStake, + removeStakeFullLimit, + startCall, + sudoSetAdminFreezeWindow, + sudoSetLockReductionInterval, + sudoSetTempo, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "05_remove_stake_full_limit", + title: "▶ remove_stake_full_limit extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + const ownerHotkey = generateKeyringPair("sr25519"); + const stakerHotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const ownerAddress = ownerHotkey.address; + const stakerAddress = stakerHotkey.address; + const coldkeyAddress = coldkey.address; + let netuid: number; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + + await sudoSetLockReductionInterval(api, 1); + await forceSetBalance(api, ownerAddress); + await forceSetBalance(api, stakerAddress); + await forceSetBalance(api, coldkeyAddress); + + await sudoSetAdminFreezeWindow(api, 0); + log("Admin freeze window set to 0"); + + netuid = await addNewSubnetwork(api, ownerHotkey, coldkey); + await startCall(api, netuid, coldkey); + // Set high tempo to prevent emissions during test + await sudoSetTempo(api, netuid, 10000); + // Register staker hotkey (not the owner) + await burnedRegister(api, netuid, stakerAddress, coldkey); + }); + + it({ + id: "T01", + title: "should remove all stake with price limit", + test: async () => { + // Add stake first + await addStake(api, coldkey, stakerAddress, netuid, tao(100)); + + // Get initial stake and balance + const stakeBefore = await getStake(api, stakerAddress, coldkeyAddress, netuid); + const balanceBefore = await getBalance(api, coldkeyAddress); + log(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); + expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); + + // Remove all stake with a reasonable limit price (low limit to avoid slippage rejection) + // Using a low limit price (0.09 TAO per alpha) allows the transaction to succeed + const limitPrice = tao(1) / 10n; // 0.1 TAO + await removeStakeFullLimit(api, coldkey, stakerAddress, netuid, limitPrice); + + // Verify stake is zero (staker is not owner, so all stake can be removed) + const stakeAfter = await getStake(api, stakerAddress, coldkeyAddress, netuid); + const balanceAfter = await getBalance(api, coldkeyAddress); + log(`Stake after: ${stakeAfter}, Balance after: ${balanceAfter}`); + + expect(stakeAfter, "Stake should be zero after full removal").toBe(0n); + expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); + + log("✅ Successfully removed all stake with price limit."); + }, + }); + + it({ + id: "T02", + title: "should remove all stake without price limit", + test: async () => { + // Add stake first + await addStake(api, coldkey, stakerAddress, netuid, tao(100)); + + // Get initial stake and balance + const stakeBefore = await getStake(api, stakerAddress, coldkeyAddress, netuid); + const balanceBefore = await getBalance(api, coldkeyAddress); + log(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); + expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); + + // Remove all stake without limit price (undefined = no slippage protection) + await removeStakeFullLimit(api, coldkey, stakerAddress, netuid, undefined); + + // Verify stake is zero (staker is not owner, so all stake can be removed) + const stakeAfter = await getStake(api, stakerAddress, coldkeyAddress, netuid); + const balanceAfter = await getBalance(api, coldkeyAddress); + log(`Stake after: ${stakeAfter}, Balance after: ${balanceAfter}`); + + expect(stakeAfter, "Stake should be zero after full removal").toBe(0n); + expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); + + log("✅ Successfully removed all stake without price limit."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/06-remove-stake-limit.test.ts b/ts-tests/suites/zombienet_staking/06-remove-stake-limit.test.ts new file mode 100644 index 0000000000..132611d4e7 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/06-remove-stake-limit.test.ts @@ -0,0 +1,91 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + forceSetBalance, + generateKeyringPair, + getBalance, + getStake, + removeStakeLimit, + startCall, + sudoSetLockReductionInterval, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "06_remove_stake_limit", + title: "▶ remove_stake_limit extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + const hotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkeyAddress = hotkey.address; + const coldkeyAddress = coldkey.address; + let netuid: number; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + await forceSetBalance(api, hotkeyAddress); + await forceSetBalance(api, coldkeyAddress); + netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + }); + + it({ + id: "T01", + title: "should remove stake with price limit (allow partial)", + test: async () => { + // Add stake first (100 TAO like benchmark) + await addStake(api, coldkey, hotkeyAddress, netuid, tao(100)); + + // Get initial stake and balance + const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + const balanceBefore = await getBalance(api, coldkeyAddress); + log(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); + expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); + + // Remove stake with limit price and allow partial fills + const unstakeAmount = tao(30); + const limitPrice = tao(1); + await removeStakeLimit(api, coldkey, hotkeyAddress, netuid, unstakeAmount, limitPrice, true); + + // Verify balance increased (received TAO from unstaking) + const balanceAfter = await getBalance(api, coldkeyAddress); + expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); + + log("✅ Successfully removed stake with limit (allow partial)."); + }, + }); + + it({ + id: "T02", + title: "should remove stake with price limit (fill or kill)", + test: async () => { + // Add stake first (100 TAO like benchmark) + await addStake(api, coldkey, hotkeyAddress, netuid, tao(100)); + + // Get initial stake and balance + const stakeBefore = await getStake(api, hotkeyAddress, coldkeyAddress, netuid); + const balanceBefore = await getBalance(api, coldkeyAddress); + log(`Stake before: ${stakeBefore}, Balance before: ${balanceBefore}`); + expect(stakeBefore, "Should have stake before removal").toBeGreaterThan(0n); + + // Remove stake with limit price (fill or kill mode) + const unstakeAmount = tao(30); + const limitPrice = tao(1); + await removeStakeLimit(api, coldkey, hotkeyAddress, netuid, unstakeAmount, limitPrice, false); + + // Verify balance increased (received TAO from unstaking) + const balanceAfter = await getBalance(api, coldkeyAddress); + expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); + + log("✅ Successfully removed stake with limit (fill or kill)."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/07-swap-stake.test.ts b/ts-tests/suites/zombienet_staking/07-swap-stake.test.ts new file mode 100644 index 0000000000..02739d06f0 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/07-swap-stake.test.ts @@ -0,0 +1,87 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + burnedRegister, + forceSetBalance, + generateKeyringPair, + getStake, + startCall, + sudoSetLockReductionInterval, + swapStake, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "07_swap_stake", + title: "▶ swap_stake extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + }); + + it({ + id: "T01", + title: "should swap stake from one subnet to another", + test: async () => { + // Setup accounts + const hotkey1 = generateKeyringPair("sr25519"); + const hotkey2 = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkey1Address = hotkey1.address; + const hotkey2Address = hotkey2.address; + const coldkeyAddress = coldkey.address; + + await sudoSetLockReductionInterval(api, 1); + await forceSetBalance(api, hotkey1Address); + await forceSetBalance(api, hotkey2Address); + await forceSetBalance(api, coldkeyAddress); + + // Create first subnet + const netuid1 = await addNewSubnetwork(api, hotkey1, coldkey); + await startCall(api, netuid1, coldkey); + + // Create second subnet + const netuid2 = await addNewSubnetwork(api, hotkey2, coldkey); + await startCall(api, netuid2, coldkey); + + // Register hotkey1 on subnet2 so we can swap stake there + await burnedRegister(api, netuid2, hotkey1Address, coldkey); + + // Add stake to hotkey1 on subnet1 + await addStake(api, coldkey, hotkey1Address, netuid1, tao(100)); + + // Get initial stakes + const stake1Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const stake2Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); + expect(stake1Before, "Should have stake on subnet1 before swap").toBeGreaterThan(0n); + + log(`Stake on netuid1 before: ${stake1Before}, Stake on netuid2 before: ${stake2Before}`); + + // Swap half the stake from subnet1 to subnet2 + // Use raw U64F64 value for the extrinsic + const stake1 = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const swapAmount = stake1 / 2n; + await swapStake(api, coldkey, hotkey1Address, netuid1, netuid2, swapAmount); + + // Verify stakes changed + const stake1After = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const stake2After = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); + + log(`Stake on netuid1 after: ${stake1After}, Stake on netuid2 after: ${stake2After}`); + + // Note: hotkey1 is the owner of netuid1, so minimum owner stake may be retained + expect(stake1After, "Stake on subnet1 should decrease after swap").toBeLessThan(stake1Before); + expect(stake2After, "Stake on subnet2 should increase after swap").toBeGreaterThan(stake2Before); + + log("✅ Successfully swapped stake from one subnet to another."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/08-swap-stake-limit.test.ts b/ts-tests/suites/zombienet_staking/08-swap-stake-limit.test.ts new file mode 100644 index 0000000000..34580b7212 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/08-swap-stake-limit.test.ts @@ -0,0 +1,142 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + burnedRegister, + forceSetBalance, + generateKeyringPair, + getStake, + startCall, + sudoSetLockReductionInterval, + swapStakeLimit, + tao, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "08_swap_stake_limit", + title: "▶ swap_stake_limit extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await sudoSetLockReductionInterval(api, 1); + }); + + it({ + id: "T01", + title: "should swap stake with price limit (allow partial)", + test: async () => { + // Setup accounts + const hotkey1 = generateKeyringPair("sr25519"); + const hotkey2 = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkey1Address = hotkey1.address; + const hotkey2Address = hotkey2.address; + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, hotkey1Address); + await forceSetBalance(api, hotkey2Address); + await forceSetBalance(api, coldkeyAddress); + + // Create first subnet + const netuid1 = await addNewSubnetwork(api, hotkey1, coldkey); + await startCall(api, netuid1, coldkey); + + // Create second subnet + const netuid2 = await addNewSubnetwork(api, hotkey2, coldkey); + await startCall(api, netuid2, coldkey); + + // Register hotkey1 on subnet2 so we can swap stake there + await burnedRegister(api, netuid2, hotkey1Address, coldkey); + + // Add stake to hotkey1 on subnet1 + await addStake(api, coldkey, hotkey1Address, netuid1, tao(100)); + + // Get initial stakes (converted from U64F64 for display) + const stake1Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const stake2Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); + expect(stake1Before, "Should have stake on subnet1 before swap").toBeGreaterThan(0n); + + log(`Stake on netuid1 before: ${stake1Before}, Stake on netuid2 before: ${stake2Before}`); + + // Swap stake with limit price (0.99 TAO relative price limit, allow partial fills) + const stake1 = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const swapAmount = stake1 / 2n; + const limitPrice = (tao(1) * 99n) / 100n; // 0.99 TAO + await swapStakeLimit(api, coldkey, hotkey1Address, netuid1, netuid2, swapAmount, limitPrice, true); + + // Verify stakes changed + const stake1After = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const stake2After = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); + + log(`Stake on netuid1 after: ${stake1After}, Stake on netuid2 after: ${stake2After}`); + + expect(stake1After, "Stake on subnet1 should decrease").toBeLessThan(stake1Before); + expect(stake2After, "Stake on subnet2 should increase").toBeGreaterThan(stake2Before); + + log("✅ Successfully swapped stake with price limit (allow partial)."); + }, + }); + + it({ + id: "T02", + title: "should swap stake with price limit (fill or kill)", + test: async () => { + // Setup accounts + const hotkey1 = generateKeyringPair("sr25519"); + const hotkey2 = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const hotkey1Address = hotkey1.address; + const hotkey2Address = hotkey2.address; + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, hotkey1Address); + await forceSetBalance(api, hotkey2Address); + await forceSetBalance(api, coldkeyAddress); + + // Create first subnet + const netuid1 = await addNewSubnetwork(api, hotkey1, coldkey); + await startCall(api, netuid1, coldkey); + + // Create second subnet + const netuid2 = await addNewSubnetwork(api, hotkey2, coldkey); + await startCall(api, netuid2, coldkey); + + // Register hotkey1 on subnet2 so we can swap stake there + await burnedRegister(api, netuid2, hotkey1Address, coldkey); + + // Add stake to hotkey1 on subnet1 + await addStake(api, coldkey, hotkey1Address, netuid1, tao(100)); + + // Get initial stakes (converted from U64F64 for display) + const stake1Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const stake2Before = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); + expect(stake1Before, "Should have stake on subnet1 before swap").toBeGreaterThan(0n); + + log(`Stake on netuid1 before: ${stake1Before}, Stake on netuid2 before: ${stake2Before}`); + + // Swap stake with limit price (fill or kill mode - allow_partial = false) + const stake1 = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const swapAmount = stake1 / 2n; + const limitPrice = tao(1) / 10n; // 0.1 TAO - permissive limit to allow slippage + await swapStakeLimit(api, coldkey, hotkey1Address, netuid1, netuid2, swapAmount, limitPrice, false); + + // Verify stakes changed + const stake1After = await getStake(api, hotkey1Address, coldkeyAddress, netuid1); + const stake2After = await getStake(api, hotkey1Address, coldkeyAddress, netuid2); + + log(`Stake on netuid1 after: ${stake1After}, Stake on netuid2 after: ${stake2After}`); + + expect(stake1After, "Stake on subnet1 should decrease").toBeLessThan(stake1Before); + expect(stake2After, "Stake on subnet2 should increase").toBeGreaterThan(stake2Before); + + log("✅ Successfully swapped stake with price limit (fill or kill)."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/09-transfer-stake.test.ts b/ts-tests/suites/zombienet_staking/09-transfer-stake.test.ts new file mode 100644 index 0000000000..a227f64835 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/09-transfer-stake.test.ts @@ -0,0 +1,152 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + forceSetBalance, + generateKeyringPair, + getStake, + startCall, + sudoSetLockReductionInterval, + tao, + transferStake, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "09_transfer_stake", + title: "▶ transfer_stake extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + }); + + it({ + id: "T01", + title: "should transfer stake to another coldkey across subnets", + test: async () => { + // Setup accounts + const hotkey1 = generateKeyringPair("sr25519"); + const hotkey2 = generateKeyringPair("sr25519"); + const originColdkey = generateKeyringPair("sr25519"); + const destinationColdkey = generateKeyringPair("sr25519"); + const hotkey1Address = hotkey1.address; + const hotkey2Address = hotkey2.address; + const originColdkeyAddress = originColdkey.address; + const destinationColdkeyAddress = destinationColdkey.address; + + await forceSetBalance(api, hotkey1Address); + await forceSetBalance(api, hotkey2Address); + await forceSetBalance(api, originColdkeyAddress); + await forceSetBalance(api, destinationColdkeyAddress); + + await sudoSetLockReductionInterval(api, 1); + // Create first subnet + const netuid1 = await addNewSubnetwork(api, hotkey1, originColdkey); + await startCall(api, netuid1, originColdkey); + + // Create second subnet + const netuid2 = await addNewSubnetwork(api, hotkey2, originColdkey); + await startCall(api, netuid2, originColdkey); + + // Add stake from origin coldkey on first subnet + await addStake(api, originColdkey, hotkey1Address, netuid1, tao(200)); + + // Get initial stakes (converted from U64F64 for display) + const originStakeBefore = await getStake(api, hotkey1Address, originColdkeyAddress, netuid1); + const destStakeBefore = await getStake(api, hotkey1Address, destinationColdkeyAddress, netuid2); + expect(originStakeBefore, "Origin should have stake before transfer").toBeGreaterThan(0n); + + log( + `Origin stake (netuid1) before: ${originStakeBefore}, Destination stake (netuid2) before: ${destStakeBefore}` + ); + + // Transfer stake to destination coldkey on a different subnet + const originStake = await getStake(api, hotkey1Address, originColdkeyAddress, netuid1); + const transferAmount = originStake / 2n; + await transferStake( + api, + originColdkey, + destinationColdkeyAddress, + hotkey1Address, + netuid1, + netuid2, + transferAmount + ); + + // Verify stakes changed + const originStakeAfter = await getStake(api, hotkey1Address, originColdkeyAddress, netuid1); + const destStakeAfter = await getStake(api, hotkey1Address, destinationColdkeyAddress, netuid2); + + log( + `Origin stake (netuid1) after: ${originStakeAfter}, Destination stake (netuid2) after: ${destStakeAfter}` + ); + + expect(originStakeAfter, "Origin stake should decrease").toBeLessThan(originStakeBefore); + expect(destStakeAfter, "Destination stake should increase").toBeGreaterThan(destStakeBefore); + + log("✅ Successfully transferred stake to another coldkey across subnets."); + }, + }); + + it({ + id: "T02", + title: "", + test: async () => { + // Setup accounts + const hotkey = generateKeyringPair("sr25519"); + const originColdkey = generateKeyringPair("sr25519"); + const destinationColdkey = generateKeyringPair("sr25519"); + const hotkeyAddress = hotkey.address; + const originColdkeyAddress = originColdkey.address; + const destinationColdkeyAddress = destinationColdkey.address; + + await forceSetBalance(api, hotkeyAddress); + await forceSetBalance(api, originColdkeyAddress); + await forceSetBalance(api, destinationColdkeyAddress); + + // Create subnet + const netuid = await addNewSubnetwork(api, hotkey, originColdkey); + await startCall(api, netuid, originColdkey); + + // Add stake from origin coldkey + const stakeAmount = tao(100); + await addStake(api, originColdkey, hotkeyAddress, netuid, stakeAmount); + + // Get initial stake (converted from U64F64 for display) + const originStakeBefore = await getStake(api, hotkeyAddress, originColdkeyAddress, netuid); + expect(originStakeBefore, "Origin should have stake before transfer").toBeGreaterThan(0n); + + log(`Origin stake before: ${originStakeBefore}`); + + // Transfer stake to destination coldkey + const originStake = await getStake(api, hotkeyAddress, originColdkeyAddress, netuid); + const transferAmount = originStake / 2n; + await transferStake( + api, + originColdkey, + destinationColdkeyAddress, + hotkeyAddress, + netuid, + netuid, + transferAmount + ); + + // Verify destination received stake + const originStakeAfter = await getStake(api, hotkeyAddress, originColdkeyAddress, netuid); + const destStakeAfter = await getStake(api, hotkeyAddress, destinationColdkeyAddress, netuid); + + log(`Origin stake after: ${originStakeAfter}, Destination stake after: ${destStakeAfter}`); + + expect(originStakeAfter, "Origin stake should decrease after transfer").toBeLessThan(originStakeBefore); + expect(destStakeAfter, "Destination stake should be non-zero after transfer").toBeGreaterThan(0n); + + log("✅ Successfully transferred stake to another coldkey."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/10-unstake-all.test.ts b/ts-tests/suites/zombienet_staking/10-unstake-all.test.ts new file mode 100644 index 0000000000..fffb975199 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/10-unstake-all.test.ts @@ -0,0 +1,102 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + burnedRegister, + forceSetBalance, + generateKeyringPair, + getBalance, + getStake, + startCall, + sudoSetLockReductionInterval, + sudoSetTempo, + tao, + unstakeAll, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "10_unstake_all", + title: "▶ unstake_all extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + }); + + it({ + id: "T01", + title: "", + test: async () => { + // Setup accounts + // - owner1Hotkey/coldkey: owns subnet 1 + // - owner2Hotkey/coldkey: owns subnet 2 + // - stakerHotkey: staker (not owner) on both subnets - used for testing unstake_all + const owner1Hotkey = generateKeyringPair("sr25519"); + const owner2Hotkey = generateKeyringPair("sr25519"); + const stakerHotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const owner1Address = owner1Hotkey.address; + const owner2Address = owner2Hotkey.address; + const stakerAddress = stakerHotkey.address; + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, owner1Address); + await forceSetBalance(api, owner2Address); + await forceSetBalance(api, stakerAddress); + await forceSetBalance(api, coldkeyAddress); + + await sudoSetLockReductionInterval(api, 1); + // Create first subnet with owner1 + const netuid1 = await addNewSubnetwork(api, owner1Hotkey, coldkey); + await startCall(api, netuid1, coldkey); + + // Create second subnet with owner2 + const netuid2 = await addNewSubnetwork(api, owner2Hotkey, coldkey); + await startCall(api, netuid2, coldkey); + + // Set high tempo to prevent emissions during test + await sudoSetTempo(api, netuid1, 10000); + await sudoSetTempo(api, netuid2, 10000); + + // Register stakerHotkey on both subnets (it's not the owner) + await burnedRegister(api, netuid1, stakerAddress, coldkey); + await burnedRegister(api, netuid2, stakerAddress, coldkey); + + // Add stake to both subnets using stakerHotkey (not the owner) + await addStake(api, coldkey, stakerAddress, netuid1, tao(100)); + await addStake(api, coldkey, stakerAddress, netuid2, tao(50)); + + // Verify stake was added to both subnets + const stake1Before = await getStake(api, stakerAddress, coldkeyAddress, netuid1); + const stake2Before = await getStake(api, stakerAddress, coldkeyAddress, netuid2); + const balanceBefore = await getBalance(api, coldkeyAddress); + + expect(stake1Before, "Should have stake in subnet 1 before unstake_all").toBeGreaterThan(0n); + expect(stake2Before, "Should have stake in subnet 2 before unstake_all").toBeGreaterThan(0n); + log(`Stake1 before: ${stake1Before}, Stake2 before: ${stake2Before}, Balance before: ${balanceBefore}`); + + // Unstake all + await unstakeAll(api, coldkey, stakerAddress); + + // Verify stakes are removed from both subnets and balance increased + const stake1After = await getStake(api, stakerAddress, coldkeyAddress, netuid1); + const stake2After = await getStake(api, stakerAddress, coldkeyAddress, netuid2); + const balanceAfter = await getBalance(api, coldkeyAddress); + + log(`Stake1 after: ${stake1After}, Stake2 after: ${stake2After}, Balance after: ${balanceAfter}`); + + // Since stakerHotkey is not the owner of either subnet, all stake should be removed + expect(stake1After, "Stake1 should be zero after unstake_all").toBe(0n); + expect(stake2After, "Stake2 should be zero after unstake_all").toBe(0n); + expect(balanceAfter, "Balance should increase after unstaking").toBeGreaterThan(balanceBefore); + + log("✅ Successfully unstaked all from multiple subnets."); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_staking/11-unstake-all-alpha.test.ts b/ts-tests/suites/zombienet_staking/11-unstake-all-alpha.test.ts new file mode 100644 index 0000000000..f372f1d746 --- /dev/null +++ b/ts-tests/suites/zombienet_staking/11-unstake-all-alpha.test.ts @@ -0,0 +1,99 @@ +import { expect, beforeAll } from "vitest"; +import { describeSuite } from "@moonwall/cli"; +import { + addNewSubnetwork, + addStake, + burnedRegister, + forceSetBalance, + generateKeyringPair, + getStake, + startCall, + sudoSetLockReductionInterval, + sudoSetTempo, + tao, + unstakeAllAlpha, +} from "../../utils"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +describeSuite({ + id: "11_unstake_all_alpha", + title: "▶ unstake_all_alpha extrinsic", + foundationMethods: "zombie", + testCases: ({ it, context, log }) => { + let api: TypedApi<typeof subtensor>; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + }); + + it({ + id: "T01", + title: "should unstake all alpha from multiple subnets and restake to root", + test: async () => { + // Setup accounts + // - owner1/coldkey: owns subnet 1 + // - owner2/coldkey: owns subnet 2 + // - stakerHotkey: staker (not owner) on both subnets - used for testing unstake_all_alpha + const owner1Hotkey = generateKeyringPair("sr25519"); + const owner2Hotkey = generateKeyringPair("sr25519"); + const stakerHotkey = generateKeyringPair("sr25519"); + const coldkey = generateKeyringPair("sr25519"); + const owner1Address = owner1Hotkey.address; + const owner2Address = owner2Hotkey.address; + const stakerAddress = stakerHotkey.address; + const coldkeyAddress = coldkey.address; + + await forceSetBalance(api, owner1Address); + await forceSetBalance(api, owner2Address); + await forceSetBalance(api, stakerAddress); + await forceSetBalance(api, coldkeyAddress); + + await sudoSetLockReductionInterval(api, 1); + // Create first subnet with owner1 + const netuid1 = await addNewSubnetwork(api, owner1Hotkey, coldkey); + await startCall(api, netuid1, coldkey); + + // Create second subnet with owner2 + const netuid2 = await addNewSubnetwork(api, owner2Hotkey, coldkey); + await startCall(api, netuid2, coldkey); + + // Set very high tempo to prevent emissions during test + await sudoSetTempo(api, netuid1, 10000); + await sudoSetTempo(api, netuid2, 10000); + + // Register stakerHotkey on both subnets (it's not the owner) + await burnedRegister(api, netuid1, stakerAddress, coldkey); + await burnedRegister(api, netuid2, stakerAddress, coldkey); + + // Add stake to both subnets using stakerHotkey (not the owner) + await addStake(api, coldkey, stakerAddress, netuid1, tao(100)); + await addStake(api, coldkey, stakerAddress, netuid2, tao(50)); + + // Verify stake was added to both subnets + const stake1Before = await getStake(api, stakerAddress, coldkeyAddress, netuid1); + const stake2Before = await getStake(api, stakerAddress, coldkeyAddress, netuid2); + + expect(stake1Before, "Should have stake in subnet 1 before unstake_all_alpha").toBeGreaterThan(0n); + expect(stake2Before, "Should have stake in subnet 2 before unstake_all_alpha").toBeGreaterThan(0n); + log(`Stake1 before: ${stake1Before}, Stake2 before: ${stake2Before}`); + + // Unstake all alpha - this removes stake from dynamic subnets and restakes to root + await unstakeAllAlpha(api, coldkey, stakerAddress); + + // Verify stakes are removed from both dynamic subnets + const stake1After = await getStake(api, stakerAddress, coldkeyAddress, netuid1); + const stake2After = await getStake(api, stakerAddress, coldkeyAddress, netuid2); + + log(`Stake1 after: ${stake1After}, Stake2 after: ${stake2After}`); + + // Since stakerHotkey is not the owner of either subnet, all stake should be removed + // High tempo prevents emissions during test, so expect exact zero + expect(stake1After, "Stake1 should be zero after unstake_all_alpha").toBe(0n); + expect(stake2After, "Stake2 should be zero after unstake_all_alpha").toBe(0n); + + log("✅ Successfully unstaked all alpha from multiple subnets to root."); + }, + }); + }, +}); diff --git a/ts-tests/tsconfig.json b/ts-tests/tsconfig.json new file mode 100644 index 0000000000..1459c6bf7d --- /dev/null +++ b/ts-tests/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "ESNext", + "target": "ESNext", + "baseUrl": "./", + "moduleResolution": "Bundler", + "importHelpers": true, + "skipLibCheck": true, + "removeComments": true, + "noEmit": true, + "preserveConstEnums": true, + "sourceMap": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, + "allowImportingTsExtensions": true + }, + "include": [ + "*suites/**/*.ts" + ], + "exclude": ["node_modules/"] +} \ No newline at end of file diff --git a/ts-tests/utils/account.ts b/ts-tests/utils/account.ts new file mode 100644 index 0000000000..38137aa708 --- /dev/null +++ b/ts-tests/utils/account.ts @@ -0,0 +1,20 @@ +import type { KeyringPair } from "@moonwall/util"; +import type { PolkadotSigner, TypedApi } from "polkadot-api"; +import type { subtensor } from "@polkadot-api/descriptors"; +import { getPolkadotSigner } from "polkadot-api/signer"; +import { mnemonicGenerate } from "@polkadot/util-crypto"; +import { Keyring } from "@polkadot/keyring"; + +export const getAccountNonce = async (api: TypedApi<typeof subtensor>, address: string): Promise<number> => { + const account = await api.query.System.Account.getValue(address, { at: "best" }); + return account.nonce; +}; + +export function getSignerFromKeypair(keypair: KeyringPair): PolkadotSigner { + return getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign); +} + +export function generateKeyringPair(type: "sr25519" | "ed25519" = "sr25519"): KeyringPair { + const keyring = new Keyring({ type }); + return keyring.addFromMnemonic(mnemonicGenerate()); +} diff --git a/ts-tests/utils/balance.ts b/ts-tests/utils/balance.ts new file mode 100644 index 0000000000..f6fe83d3b0 --- /dev/null +++ b/ts-tests/utils/balance.ts @@ -0,0 +1,30 @@ +import { waitForTransactionWithRetry } from "./transactions.js"; +import type { TypedApi } from "polkadot-api"; +import { type subtensor, MultiAddress } from "@polkadot-api/descriptors"; +import { Keyring } from "@polkadot/keyring"; + +export const TAO = BigInt(1000000000); // 10^9 RAO per TAO + +export function tao(value: number): bigint { + return TAO * BigInt(value); +} + +export async function getBalance(api: TypedApi<typeof subtensor>, ss58Address: string): Promise<bigint> { + const account = await api.query.System.Account.getValue(ss58Address); + return account.data.free; +} + +export async function forceSetBalance( + api: TypedApi<typeof subtensor>, + ss58Address: string, + amount: bigint = tao(1e10) +): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.Balances.force_set_balance({ + who: MultiAddress.Id(ss58Address), + new_free: amount, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "force_set_balance"); +} diff --git a/ts-tests/utils/index.ts b/ts-tests/utils/index.ts new file mode 100644 index 0000000000..7e9a6b4d5e --- /dev/null +++ b/ts-tests/utils/index.ts @@ -0,0 +1,6 @@ +export * from "./transactions.js"; +export * from "./balance.js"; +export * from "./subnet.js"; +export * from "./staking.js"; +export * from "./shield_helpers.ts"; +export * from "./account.ts"; diff --git a/ts-tests/utils/logger.ts b/ts-tests/utils/logger.ts new file mode 100644 index 0000000000..550026e167 --- /dev/null +++ b/ts-tests/utils/logger.ts @@ -0,0 +1,7 @@ +const LOG_INDENT = " "; + +export const log = { + tx: (label: string, msg: string) => console.log(`${LOG_INDENT}[${label}] ${msg}`), + info: (msg: string) => console.log(`${LOG_INDENT}${msg}`), + error: (label: string, msg: string) => console.error(`${LOG_INDENT}[${label}] ${msg}`), +}; diff --git a/ts-tests/utils/shield_helpers.ts b/ts-tests/utils/shield_helpers.ts new file mode 100644 index 0000000000..24656b1208 --- /dev/null +++ b/ts-tests/utils/shield_helpers.ts @@ -0,0 +1,88 @@ +import type { KeyringPair } from "@moonwall/util"; +import { xxhashAsU8a } from "@polkadot/util-crypto"; +import { randomBytes } from "ethers"; +import { xchacha20poly1305 } from "@noble/ciphers/chacha.js"; +import { MlKem768 } from "mlkem"; +import { type TypedApi, Binary } from "polkadot-api"; +import type { subtensor } from "@polkadot-api/descriptors"; +import { getSignerFromKeypair } from "./account.ts"; +import { waitForFinalizedBlocks } from "./transactions.ts"; +import { hexToU8a } from "@polkadot/util"; + +export const getNextKey = async (api: TypedApi<typeof subtensor>): Promise<Uint8Array | undefined> => { + // Query at "best" (not default "finalized") because keys rotate every block + // and finalized lags ~2 blocks behind best with GRANDPA. Using finalized + // would return a stale key whose hash won't match CurrentKey/NextKey at + // block-building time, causing InvalidShieldedTxPubKeyHash rejection. + const key = await api.query.MevShield.NextKey.getValue({ at: "best" }); + if (!key) return undefined; + if (key instanceof Binary) return key.asBytes(); + return hexToU8a(key as string); +}; + +export const checkRuntime = async (api: TypedApi<typeof subtensor>) => { + const ts1 = await api.query.Timestamp.Now.getValue(); + + await waitForFinalizedBlocks(api, 1); + + const ts2 = await api.query.Timestamp.Now.getValue(); + + const blockTimeMs = ts2 - ts1; + + const MIN_BLOCK_TIME_MS = 6000; + // We check at least half of the block time length + if (blockTimeMs < MIN_BLOCK_TIME_MS) { + throw new Error( + `Fast runtime detected (block time ~${blockTimeMs}ms < ${MIN_BLOCK_TIME_MS}ms). Rebuild with normal runtime before running MEV Shield tests.` + ); + } +}; + +export const getCurrentKey = async (api: TypedApi<typeof subtensor>): Promise<Uint8Array | undefined> => { + const key = await api.query.MevShield.CurrentKey.getValue({ at: "best" }); + if (!key) return undefined; + if (key instanceof Binary) return key.asBytes(); + return hexToU8a(key as string); +}; + +export const encryptTransaction = async (plaintext: Uint8Array, publicKey: Uint8Array): Promise<Uint8Array> => { + const keyHash = xxhashAsU8a(publicKey, 128); + + const mlKem = new MlKem768(); + const [kemCt, sharedSecret] = await mlKem.encap(publicKey); + + const nonce = randomBytes(24); + const chacha = xchacha20poly1305(sharedSecret, nonce); + const aeadCt = chacha.encrypt(plaintext); + + const kemLenBytes = new Uint8Array(2); + new DataView(kemLenBytes.buffer).setUint16(0, kemCt.length, true); + + return new Uint8Array([...keyHash, ...kemLenBytes, ...kemCt, ...nonce, ...aeadCt]); +}; + +export const submitEncrypted = async ( + api: TypedApi<typeof subtensor>, + signer: KeyringPair, + innerTxBytes: Uint8Array, + publicKey: Uint8Array, + nonce?: number +) => { + const ciphertext = await encryptTransaction(innerTxBytes, publicKey); + return submitEncryptedRaw(api, signer, ciphertext, nonce); +}; + +export const submitEncryptedRaw = async ( + api: TypedApi<typeof subtensor>, + signer: KeyringPair, + ciphertext: Uint8Array, + nonce?: number +) => { + const tx = api.tx.MevShield.submit_encrypted({ + ciphertext: Binary.fromBytes(ciphertext), + }); + return tx.signAndSubmit(getSignerFromKeypair(signer), { + ...(nonce !== undefined ? { nonce } : {}), + mortality: { mortal: true, period: 8 }, + }); +}; diff --git a/ts-tests/utils/staking.ts b/ts-tests/utils/staking.ts new file mode 100644 index 0000000000..4efdc19802 --- /dev/null +++ b/ts-tests/utils/staking.ts @@ -0,0 +1,439 @@ +import { waitForTransactionWithRetry } from "./transactions.js"; +import type { KeyringPair } from "@moonwall/util"; +import type { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; +import { Keyring } from "@polkadot/keyring"; + +export async function addStake( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string, + netuid: number, + amount: bigint +): Promise<void> { + const tx = api.tx.SubtensorModule.add_stake({ + hotkey: hotkey, + netuid: netuid, + amount_staked: amount, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "add_stake"); +} + +export async function addStakeLimit( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string, + netuid: number, + amount: bigint, + limitPrice: bigint, + allowPartial: boolean +): Promise<void> { + const tx = api.tx.SubtensorModule.add_stake_limit({ + hotkey: hotkey, + netuid: netuid, + amount_staked: amount, + limit_price: limitPrice, + allow_partial: allowPartial, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "add_stake_limit"); +} + +export async function removeStake( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string, + netuid: number, + amount: bigint +): Promise<void> { + const tx = api.tx.SubtensorModule.remove_stake({ + hotkey: hotkey, + netuid: netuid, + amount_unstaked: amount, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "remove_stake"); +} + +export async function removeStakeLimit( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string, + netuid: number, + amount: bigint, + limitPrice: bigint, + allowPartial: boolean +): Promise<void> { + const tx = api.tx.SubtensorModule.remove_stake_limit({ + hotkey: hotkey, + netuid: netuid, + amount_unstaked: amount, + limit_price: limitPrice, + allow_partial: allowPartial, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "remove_stake_limit"); +} + +export async function removeStakeFullLimit( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string, + netuid: number, + limitPrice: bigint | undefined +): Promise<void> { + const tx = api.tx.SubtensorModule.remove_stake_full_limit({ + hotkey: hotkey, + netuid: netuid, + limit_price: limitPrice, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "remove_stake_full_limit"); +} + +export async function unstakeAll(api: TypedApi<typeof subtensor>, coldkey: KeyringPair, hotkey: string): Promise<void> { + const tx = api.tx.SubtensorModule.unstake_all({ + hotkey: hotkey, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "unstake_all"); +} + +export async function unstakeAllAlpha( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string +): Promise<void> { + const tx = api.tx.SubtensorModule.unstake_all_alpha({ + hotkey: hotkey, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "unstake_all_alpha"); +} + +/** + * Get stake shares (Alpha) for a hotkey/coldkey/netuid triplet. + * Returns the integer part of the U64F64 value. + */ +export async function getStake( + api: TypedApi<typeof subtensor>, + hotkey: string, + coldkey: string, + netuid: number +): Promise<bigint> { + const value = await api.query.SubtensorModule.AlphaV2.getValue(hotkey, coldkey, netuid); + + const mantissa = value.mantissa; + const exponent = value.exponent; + + let result: bigint; + + if (exponent >= 0) { + result = mantissa * BigInt(10) ** exponent; + } else { + result = mantissa / BigInt(10) ** -exponent; + } + + return result; +} + +/** + * Get raw stake shares (Alpha) in U64F64 format. + * Use this when you need the raw value for extrinsics like transfer_stake. + */ +export async function getStakeRaw( + api: TypedApi<typeof subtensor>, + hotkey: string, + coldkey: string, + netuid: number +): Promise<bigint> { + return await api.query.SubtensorModule.Alpha.getValue(hotkey, coldkey, netuid); +} + +export async function transferStake( + api: TypedApi<typeof subtensor>, + originColdkey: KeyringPair, + destinationColdkey: string, + hotkey: string, + originNetuid: number, + destinationNetuid: number, + amount: bigint +): Promise<void> { + const tx = api.tx.SubtensorModule.transfer_stake({ + destination_coldkey: destinationColdkey, + hotkey: hotkey, + origin_netuid: originNetuid, + destination_netuid: destinationNetuid, + alpha_amount: amount, + }); + await waitForTransactionWithRetry(api, tx, originColdkey, "transfer_stake"); +} + +export async function moveStake( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + originHotkey: string, + destinationHotkey: string, + originNetuid: number, + destinationNetuid: number, + amount: bigint +): Promise<void> { + const tx = api.tx.SubtensorModule.move_stake({ + origin_hotkey: originHotkey, + destination_hotkey: destinationHotkey, + origin_netuid: originNetuid, + destination_netuid: destinationNetuid, + alpha_amount: amount, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "move_stake"); +} + +export async function swapStake( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string, + originNetuid: number, + destinationNetuid: number, + amount: bigint +): Promise<void> { + const tx = api.tx.SubtensorModule.swap_stake({ + hotkey: hotkey, + origin_netuid: originNetuid, + destination_netuid: destinationNetuid, + alpha_amount: amount, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "swap_stake"); +} + +export async function swapStakeLimit( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + hotkey: string, + originNetuid: number, + destinationNetuid: number, + amount: bigint, + limitPrice: bigint, + allowPartial: boolean +): Promise<void> { + const tx = api.tx.SubtensorModule.swap_stake_limit({ + hotkey: hotkey, + origin_netuid: originNetuid, + destination_netuid: destinationNetuid, + alpha_amount: amount, + limit_price: limitPrice, + allow_partial: allowPartial, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "swap_stake_limit"); +} + +export type RootClaimType = "Swap" | "Keep" | { type: "KeepSubnets"; subnets: number[] }; + +export async function getRootClaimType(api: TypedApi<typeof subtensor>, coldkey: string): Promise<RootClaimType> { + const result = await api.query.SubtensorModule.RootClaimType.getValue(coldkey); + if (result.type === "KeepSubnets") { + return { type: "KeepSubnets", subnets: result.value.subnets as number[] }; + } + return result.type as "Swap" | "Keep"; +} + +export async function setRootClaimType( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + claimType: RootClaimType +): Promise<void> { + let newRootClaimType; + if (typeof claimType === "string") { + newRootClaimType = { type: claimType, value: undefined }; + } else { + newRootClaimType = { type: "KeepSubnets", value: { subnets: claimType.subnets } }; + } + const tx = api.tx.SubtensorModule.set_root_claim_type({ + new_root_claim_type: newRootClaimType, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "set_root_claim_type"); +} + +export async function claimRoot( + api: TypedApi<typeof subtensor>, + coldkey: KeyringPair, + subnets: number[] +): Promise<void> { + const tx = api.tx.SubtensorModule.claim_root({ + subnets: subnets, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "claim_root"); +} + +export async function getNumRootClaims(api: TypedApi<typeof subtensor>): Promise<bigint> { + return await api.query.SubtensorModule.NumRootClaim.getValue(); +} + +export async function sudoSetNumRootClaims(api: TypedApi<typeof subtensor>, newValue: bigint): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.SubtensorModule.sudo_set_num_root_claims({ + new_value: newValue, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_num_root_claims"); +} + +export async function getRootClaimThreshold(api: TypedApi<typeof subtensor>, netuid: number): Promise<bigint> { + return await api.query.SubtensorModule.RootClaimableThreshold.getValue(netuid); +} + +export async function sudoSetRootClaimThreshold( + api: TypedApi<typeof subtensor>, + netuid: number, + newValue: bigint +): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.SubtensorModule.sudo_set_root_claim_threshold({ + netuid: netuid, + new_value: newValue, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_root_claim_threshold"); +} + +export async function getTempo(api: TypedApi<typeof subtensor>, netuid: number): Promise<number> { + return await api.query.SubtensorModule.Tempo.getValue(netuid); +} + +export async function sudoSetTempo(api: TypedApi<typeof subtensor>, netuid: number, tempo: number): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_tempo({ + netuid: netuid, + tempo: tempo, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_tempo"); +} + +export async function waitForBlocks(api: TypedApi<typeof subtensor>, numBlocks: number): Promise<void> { + const startBlock = await api.query.System.Number.getValue(); + const targetBlock = startBlock + numBlocks; + + while (true) { + const currentBlock = await api.query.System.Number.getValue(); + if (currentBlock >= targetBlock) { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } +} + +export async function getRootClaimable(api: TypedApi<typeof subtensor>, hotkey: string): Promise<Map<number, bigint>> { + const result = await api.query.SubtensorModule.RootClaimable.getValue(hotkey); + const claimableMap = new Map<number, bigint>(); + for (const [netuid, amount] of result) { + claimableMap.set(netuid, amount); + } + return claimableMap; +} + +export async function getRootClaimed( + api: TypedApi<typeof subtensor>, + netuid: number, + hotkey: string, + coldkey: string +): Promise<bigint> { + return await api.query.SubtensorModule.RootClaimed.getValue(netuid, hotkey, coldkey); +} + +export async function isSubtokenEnabled(api: TypedApi<typeof subtensor>, netuid: number): Promise<boolean> { + return await api.query.SubtensorModule.SubtokenEnabled.getValue(netuid); +} + +export async function sudoSetSubtokenEnabled( + api: TypedApi<typeof subtensor>, + netuid: number, + enabled: boolean +): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_subtoken_enabled({ + netuid: netuid, + subtoken_enabled: enabled, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_subtoken_enabled"); +} + +export async function isNetworkAdded(api: TypedApi<typeof subtensor>, netuid: number): Promise<boolean> { + return await api.query.SubtensorModule.NetworksAdded.getValue(netuid); +} + +export async function getAdminFreezeWindow(api: TypedApi<typeof subtensor>): Promise<number> { + return await api.query.SubtensorModule.AdminFreezeWindow.getValue(); +} + +export async function sudoSetAdminFreezeWindow(api: TypedApi<typeof subtensor>, window: number): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_admin_freeze_window({ + window: window, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_admin_freeze_window"); +} + +export async function sudoSetEmaPriceHalvingPeriod( + api: TypedApi<typeof subtensor>, + netuid: number, + emaPriceHalvingPeriod: number +): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_ema_price_halving_period({ + netuid: netuid, + ema_halving: BigInt(emaPriceHalvingPeriod), + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_ema_price_halving_period"); +} + +export async function sudoSetLockReductionInterval(api: TypedApi<typeof subtensor>, interval: number): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_lock_reduction_interval({ + interval: BigInt(interval), + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_lock_reduction_interval"); +} + +export async function sudoSetSubnetMovingAlpha(api: TypedApi<typeof subtensor>, alpha: bigint): Promise<void> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_subnet_moving_alpha({ + alpha: alpha, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_subnet_moving_alpha"); +} + +// Debug helpers for claim_root investigation +export async function getSubnetTAO(api: TypedApi<typeof subtensor>, netuid: number): Promise<bigint> { + return await api.query.SubtensorModule.SubnetTAO.getValue(netuid); +} + +export async function getSubnetMovingPrice(api: TypedApi<typeof subtensor>, netuid: number): Promise<bigint> { + return await api.query.SubtensorModule.SubnetMovingPrice.getValue(netuid); +} + +export async function getPendingRootAlphaDivs(api: TypedApi<typeof subtensor>, netuid: number): Promise<bigint> { + return await api.query.SubtensorModule.PendingRootAlphaDivs.getValue(netuid); +} + +export async function getTaoWeight(api: TypedApi<typeof subtensor>): Promise<bigint> { + return await api.query.SubtensorModule.TaoWeight.getValue(); +} + +export async function getSubnetAlphaIn(api: TypedApi<typeof subtensor>, netuid: number): Promise<bigint> { + return await api.query.SubtensorModule.SubnetAlphaIn.getValue(netuid); +} + +export async function getTotalHotkeyAlpha( + api: TypedApi<typeof subtensor>, + hotkey: string, + netuid: number +): Promise<bigint> { + return await api.query.SubtensorModule.TotalHotkeyAlpha.getValue(hotkey, netuid); +} diff --git a/ts-tests/utils/subnet.ts b/ts-tests/utils/subnet.ts new file mode 100644 index 0000000000..ec1d39ff84 --- /dev/null +++ b/ts-tests/utils/subnet.ts @@ -0,0 +1,66 @@ +import { waitForTransactionWithRetry } from "./transactions.js"; +import { log } from "./logger.js"; +import type { KeyringPair } from "@moonwall/util"; +import { Keyring } from "@polkadot/keyring"; +import type { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +export async function addNewSubnetwork( + api: TypedApi<typeof subtensor>, + hotkey: KeyringPair, + coldkey: KeyringPair +): Promise<number> { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const totalNetworks = await api.query.SubtensorModule.TotalNetworks.getValue(); + + // Disable network rate limit for testing + const rateLimit = await api.query.SubtensorModule.NetworkRateLimit.getValue(); + if (rateLimit !== BigInt(0)) { + const internalCall = api.tx.AdminUtils.sudo_set_network_rate_limit({ rate_limit: BigInt(0) }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "set_network_rate_limit"); + } + + const registerNetworkTx = api.tx.SubtensorModule.register_network({ + hotkey: hotkey.address, + }); + await waitForTransactionWithRetry(api, registerNetworkTx, coldkey, "register_network"); + + return totalNetworks; +} + +export async function burnedRegister( + api: TypedApi<typeof subtensor>, + netuid: number, + hotkeyAddress: string, + coldkey: KeyringPair +): Promise<void> { + const registered = await api.query.SubtensorModule.Uids.getValue(netuid, hotkeyAddress); + if (registered !== undefined) { + log.tx("burned_register", `skipped: hotkey already registered on netuid ${netuid}`); + return; + } + + await new Promise((resolve) => setTimeout(resolve, 1000)); + const tx = api.tx.SubtensorModule.burned_register({ hotkey: hotkeyAddress, netuid: netuid }); + await waitForTransactionWithRetry(api, tx, coldkey, "burned_register"); +} + +export async function startCall(api: TypedApi<typeof subtensor>, netuid: number, coldkey: KeyringPair): Promise<void> { + const registerBlock = Number(await api.query.SubtensorModule.NetworkRegisteredAt.getValue(netuid)); + let currentBlock = await api.query.System.Number.getValue(); + const duration = Number(await api.constants.SubtensorModule.InitialStartCallDelay); + + while (currentBlock - registerBlock <= duration) { + await new Promise((resolve) => setTimeout(resolve, 2000)); + currentBlock = await api.query.System.Number.getValue(); + } + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const tx = api.tx.SubtensorModule.start_call({ netuid: netuid }); + await waitForTransactionWithRetry(api, tx, coldkey, "start_call"); + + await new Promise((resolve) => setTimeout(resolve, 1000)); +} diff --git a/ts-tests/utils/transactions.ts b/ts-tests/utils/transactions.ts new file mode 100644 index 0000000000..2842edfd56 --- /dev/null +++ b/ts-tests/utils/transactions.ts @@ -0,0 +1,113 @@ +import { log } from "./logger.js"; +import type { KeyringPair } from "@moonwall/util"; +import { sleep } from "@zombienet/utils"; +import { waitForBlocks } from "./staking.ts"; +import type { Transaction, TypedApi } from "polkadot-api"; +import type { subtensor } from "@polkadot-api/descriptors"; +import { getPolkadotSigner } from "polkadot-api/signer"; + +export async function waitForTransactionWithRetry( + api: TypedApi<typeof subtensor>, + tx: Transaction<Record<string, unknown>, string, string, void>, + signer: KeyringPair, + label: string, + maxRetries = 1 +): Promise<void> { + let retries = 0; + + while (retries < maxRetries) { + try { + await waitForTransactionCompletion(tx, signer); + return; + } catch (error) { + log.tx(label, `attempt ${retries + 1} failed: ${error}`); + retries += 1; + if (retries >= maxRetries) { + throw new Error(`[${label}] failed after ${maxRetries} retries`); + } + await waitForBlocks(api, 1); + } + } +} + +export async function waitForTransactionCompletion( + tx: Transaction<Record<string, unknown>, string, string, void>, + keypair: KeyringPair, + timeout: number | null = 3 * 60 * 1000 +): Promise<{ txHash: string; blockHash: string }> { + const callerStack = new Error().stack; + + const signer = getPolkadotSigner(keypair.publicKey, "Sr25519", keypair.sign); + + const signSubmitAndWatchInner = (): Promise<{ txHash: string; blockHash: string }> => { + return new Promise((resolve, reject) => { + const subscription = tx.signSubmitAndWatch(signer).subscribe({ + next(event) { + if (event.type === "finalized") { + subscription.unsubscribe(); + + const failed = event.dispatchError; + if (failed) { + reject(new Error(`ExtrinsicFailed: ${JSON.stringify(failed)}`)); + } else { + resolve({ + txHash: event.txHash, + blockHash: event.block.hash, + }); + } + } + }, + error(err) { + console.error("callerStack", callerStack); + reject(err instanceof Error ? err : new Error(String(err))); + }, + }); + }); + }; + + if (timeout === null) { + return signSubmitAndWatchInner(); + } + + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + console.log("Transaction timed out"); + console.error("callerStack", callerStack); + reject(new Error("Transaction timed out")); + }, timeout); + + signSubmitAndWatchInner() + .then((result) => { + clearTimeout(timer); + resolve(result); + }) + .catch((error) => { + clearTimeout(timer); + reject(error instanceof Error ? error : new Error(String(error))); + }); + }); +} + +const SECOND = 1000; + +/** Polls the chain until `count` new finalized blocks have been produced. */ +export async function waitForFinalizedBlocks( + api: TypedApi<typeof subtensor>, + count: number, + pollInterval = 1 * SECOND, + timeout = 120 * SECOND +): Promise<void> { + const startBlock = await api.query.System.Number.getValue({ at: "finalized" }); + const target = startBlock + count; + const deadline = Date.now() + timeout; + + while (Date.now() < deadline) { + await sleep(pollInterval); + + const currentBlock = await api.query.System.Number.getValue({ at: "finalized" }); + + if (currentBlock >= target) return; + } + + throw new Error(`Timed out waiting for ${count} finalized blocks (from #${startBlock}, target #${target})`); +}