diff --git a/.github/workflows/contract-tests.yml b/.github/workflows/contract-tests.yml deleted file mode 100644 index a8182097a7..0000000000 --- a/.github/workflows/contract-tests.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Contract E2E Tests - -on: - pull_request: - - ## Allow running workflow manually from the Actions tab - workflow_dispatch: - inputs: - verbose: - description: "Output more information when triggered manually" - required: false - default: "" - -concurrency: - group: evm-tests-${{ github.ref }} - cancel-in-progress: true - -env: - CARGO_TERM_COLOR: always - VERBOSE: ${{ github.events.input.verbose }} - -permissions: - contents: read - -jobs: - run: - runs-on: [self-hosted, fireactions-light] - env: - RUST_BACKTRACE: full - steps: - - name: Check-out repository under $GITHUB_WORKSPACE - uses: actions/checkout@v4 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - - name: Utilize Shared Rust Cache - uses: Swatinem/rust-cache@v2 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - - - 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 libssl-dev llvm libudev-dev protobuf-compiler nodejs pkg-config - - - name: Run tests - uses: nick-fields/retry@v3 - with: - timeout_minutes: 120 - max_attempts: 3 - retry_wait_seconds: 60 - command: | - cd ${{ github.workspace }} - npm install --global yarn - ./contract-tests/run-ci.sh diff --git a/.github/workflows/eco-tests-indexer-notify.yml b/.github/workflows/eco-tests-indexer-notify.yml new file mode 100644 index 0000000000..e55bbdf859 --- /dev/null +++ b/.github/workflows/eco-tests-indexer-notify.yml @@ -0,0 +1,140 @@ +name: on eco-tests change notification + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + paths: + - 'eco-tests/**' + +permissions: + contents: read + pull-requests: write + issues: write + +concurrency: + group: eco-tests-indexer-notify-${{ github.ref }} + cancel-in-progress: true + +env: + ECO_TESTS_REVIEWERS: "evgeny-s" + +jobs: + notify: + name: Notify indexer reviewer + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: List changed files under eco-tests/ + id: changes + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + changed=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'eco-tests/' || true) + { + echo "files<> "$GITHUB_OUTPUT" + + - name: Post or update sticky review-request comment + if: steps.changes.outputs.files != '' + uses: actions/github-script@v7 + env: + CHANGED_FILES: ${{ steps.changes.outputs.files }} + REVIEWERS: ${{ env.ECO_TESTS_REVIEWERS }} + with: + script: | + const marker = ''; + + const reviewers = (process.env.REVIEWERS || '') + .split(',') + .map(s => s.trim()) + .filter(Boolean); + const ccLine = reviewers.length + ? reviewers.map(u => `@${u}`).join(' ') + : '_(no reviewers configured — set ECO_TESTS_REVIEWERS in the workflow)_'; + + const changed = (process.env.CHANGED_FILES || '').trim(); + const fileList = changed + .split('\n') + .filter(Boolean) + .map(f => `- \`${f}\``) + .join('\n'); + + const body = [ + marker, + '### eco-tests changed — indexer review required', + '', + 'This PR modifies files under `eco-tests/`. and may affect downstream indexing.', + `**cc ${ccLine}** — please review manually`, + '', + '
Changed files', + '', + fileList, + '', + '
', + ].join('\n'); + + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number, per_page: 100 } + ); + const existing = comments.find(c => c.body && c.body.includes(marker)); + + if (existing) { + if (existing.body !== body) { + await github.rest.issues.updateComment({ + owner, repo, comment_id: existing.id, body, + }); + } + } else { + await github.rest.issues.createComment({ + owner, repo, issue_number, body, + }); + } + + - name: Request reviews from configured reviewers + if: steps.changes.outputs.files != '' + uses: actions/github-script@v7 + env: + REVIEWERS: ${{ env.ECO_TESTS_REVIEWERS }} + with: + script: | + const reviewers = (process.env.REVIEWERS || '') + .split(',') + .map(s => s.trim()) + .filter(Boolean); + if (reviewers.length === 0) { + core.info('ECO_TESTS_REVIEWERS is empty — skipping review request.'); + return; + } + + const { owner, repo } = context.repo; + const pull_number = context.issue.number; + const pr = await github.rest.pulls.get({ owner, repo, pull_number }); + + // GitHub rejects requesting a review from the PR author. + const author = pr.data.user && pr.data.user.login; + const filtered = reviewers.filter(u => u !== author); + if (filtered.length === 0) { + core.info(`All configured reviewers are the PR author (${author}) — skipping.`); + return; + } + + try { + await github.rest.pulls.requestReviewers({ + owner, repo, pull_number, + reviewers: filtered, + }); + } catch (e) { + core.warning(`requestReviewers failed: ${e.message}`); + } diff --git a/.github/workflows/typescript-e2e.yml b/.github/workflows/typescript-e2e.yml index b73c50c86e..822c104c12 100644 --- a/.github/workflows/typescript-e2e.yml +++ b/.github/workflows/typescript-e2e.yml @@ -107,6 +107,8 @@ jobs: binary: fast - test: zombienet_subnets binary: fast + - test: zombienet_evm + binary: fast name: "typescript-e2e-${{ matrix.test }}" @@ -137,7 +139,13 @@ jobs: working-directory: ts-tests run: pnpm install --frozen-lockfile + - name: Install lsof (dev foundation RPC port discovery) + if: matrix.test == 'dev' + 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 lsof + - name: Run tests - run: | + run: | cd ts-tests pnpm moonwall test ${{ matrix.test }} diff --git a/Cargo.lock b/Cargo.lock index 32d4c7655d..b49277401c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,17 @@ dependencies = [ "subtle 2.6.1", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.12" @@ -497,7 +508,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ - "ahash", + "ahash 0.8.12", "ark-ff 0.5.0", "ark-poly 0.5.0", "ark-serialize 0.5.0", @@ -732,7 +743,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ - "ahash", + "ahash 0.8.12", "ark-ff 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", @@ -1669,6 +1680,29 @@ dependencies = [ "piper", ] +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases 0.2.1", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "bounded-collections" version = "0.1.9" @@ -1955,6 +1989,28 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.24.0" @@ -4085,6 +4141,16 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "endian-cast" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f7a506e5de77a3db9e56fdbed17fa6f3b8d27ede81545dde96107c3d6a1d2" +dependencies = [ + "generic-array 1.3.5", + "typenum", +] + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -5524,6 +5590,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "rustversion", + "typenum", +] + [[package]] name = "gethostname" version = "0.2.3" @@ -5737,6 +5813,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] [[package]] name = "hashbrown" @@ -5744,7 +5823,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.12", ] [[package]] @@ -5753,7 +5832,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.12", "allocator-api2", "serde", ] @@ -6934,6 +7013,33 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "lencode" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83dc280ed78264020f986b2539e6a44e0720f98f66c99a48a2f52e4a441e99d8" +dependencies = [ + "endian-cast", + "generic-array 1.3.5", + "hashbrown 0.12.3", + "lencode-macros", + "newt-hype", + "ruint", + "zstd-safe 7.2.4", +] + +[[package]] +name = "lencode-macros" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c57df14b9005d1e4e8e56436e922e2c046ad0be55d7cfb062a303714857508" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "libc" version = "0.2.176" @@ -8236,6 +8342,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "newt-hype" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8b7b69b0eafaa88ec8dc9fe7c3860af0a147517e5207cfbd0ecd21cd7cde18" + [[package]] name = "nix" version = "0.26.4" @@ -8416,12 +8528,12 @@ dependencies = [ "pallet-grandpa", "pallet-hotfix-sufficients", "pallet-insecure-randomness-collective-flip", + "pallet-limit-orders", "pallet-multisig", "pallet-nomination-pools", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-preimage", - "pallet-registry", "pallet-safe-mode", "pallet-scheduler", "pallet-session", @@ -8459,6 +8571,7 @@ dependencies = [ "sp-genesis-builder", "sp-inherents", "sp-io", + "sp-keyring", "sp-npos-elections", "sp-offchain", "sp-runtime", @@ -9984,6 +10097,28 @@ dependencies = [ "scale-info", ] +[[package]] +name = "pallet-limit-orders" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-keyring", + "sp-keystore", + "sp-runtime", + "sp-std", + "substrate-fixed", + "subtensor-macros", + "subtensor-runtime-common", + "subtensor-swap-interface", +] + [[package]] name = "pallet-lottery" version = "41.0.0" @@ -10379,25 +10514,6 @@ dependencies = [ "sp-runtime", ] -[[package]] -name = "pallet-registry" -version = "4.0.0-dev" -dependencies = [ - "enumflags2", - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "subtensor-macros", - "subtensor-runtime-common", -] - [[package]] name = "pallet-remark" version = "41.0.0" @@ -10902,6 +11018,9 @@ dependencies = [ "log", "pallet-subtensor-swap-runtime-api", "parity-scale-codec", + "rand 0.8.5", + "rayon", + "safe-bigmath", "safe-math", "scale-info", "serde", @@ -10938,6 +11057,7 @@ dependencies = [ "frame-support", "parity-scale-codec", "scale-info", + "serde", "sp-api", "sp-std", "subtensor-macros", @@ -13546,6 +13666,26 @@ dependencies = [ "cc", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quanta" version = "0.12.6" @@ -13654,6 +13794,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quoth" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d9da82a5dc3ff2fb2eee43d2b434fb197a9bf6a2a243850505b61584f888d2" +dependencies = [ + "quoth-macros", + "regex", + "rust_decimal", + "safe-string", +] + +[[package]] +name = "quoth-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58547202bec9896e773db7ef04b4d47c444f9c97bc4386f36e55718c347db440" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "r-efi" version = "5.3.0" @@ -13949,6 +14112,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "resolv-conf" version = "0.7.5" @@ -14003,6 +14175,35 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rlp" version = "0.5.2" @@ -14238,6 +14439,22 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rust_decimal" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" +dependencies = [ + "arrayvec 0.7.6", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -14493,6 +14710,18 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe-bigmath" +version = "0.4.1" +source = "git+https://github.com/sam0x17/safe-bigmath?rev=013c49984910e1c9a23289e8c85e7a856e263a02#013c49984910e1c9a23289e8c85e7a856e263a02" +dependencies = [ + "lencode", + "num-bigint", + "num-integer", + "num-traits", + "quoth", +] + [[package]] name = "safe-math" version = "0.1.0" @@ -14512,6 +14741,12 @@ dependencies = [ "rustc_version 0.2.3", ] +[[package]] +name = "safe-string" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fc51f1e562058dee569383bfdb5a58752bfeb7fa7f0823f5c07c4c45381b5a" + [[package]] name = "safe_arch" version = "0.7.4" @@ -14934,7 +15169,7 @@ name = "sc-consensus-grandpa" version = "0.36.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=7cc54bf2d50ae3921d718736dfeb0de9468539c7#7cc54bf2d50ae3921d718736dfeb0de9468539c7" dependencies = [ - "ahash", + "ahash 0.8.12", "array-bytes 6.2.3", "async-trait", "dyn-clone", @@ -15237,7 +15472,7 @@ name = "sc-network-gossip" version = "0.51.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=7cc54bf2d50ae3921d718736dfeb0de9468539c7#7cc54bf2d50ae3921d718736dfeb0de9468539c7" dependencies = [ - "ahash", + "ahash 0.8.12", "futures", "futures-timer", "log", @@ -15950,7 +16185,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" dependencies = [ - "ahash", + "ahash 0.8.12", "cfg-if", "hashbrown 0.13.2", ] @@ -16014,6 +16249,12 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "sec1" version = "0.7.3" @@ -16454,6 +16695,12 @@ dependencies = [ "wide", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -17554,7 +17801,7 @@ name = "sp-trie" version = "40.0.0" source = "git+https://github.com/opentensor/polkadot-sdk.git?rev=7cc54bf2d50ae3921d718736dfeb0de9468539c7#7cc54bf2d50ae3921d718736dfeb0de9468539c7" dependencies = [ - "ahash", + "ahash 0.8.12", "foldhash 0.1.5", "hash-db", "hashbrown 0.15.5", @@ -18194,6 +18441,7 @@ dependencies = [ "sp-runtime", "sp-std", "substrate-fixed", + "subtensor-macros", "subtensor-runtime-common", "subtensor-swap-interface", ] @@ -18237,7 +18485,7 @@ dependencies = [ name = "subtensor-macros" version = "0.1.0" dependencies = [ - "ahash", + "ahash 0.8.12", "proc-macro2", "quote", "syn 2.0.106", @@ -19886,7 +20134,7 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c128c039340ffd50d4195c3f8ce31aac357f06804cfc494c8b9508d4b30dca4" dependencies = [ - "ahash", + "ahash 0.8.12", "hashbrown 0.14.5", "string-interner", ] @@ -21196,11 +21444,18 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "git+https://github.com/gztensor/zstd-safe?rev=42cc34ef6abe5d35d982f6afefb5d7e4e69f5f18#42cc34ef6abe5d35d982f6afefb5d7e4e69f5f18" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +source = "git+https://github.com/gztensor/zstd-sys#01e299b6ce8d08af5a3429f7ceb956f8355cf1aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 14ded6a4f9..6f160c29e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,14 +57,15 @@ useless_conversion = "allow" # until polkadot is patched pallet-alpha-assets = { path = "pallets/alpha-assets", default-features = false } node-subtensor-runtime = { path = "runtime", default-features = false } pallet-admin-utils = { path = "pallets/admin-utils", default-features = false } +pallet-limit-orders = { path = "pallets/limit-orders", default-features = false } pallet-commitments = { path = "pallets/commitments", default-features = false } -pallet-registry = { path = "pallets/registry", default-features = false } pallet-crowdloan = { path = "pallets/crowdloan", default-features = false } pallet-subtensor = { path = "pallets/subtensor", default-features = false } pallet-subtensor-swap = { path = "pallets/swap", default-features = false } pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default-features = false } pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false } procedural-fork = { path = "support/procedural-fork", default-features = false } +safe-bigmath = { package = "safe-bigmath", default-features = false, git = "https://github.com/sam0x17/safe-bigmath", rev = "013c49984910e1c9a23289e8c85e7a856e263a02" } safe-math = { path = "primitives/safe-math", default-features = false } share-pool = { path = "primitives/share-pool", default-features = false } subtensor-macros = { path = "support/macros", default-features = false } @@ -72,7 +73,7 @@ subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc subtensor-custom-rpc-runtime-api = { default-features = false, path = "pallets/subtensor/runtime-api" } subtensor-precompiles = { default-features = false, path = "precompiles" } subtensor-runtime-common = { default-features = false, path = "common" } -subtensor-swap-interface = { default-features = false, path = "pallets/swap-interface" } +subtensor-swap-interface = { default-features = false, path = "primitives/swap-interface" } subtensor-transaction-fee = { default-features = false, path = "pallets/transaction-fee" } subtensor-chain-extensions = { default-features = false, path = "chain-extensions" } stp-shield = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "7cc54bf2d50ae3921d718736dfeb0de9468539c7", default-features = false } @@ -87,6 +88,7 @@ enumflags2 = "0.7.9" futures = "0.3.30" hex = { version = "0.4", default-features = false } hex-literal = "0.4.1" +impl-trait-for-tuples = "0.2.3" jsonrpsee = { version = "0.24.9", default-features = false } libsecp256k1 = { version = "0.7.2", default-features = false } lencode = "0.1.6" @@ -117,7 +119,7 @@ toml_edit = "0.22" derive-syn-parse = "0.2" Inflector = "0.11" cfg-expr = "0.15" -itertools = "0.10" +itertools = { version = "0.10", default-features = false } macro_magic = { version = "0.5", default-features = false } frame-support-procedural-tools = { version = "10.0.0", default-features = false } proc-macro-warning = { version = "1", default-features = false } @@ -319,3 +321,5 @@ pow-faucet = [] [patch.crates-io] w3f-bls = { git = "https://github.com/opentensor/bls", branch = "fix-no-std" } +zstd-sys = { git = "https://github.com/gztensor/zstd-sys" } +zstd-safe = { git = "https://github.com/gztensor/zstd-safe", rev = "42cc34ef6abe5d35d982f6afefb5d7e4e69f5f18" } diff --git a/build.rs b/build.rs index 854778873e..f382604525 100644 --- a/build.rs +++ b/build.rs @@ -29,45 +29,52 @@ fn main() { // as we process each Rust file let (tx, rx) = channel(); - // Parse each rust file with syn and run the linting suite on it in parallel - rust_files.par_iter().for_each_with(tx.clone(), |tx, file| { - let is_test = file.display().to_string().contains("test"); - let Ok(content) = fs::read_to_string(file) else { - return; - }; - let Ok(parsed_tokens) = proc_macro2::TokenStream::from_str(&content) else { - return; - }; - let Ok(parsed_file) = syn::parse2::(parsed_tokens) else { - return; - }; + let pool = rayon::ThreadPoolBuilder::new() + .stack_size(64 * 1024 * 1024) + .build() + .expect("build script lint thread pool can be created"); - let track_lint = |result: Result| { - let Err(errors) = result else { + pool.install(|| { + // Parse each rust file with syn and run the linting suite on it in parallel. + rust_files.par_iter().for_each_with(tx.clone(), |tx, file| { + let is_test = file.display().to_string().contains("test"); + let Ok(content) = fs::read_to_string(file) else { return; }; - let relative_path = file.strip_prefix(workspace_root).unwrap_or(file.as_path()); - for error in errors { - let loc = error.span().start(); - let file_path = relative_path.display(); - // note that spans can't go across thread boundaries without losing their location - // info so we we serialize here and send a String - tx.send(format!( - "cargo:warning={}:{}:{}: {}", - file_path, loc.line, loc.column, error, - )) - .unwrap(); - } - }; + let Ok(parsed_tokens) = proc_macro2::TokenStream::from_str(&content) else { + return; + }; + let Ok(parsed_file) = syn::parse2::(parsed_tokens) else { + return; + }; + + let track_lint = |result: Result| { + let Err(errors) = result else { + return; + }; + let relative_path = file.strip_prefix(workspace_root).unwrap_or(file.as_path()); + for error in errors { + let loc = error.span().start(); + let file_path = relative_path.display(); + // note that spans can't go across thread boundaries without losing their location + // info so we we serialize here and send a String + tx.send(format!( + "cargo:warning={}:{}:{}: {}", + file_path, loc.line, loc.column, error, + )) + .unwrap(); + } + }; - track_lint(ForbidAsPrimitiveConversion::lint(&parsed_file)); - track_lint(ForbidKeysRemoveCall::lint(&parsed_file)); - track_lint(RequireFreezeStruct::lint(&parsed_file)); - track_lint(RequireExplicitPalletIndex::lint(&parsed_file)); + track_lint(ForbidAsPrimitiveConversion::lint(&parsed_file)); + track_lint(ForbidKeysRemoveCall::lint(&parsed_file)); + track_lint(RequireFreezeStruct::lint(&parsed_file)); + track_lint(RequireExplicitPalletIndex::lint(&parsed_file)); - if is_test { - track_lint(ForbidSaturatingMath::lint(&parsed_file)); - } + if is_test { + track_lint(ForbidSaturatingMath::lint(&parsed_file)); + } + }); }); // Collect and print all errors after the parallel processing is done diff --git a/chain-extensions/Cargo.toml b/chain-extensions/Cargo.toml index ecc30878b5..74209cc106 100644 --- a/chain-extensions/Cargo.toml +++ b/chain-extensions/Cargo.toml @@ -21,6 +21,7 @@ sp-std.workspace = true codec = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] } subtensor-runtime-common.workspace = true +subtensor-macros.workspace = true pallet-contracts.workspace = true pallet-subtensor.workspace = true pallet-subtensor-swap.workspace = true @@ -84,5 +85,6 @@ runtime-benchmarks = [ "pallet-subtensor-proxy/runtime-benchmarks", "pallet-subtensor-utility/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", - "subtensor-runtime-common/runtime-benchmarks" + "subtensor-runtime-common/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks" ] diff --git a/chain-extensions/src/lib.rs b/chain-extensions/src/lib.rs index 1aa1a2f966..0315668441 100644 --- a/chain-extensions/src/lib.rs +++ b/chain-extensions/src/lib.rs @@ -7,7 +7,7 @@ mod tests; pub mod types; -use crate::types::{FunctionId, Output}; +use crate::types::{ColdkeyLock, FunctionId, Output, StakeAvailability, SubnetRegistrationState}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{DebugNoBound, traits::Get}; use frame_system::RawOrigin; @@ -19,7 +19,7 @@ use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_proxy::WeightInfo; use sp_runtime::{DispatchError, Weight, traits::StaticLookup}; use sp_std::marker::PhantomData; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaBalance, NetUid, ProxyType, TaoBalance}; use subtensor_swap_interface::SwapHandler; @@ -595,6 +595,61 @@ where Ok(RetVal::Converging(Output::Success as u32)) } + FunctionId::GetSubnetRegistrationStateV1 => { + let netuid: NetUid = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let state = SubnetRegistrationState { + netuid, + exists: pallet_subtensor::Pallet::::if_subnet_exist(netuid), + registered_subnet_counter: + pallet_subtensor::Pallet::::get_registered_subnet_counter(netuid), + }; + + env.write_output(&state.encode()) + .map_err(|_| DispatchError::Other("Failed to write output"))?; + + Ok(RetVal::Converging(Output::Success as u32)) + } + FunctionId::GetColdkeyLockV1 => { + let (coldkey, netuid): (T::AccountId, NetUid) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let lock = + pallet_subtensor::Pallet::::get_coldkey_lock(&coldkey, netuid).map(|lock| { + ColdkeyLock { + locked_mass: lock.locked_mass, + conviction_bits: lock.conviction.to_bits(), + last_update: lock.last_update, + } + }); + + env.write_output(&lock.encode()) + .map_err(|_| DispatchError::Other("Failed to write output"))?; + + Ok(RetVal::Converging(Output::Success as u32)) + } + FunctionId::GetStakeAvailabilityV1 => { + let (coldkey, netuid): (T::AccountId, NetUid) = env + .read_as() + .map_err(|_| DispatchError::Other("Failed to decode input parameters"))?; + + let (total, locked, available) = + pallet_subtensor::Pallet::::stake_availability(&coldkey, netuid); + let availability = StakeAvailability { + netuid, + total, + locked, + available, + }; + + env.write_output(&availability.encode()) + .map_err(|_| DispatchError::Other("Failed to write output"))?; + + Ok(RetVal::Converging(Output::Success as u32)) + } FunctionId::AddStakeV1 => { let origin = RawOrigin::Signed(env.caller()); Self::dispatch_add_stake_v1(env, origin) @@ -719,7 +774,7 @@ where netuid.into(), ); - let price = current_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let price = current_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000)); let price: u64 = price.saturating_to_num(); let encoded_result = price.encode(); diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 37c6d4fb47..db25a9df68 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -8,13 +8,13 @@ use core::num::NonZeroU64; use frame_support::dispatch::DispatchResult; use frame_support::pallet_prelude::Zero; -use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth}; +use frame_support::traits::{Contains, Everything, InsideBoth}; use frame_support::weights::Weight; use frame_support::weights::constants::RocksDbWeight; use frame_support::{PalletId, derive_impl}; use frame_support::{assert_ok, parameter_types, traits::PrivilegeCmp}; use frame_system as system; -use frame_system::{EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase}; +use frame_system::{EnsureRoot, RawOrigin, limits}; use pallet_contracts::HoldReason as ContractsHoldReason; use pallet_subtensor::*; use pallet_subtensor_proxy as pallet_proxy; @@ -26,9 +26,7 @@ use sp_runtime::{ traits::{BlakeTwo256, Convert, IdentityLookup}, }; use sp_std::{cell::RefCell, cmp::Ordering, sync::OnceLock}; -use subtensor_runtime_common::{ - AlphaBalance, AuthorshipInfo, NetUid, Saturating, TaoBalance, Token, -}; +use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, Saturating, TaoBalance}; type Block = frame_system::mocking::MockBlock; @@ -315,6 +313,10 @@ parameter_types! { 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 MinTempo: u16 = pallet_subtensor::MIN_TEMPO; + pub const MaxTempo: u16 = pallet_subtensor::MAX_TEMPO; + pub const MinActivityCutoffFactorMilli: u32 = pallet_subtensor::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const MaxActivityCutoffFactorMilli: u32 = pallet_subtensor::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const InitialValidatorPruneLen: u64 = 0; pub const InitialScalingLawPower: u16 = 50; pub const InitialMaxAllowedValidators: u16 = 100; @@ -354,6 +356,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = 10; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const MaxEpochsPerBlock: u8 = 32; } impl pallet_subtensor::Config for Test { @@ -402,6 +405,10 @@ impl pallet_subtensor::Config for Test { type InitialMinStake = InitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = MinTempo; + type MaxTempo = MaxTempo; + type MinActivityCutoffFactorMilli = MinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = MaxActivityCutoffFactorMilli; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; @@ -433,6 +440,7 @@ impl pallet_subtensor::Config for Test { type AuthorshipProvider = MockAuthorshipProvider; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = MaxEpochsPerBlock; type WeightInfo = (); } @@ -440,7 +448,6 @@ impl pallet_subtensor::Config for Test { 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(); } @@ -452,7 +459,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = TaoBalanceReserve; type AlphaReserve = AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); @@ -620,28 +626,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), ())) + UncheckedExtrinsic::new_bare(call) } } @@ -676,8 +666,7 @@ pub fn register_ok_neuron( // Ensure reserves exist for swap/burn path, but do NOT clobber reserves if the test already set them. let reserve: u64 = 1_000_000_000_000; let tao_reserve = SubnetTAO::::get(netuid); - let alpha_reserve = SubnetAlphaIn::::get(netuid) - .saturating_add(SubnetAlphaInProvided::::get(netuid)); + let alpha_reserve = SubnetAlphaIn::::get(netuid); if tao_reserve.is_zero() && alpha_reserve.is_zero() { setup_reserves(netuid, reserve.into(), reserve.into()); @@ -765,11 +754,6 @@ pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> NetUid { netuid } -#[allow(dead_code)] -pub(crate) fn remove_stake_rate_limit_for_tests(hotkey: &U256, coldkey: &U256, netuid: NetUid) { - StakingOperationRateLimiter::::remove((hotkey, coldkey, netuid)); -} - #[allow(dead_code)] pub(crate) fn setup_reserves(netuid: NetUid, tao: TaoBalance, alpha: AlphaBalance) { SubnetTAO::::set(netuid, tao); diff --git a/chain-extensions/src/tests.rs b/chain-extensions/src/tests.rs index 08a9e17f77..16619389bf 100644 --- a/chain-extensions/src/tests.rs +++ b/chain-extensions/src/tests.rs @@ -1,7 +1,7 @@ #![allow(clippy::unwrap_used)] use super::{SubtensorChainExtension, SubtensorExtensionEnv, mock}; -use crate::types::{FunctionId, Output}; +use crate::types::{ColdkeyLock, FunctionId, Output, StakeAvailability, SubnetRegistrationState}; use codec::{Decode, Encode}; use frame_support::pallet_prelude::Zero; use frame_support::{assert_ok, weights::Weight}; @@ -12,7 +12,7 @@ use pallet_subtensor::weights::WeightInfo as SubtensorWeightInfo; use sp_core::Get; use sp_core::U256; use sp_runtime::DispatchError; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -106,8 +106,6 @@ fn remove_stake_full_limit_success_with_limit_price() { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake_full_limit(); let balance_before = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); @@ -170,8 +168,6 @@ fn swap_stake_limit_with_tight_price_returns_slippage_error() { stake_alpha, ); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid_a); - let alpha_origin_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid_a, @@ -241,8 +237,6 @@ fn remove_stake_limit_success_respects_price_limit() { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let alpha_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, @@ -382,8 +376,6 @@ fn swap_stake_success_moves_between_subnets() { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid_a); - let alpha_origin_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid_a, @@ -454,8 +446,6 @@ fn transfer_stake_success_moves_between_coldkeys() { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &origin_coldkey, netuid); - let alpha_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -533,8 +523,6 @@ fn move_stake_success_moves_alpha_between_hotkeys() { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&origin_hotkey, &coldkey, netuid); - let alpha_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, @@ -608,8 +596,6 @@ fn unstake_all_alpha_success_moves_stake_to_root() { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all_alpha(); let mut env = MockEnv::new(FunctionId::UnstakeAllAlphaV1, coldkey, hotkey.encode()) @@ -1579,8 +1565,6 @@ fn unstake_all_success_unstakes_balance() { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all(); let pre_balance = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); @@ -1622,7 +1606,7 @@ fn get_alpha_price_returns_encoded_price() { as SwapHandler>::current_alpha_price( netuid.into(), ); - let expected_price_scaled = expected_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let expected_price_scaled = expected_price.saturating_mul(U64F64::from_num(1_000_000_000)); let expected_price_u64: u64 = expected_price_scaled.saturating_to_num(); let mut env = MockEnv::new(FunctionId::GetAlphaPriceV1, caller, netuid.encode()); @@ -1641,6 +1625,300 @@ fn get_alpha_price_returns_encoded_price() { }); } +#[test] +fn get_subnet_registration_state_returns_existing_subnet_counter() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(8101); + let owner_coldkey = U256::from(8102); + let caller = U256::from(8103); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + let expected_counter = + pallet_subtensor::Pallet::::get_registered_subnet_counter(netuid); + + let mut env = MockEnv::new( + FunctionId::GetSubnetRegistrationStateV1, + caller, + netuid.encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert!(env.charged_weight().is_none()); + + let state = SubnetRegistrationState::decode(&mut &env.output()[..]).unwrap(); + assert_eq!( + state, + SubnetRegistrationState { + netuid, + exists: true, + registered_subnet_counter: expected_counter, + } + ); + }); +} + +#[test] +fn get_subnet_registration_state_preserves_counter_after_dissolve() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(8201); + let owner_coldkey = U256::from(8202); + let caller = U256::from(8203); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + let registered_counter = + pallet_subtensor::Pallet::::get_registered_subnet_counter(netuid); + + assert_ok!(pallet_subtensor::Pallet::::do_dissolve_network( + netuid + )); + + let mut env = MockEnv::new( + FunctionId::GetSubnetRegistrationStateV1, + caller, + netuid.encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let state = SubnetRegistrationState::decode(&mut &env.output()[..]).unwrap(); + assert_eq!(state.netuid, netuid); + assert!(!state.exists); + assert_eq!(state.registered_subnet_counter, registered_counter); + }); +} + +#[test] +fn get_subnet_registration_state_detects_reused_netuid_generation() { + mock::new_test_ext(1).execute_with(|| { + let first_hotkey = U256::from(8301); + let first_coldkey = U256::from(8302); + let second_hotkey = U256::from(8303); + let second_coldkey = U256::from(8304); + let caller = U256::from(8305); + + let netuid = mock::add_dynamic_network(&first_hotkey, &first_coldkey); + let first_counter = + pallet_subtensor::Pallet::::get_registered_subnet_counter(netuid); + + assert_ok!(pallet_subtensor::Pallet::::do_dissolve_network( + netuid + )); + let reused_netuid = mock::add_dynamic_network(&second_hotkey, &second_coldkey); + assert_eq!(reused_netuid, netuid); + + let mut env = MockEnv::new( + FunctionId::GetSubnetRegistrationStateV1, + caller, + netuid.encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + + let state = SubnetRegistrationState::decode(&mut &env.output()[..]).unwrap(); + assert_eq!(state.netuid, netuid); + assert!(state.exists); + assert!(state.registered_subnet_counter > first_counter); + }); +} + +#[test] +fn get_coldkey_lock_returns_none_without_lock() { + mock::new_test_ext(1).execute_with(|| { + let caller = U256::from(8401); + let coldkey = U256::from(8402); + let netuid = NetUid::from(1); + + let mut env = MockEnv::new( + FunctionId::GetColdkeyLockV1, + caller, + (coldkey, netuid).encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert!(env.charged_weight().is_none()); + + let lock = Option::::decode(&mut &env.output()[..]).unwrap(); + assert_eq!(lock, None); + }); +} + +#[test] +fn get_coldkey_lock_returns_rolled_lock() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(8501); + let owner_coldkey = U256::from(8502); + let coldkey = U256::from(8503); + let hotkey = U256::from(8504); + let stake = AlphaBalance::from(10_000u64); + let lock_amount = AlphaBalance::from(5_000u64); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + pallet_subtensor::Pallet::::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, stake, + ); + + assert_ok!(pallet_subtensor::Pallet::::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + frame_system::Pallet::::set_block_number(1_001); + let expected = + pallet_subtensor::Pallet::::get_coldkey_lock(&coldkey, netuid).unwrap(); + + let mut env = MockEnv::new( + FunctionId::GetColdkeyLockV1, + coldkey, + (coldkey, netuid).encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert!(env.charged_weight().is_none()); + + let lock = Option::::decode(&mut &env.output()[..]) + .unwrap() + .unwrap(); + assert_eq!(lock.locked_mass, expected.locked_mass); + assert_eq!(lock.conviction_bits, expected.conviction.to_bits()); + assert!(lock.conviction_bits > 0); + assert_eq!( + lock.last_update, + pallet_subtensor::Pallet::::get_current_block_as_u64() + ); + }); +} + +#[test] +fn get_stake_availability_returns_zeroes_without_stake_or_lock() { + mock::new_test_ext(1).execute_with(|| { + let caller = U256::from(8601); + let coldkey = U256::from(8602); + let netuid = NetUid::from(1); + + let mut env = MockEnv::new( + FunctionId::GetStakeAvailabilityV1, + caller, + (coldkey, netuid).encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert!(env.charged_weight().is_none()); + + let availability = StakeAvailability::decode(&mut &env.output()[..]).unwrap(); + assert_eq!( + availability, + StakeAvailability { + netuid, + total: AlphaBalance::ZERO, + locked: AlphaBalance::ZERO, + available: AlphaBalance::ZERO, + } + ); + }); +} + +#[test] +fn get_stake_availability_returns_partial_lock_breakdown() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(8701); + let owner_coldkey = U256::from(8702); + let coldkey = U256::from(8703); + let hotkey = U256::from(8704); + let stake = AlphaBalance::from(10_000u64); + let lock_amount = AlphaBalance::from(4_000u64); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + pallet_subtensor::Pallet::::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, stake, + ); + assert_ok!(pallet_subtensor::Pallet::::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + let mut env = MockEnv::new( + FunctionId::GetStakeAvailabilityV1, + coldkey, + (coldkey, netuid).encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert!(env.charged_weight().is_none()); + + let availability = StakeAvailability::decode(&mut &env.output()[..]).unwrap(); + assert_eq!( + availability, + StakeAvailability { + netuid, + total: stake, + locked: lock_amount, + available: stake.saturating_sub(lock_amount), + } + ); + }); +} + +#[test] +fn get_stake_availability_uses_rolled_forward_lock() { + mock::new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(8801); + let owner_coldkey = U256::from(8802); + let coldkey = U256::from(8803); + let hotkey = U256::from(8804); + let stake = AlphaBalance::from(10_000u64); + let lock_amount = AlphaBalance::from(5_000u64); + + let netuid = mock::add_dynamic_network(&owner_hotkey, &owner_coldkey); + mock::register_ok_neuron(netuid, hotkey, coldkey, 0); + pallet_subtensor::Pallet::::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid, stake, + ); + assert_ok!(pallet_subtensor::Pallet::::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + frame_system::Pallet::::set_block_number(1_001); + let expected_locked = + pallet_subtensor::Pallet::::get_current_locked(&coldkey, netuid); + assert!(expected_locked < lock_amount); + + let mut env = MockEnv::new( + FunctionId::GetStakeAvailabilityV1, + coldkey, + (coldkey, netuid).encode(), + ); + + let ret = SubtensorChainExtension::::dispatch(&mut env).unwrap(); + assert_success(ret); + assert!(env.charged_weight().is_none()); + + let availability = StakeAvailability::decode(&mut &env.output()[..]).unwrap(); + assert_eq!(availability.netuid, netuid); + assert_eq!(availability.total, stake); + assert_eq!(availability.locked, expected_locked); + assert_eq!( + availability.available, + stake.saturating_sub(expected_locked) + ); + }); +} + /// `Caller*` dispatch uses `env.origin()` via `convert_origin`; with [`MockEnv`] both match /// `Signed(caller)`, so outcomes align with non-`Caller` arms. Weight expectations match the shared /// `dispatch_*_v1` helpers used by each pair. @@ -1752,8 +2030,6 @@ mod caller_dispatch_tests { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all(); let pre_balance = pallet_subtensor::Pallet::::get_coldkey_balance(&coldkey); @@ -1806,8 +2082,6 @@ mod caller_dispatch_tests { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = <::WeightInfo as SubtensorWeightInfo>::unstake_all_alpha(); let mut env = MockEnv::new( @@ -1870,8 +2144,6 @@ mod caller_dispatch_tests { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&origin_hotkey, &coldkey, netuid); - let alpha_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, @@ -1950,8 +2222,6 @@ mod caller_dispatch_tests { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &origin_coldkey, netuid); - let alpha_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, @@ -2039,8 +2309,6 @@ mod caller_dispatch_tests { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid_a); - let alpha_origin_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid_a, @@ -2171,8 +2439,6 @@ mod caller_dispatch_tests { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let alpha_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, @@ -2251,8 +2517,6 @@ mod caller_dispatch_tests { stake_alpha, ); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid_a); - let alpha_origin_before = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid_a, @@ -2321,8 +2585,6 @@ mod caller_dispatch_tests { stake_amount_raw.into(), )); - mock::remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - let expected_weight = <::WeightInfo as SubtensorWeightInfo>::remove_stake_full_limit(); let balance_before = diff --git a/chain-extensions/src/types.rs b/chain-extensions/src/types.rs index 5c799d71e2..001965cc90 100644 --- a/chain-extensions/src/types.rs +++ b/chain-extensions/src/types.rs @@ -1,6 +1,8 @@ use codec::{Decode, Encode}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use sp_runtime::{DispatchError, ModuleError}; +use subtensor_macros::freeze_struct; +use subtensor_runtime_common::{AlphaBalance, NetUid}; #[repr(u16)] #[derive(TryFromPrimitive, IntoPrimitive, Decode, Encode)] @@ -39,6 +41,34 @@ pub enum FunctionId { CallerSetColdkeyAutoStakeHotkeyV1 = 31, CallerAddProxyV1 = 32, CallerRemoveProxyV1 = 33, + GetSubnetRegistrationStateV1 = 34, + GetColdkeyLockV1 = 35, + GetStakeAvailabilityV1 = 36, +} + +#[freeze_struct("5dc33d60abed5c08")] +#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug, scale_info::TypeInfo)] +pub struct SubnetRegistrationState { + pub netuid: NetUid, + pub exists: bool, + pub registered_subnet_counter: u64, +} + +#[freeze_struct("bf4c1e249109618")] +#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug, scale_info::TypeInfo)] +pub struct ColdkeyLock { + pub locked_mass: AlphaBalance, + pub conviction_bits: u128, + pub last_update: u64, +} + +#[freeze_struct("fb12f00479cf6990")] +#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug, scale_info::TypeInfo)] +pub struct StakeAvailability { + pub netuid: NetUid, + pub total: AlphaBalance, + pub locked: AlphaBalance, + pub available: AlphaBalance, } #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Debug)] @@ -151,11 +181,14 @@ mod function_id_tests { assert_eq!(FunctionId::CallerSetColdkeyAutoStakeHotkeyV1 as u16, 31); assert_eq!(FunctionId::CallerAddProxyV1 as u16, 32); assert_eq!(FunctionId::CallerRemoveProxyV1 as u16, 33); + assert_eq!(FunctionId::GetSubnetRegistrationStateV1 as u16, 34); + assert_eq!(FunctionId::GetColdkeyLockV1 as u16, 35); + assert_eq!(FunctionId::GetStakeAvailabilityV1 as u16, 36); } #[test] fn caller_ids_roundtrip_try_from_primitive() { - for id in 16u16..=33u16 { + for id in 16u16..=36u16 { let v = FunctionId::try_from_primitive(id) .unwrap_or_else(|_| panic!("try_from_primitive failed for {id}")); assert_eq!(v as u16, id); diff --git a/contract-tests/.gitignore b/contract-tests/.gitignore deleted file mode 100644 index 661f94a6e0..0000000000 --- a/contract-tests/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -.papi -.env diff --git a/contract-tests/README.md b/contract-tests/README.md deleted file mode 100644 index 78294603d3..0000000000 --- a/contract-tests/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# type-test - -The contract-tests folder includes all typescript code to test the basic EVM function -like token transfer, and all precompile contracts in Subtensor. It is -implemented in typescript, use both ethers and viem lib to interact with -contracts. The polkadot API is used to call extrinsic, get storage in Subtensor -. The developers can use it to verify the code change in precompile contracts. - -The Ink contract tests also are included in the contract-tests folder. -There is an Ink project in the bittensor folder, which include all functions defined -in the runtime extension. The test file for it is wasm.contract.test.ts. - -The whole test process is also included in the CI, all test cases are executed for new -commit. CI flow can get catch any failed test cases. The polkadot API get the -latest metadata from the runtime, the case also can find out any incompatibility -between runtime and precompile contracts. - -## polkadot api - -You need `polkadot-api` globally installed: - -```bash -$ npm i -g polkadot-api -``` - -To get the metadata, you need start the localnet via run -`./scripts/localnet.sh`. then run following command to get metadata, a folder -name .papi will be created, which include the metadata and type definitions. - -```bash -npx papi add devnet -w ws://localhost:9944 -``` - -## get the new metadata - -If the runtime is upgrade, need to get the metadata again. - -```bash -sh get-metadata.sh -``` - -## run all tests - -```bash -yarn run test -``` - -## To run a particular test case, you can pass an argument with the name or part of the name. For example: - -```bash -yarn run test -- -g "Can set subnet parameter" -``` diff --git a/contract-tests/get-metadata.sh b/contract-tests/get-metadata.sh deleted file mode 100755 index 64d76bff29..0000000000 --- a/contract-tests/get-metadata.sh +++ /dev/null @@ -1,3 +0,0 @@ -rm -rf .papi -npx papi add devnet -w ws://localhost:9944 -npx papi ink add ./bittensor/target/ink/bittensor.json \ No newline at end of file diff --git a/contract-tests/package-lock.json b/contract-tests/package-lock.json deleted file mode 100644 index 52b74f2bf8..0000000000 --- a/contract-tests/package-lock.json +++ /dev/null @@ -1,6222 +0,0 @@ -{ - "name": "contract-tests", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "license": "ISC", - "dependencies": { - "@polkadot-api/descriptors": "file:.papi/descriptors", - "@polkadot-api/ink-contracts": "^0.4.1", - "@polkadot-api/sdk-ink": "^0.5.1", - "@polkadot-labs/hdkd": "^0.0.25", - "@polkadot-labs/hdkd-helpers": "^0.0.25", - "@polkadot/api": "^16.4.6", - "@polkadot/util-crypto": "^14.0.1", - "@types/mocha": "^10.0.10", - "dotenv": "17.2.1", - "ethers": "^6.13.5", - "mocha": "^11.1.0", - "polkadot-api": "^1.22.0", - "rxjs": "^7.8.2", - "scale-ts": "^1.6.1", - "viem": "2.23.4", - "ws": "^8.18.2" - }, - "devDependencies": { - "@types/chai": "^5.0.1", - "@types/node": "^22.18.0", - "assert": "^2.1.0", - "chai": "^6.0.1", - "prettier": "^3.3.3", - "ts-node": "^10.9.2", - "typescript": "^5.7.2" - } - }, - ".papi/descriptors": { - "name": "@polkadot-api/descriptors", - "version": "0.1.0-autogenerated.9947536328969970535", - "peerDependencies": { - "polkadot-api": ">=1.21.0" - } - }, - "node_modules/@adraffy/ens-normalize": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", - "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", - "license": "MIT" - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@commander-js/extra-typings": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", - "integrity": "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==", - "license": "MIT", - "peerDependencies": { - "commander": "~14.0.0" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@ethereumjs/rlp": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-10.1.0.tgz", - "integrity": "sha512-r67BJbwilammAqYI4B5okA66cNdTlFzeWxPNJOolKV52ZS/flo0tUBf4x4gxWXBgh48OgsdFV1Qp5pRoSe8IhQ==", - "license": "MPL-2.0", - "bin": { - "rlp": "bin/rlp.cjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@noble/ciphers": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", - "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz", - "integrity": "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@polkadot-api/cli": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.16.3.tgz", - "integrity": "sha512-s+p3dFw1vOeyMMqhUbt1RFyqPZdR7vg6joS0v9wBvK3qX5xU+QfOOaMxXJ8fl0mJEbwoJnJsvVl4MzjsABaKCg==", - "license": "MIT", - "dependencies": { - "@commander-js/extra-typings": "^14.0.0", - "@polkadot-api/codegen": "0.20.0", - "@polkadot-api/ink-contracts": "0.4.3", - "@polkadot-api/json-rpc-provider": "0.0.4", - "@polkadot-api/known-chains": "0.9.15", - "@polkadot-api/legacy-provider": "0.3.6", - "@polkadot-api/metadata-compatibility": "0.4.1", - "@polkadot-api/observable-client": "0.17.0", - "@polkadot-api/polkadot-sdk-compat": "2.3.3", - "@polkadot-api/sm-provider": "0.1.14", - "@polkadot-api/smoldot": "0.3.14", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/substrate-client": "0.4.7", - "@polkadot-api/utils": "0.2.0", - "@polkadot-api/wasm-executor": "^0.2.2", - "@polkadot-api/ws-provider": "0.7.4", - "@types/node": "^24.10.1", - "commander": "^14.0.2", - "execa": "^9.6.0", - "fs.promises.exists": "^1.1.4", - "ora": "^9.0.0", - "read-pkg": "^10.0.0", - "rxjs": "^7.8.2", - "tsc-prog": "^2.3.0", - "tsup": "8.5.0", - "typescript": "^5.9.3", - "write-package": "^7.2.0" - }, - "bin": { - "papi": "dist/main.js", - "polkadot-api": "dist/main.js" - } - }, - "node_modules/@polkadot-api/cli/node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@polkadot-api/cli/node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/@polkadot-api/codegen": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.20.0.tgz", - "integrity": "sha512-akwPArm35UZcebUFtTKcEkdBLCjYyKweGw3/tT04p/EtM4OsQ1FxhRdXZ51ScBC3JVGCFQTUO2hNsd1E6YXvlw==", - "license": "MIT", - "dependencies": { - "@polkadot-api/ink-contracts": "0.4.3", - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/metadata-compatibility": "0.4.1", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/common-sdk-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@polkadot-api/common-sdk-utils/-/common-sdk-utils-0.1.0.tgz", - "integrity": "sha512-cgA9fh8dfBai9b46XaaQmj9vwzyHStQjc/xrAvQksgF6SqvZ0yAfxVqLvGrsz/Xi3dsAdKLg09PybC7MUAMv9w==", - "license": "MIT", - "peerDependencies": { - "polkadot-api": "^1.8.1", - "rxjs": ">=7.8.1" - } - }, - "node_modules/@polkadot-api/descriptors": { - "resolved": ".papi/descriptors", - "link": true - }, - "node_modules/@polkadot-api/ink-contracts": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.4.3.tgz", - "integrity": "sha512-Wl+4Dxjt0GAl+rADZEgrrqEesqX/xygTpX18TmzmspcKhb9QIZf9FJI8A5Sgtq0TKAOwsd1d/hbHVX3LgbXFXg==", - "license": "MIT", - "dependencies": { - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/json-rpc-provider": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.4.tgz", - "integrity": "sha512-9cDijLIxzHOBuq6yHqpqjJ9jBmXrctjc1OFqU+tQrS96adQze3mTIH6DTgfb/0LMrqxzxffz1HQGrIlEH00WrA==", - "license": "MIT" - }, - "node_modules/@polkadot-api/json-rpc-provider-proxy": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.2.7.tgz", - "integrity": "sha512-+HM4JQXzO2GPUD2++4GOLsmFL6LO8RoLvig0HgCLuypDgfdZMlwd8KnyGHjRnVEHA5X+kvXbk84TDcAXVxTazQ==", - "license": "MIT" - }, - "node_modules/@polkadot-api/known-chains": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.9.15.tgz", - "integrity": "sha512-VQGu2Anvnx0y0Ltd6sQB3aYzQFGsaQwf2znh+w4Oflaxln5lsjO/+trpXz/rdrdgyi0iafkhpeho/p/EGBwJ+A==", - "license": "MIT" - }, - "node_modules/@polkadot-api/legacy-provider": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@polkadot-api/legacy-provider/-/legacy-provider-0.3.6.tgz", - "integrity": "sha512-JZQg0HVtBowFKxNrZdnMBKXmeSBD4yFlz6egEpvE97RXRvjaBzTaVuFFhBchngq9YmgFQewuWSoX5XSUW6hcEg==", - "license": "MIT", - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.4", - "@polkadot-api/raw-client": "0.1.1", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - }, - "peerDependencies": { - "rxjs": ">=7.8.0" - } - }, - "node_modules/@polkadot-api/logs-provider": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.0.6.tgz", - "integrity": "sha512-4WgHlvy+xee1ADaaVf6+MlK/+jGMtsMgAzvbQOJZnP4PfQuagoTqaeayk8HYKxXGphogLlPbD06tANxcb+nvAg==", - "license": "MIT", - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.4" - } - }, - "node_modules/@polkadot-api/merkleize-metadata": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.1.27.tgz", - "integrity": "sha512-OdKwOzzrLL0Ju3pQA9LjeQEquMcD+KtLybUAO3fVxwjxD5cyI0RwillGoAIBJvfMaZpNxnxJnD+WzNjRcr7FiQ==", - "license": "MIT", - "dependencies": { - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/metadata-builders": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.13.7.tgz", - "integrity": "sha512-xwggY8F/gtX7qGzz+jzP3DZvWgBWIIFQhk+r2MJ431CR+tNKeTtzGdwNocVrb9NYTK2naC9ckJS14nrNM6LWLw==", - "license": "MIT", - "dependencies": { - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/metadata-compatibility": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.4.1.tgz", - "integrity": "sha512-mZt4Af6oPXEHAprrckJiSZkWRVf0mqwF+Bm+703rPsezLptQid9AjSzh1hkgIkOrPbg6IhWbmMhbuJVjx9VeQA==", - "license": "MIT", - "dependencies": { - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/substrate-bindings": "0.16.5" - } - }, - "node_modules/@polkadot-api/observable-client": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.17.0.tgz", - "integrity": "sha512-hilb12Fg1JrlM/0nucMT85//EQltB53fmoh7YNBsZMiNpavn/3qGTO4s0JMlC/LBbddYg0nxA+DMkSVlapo7cQ==", - "license": "MIT", - "dependencies": { - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/substrate-client": "0.4.7", - "@polkadot-api/utils": "0.2.0" - }, - "peerDependencies": { - "rxjs": ">=7.8.0" - } - }, - "node_modules/@polkadot-api/pjs-signer": { - "version": "0.6.17", - "resolved": "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.6.17.tgz", - "integrity": "sha512-bxFtyiNOchV0osh6m+1CaN4tkWF7Mo4IT9XPLZBwSybpHZgwmu2wbhgqBkVL98QMyGzud7NHfrJsTCgFU6jHGg==", - "license": "MIT", - "dependencies": { - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/polkadot-signer": "0.1.6", - "@polkadot-api/signers-common": "0.1.18", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/polkadot-sdk-compat": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@polkadot-api/polkadot-sdk-compat/-/polkadot-sdk-compat-2.3.3.tgz", - "integrity": "sha512-p30po+iv4trniSJ7UZiIt/rFInvtA9Tzg65EzuRkCaQAnh54a3MPp9w/q+x+SNLEcfzVLvf8LyPnMPOIpKuj5w==", - "license": "MIT", - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.4" - } - }, - "node_modules/@polkadot-api/polkadot-signer": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@polkadot-api/polkadot-signer/-/polkadot-signer-0.1.6.tgz", - "integrity": "sha512-X7ghAa4r7doETtjAPTb50IpfGtrBmy3BJM5WCfNKa1saK04VFY9w+vDn+hwEcM4p0PcDHt66Ts74hzvHq54d9A==", - "license": "MIT" - }, - "node_modules/@polkadot-api/raw-client": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.1.1.tgz", - "integrity": "sha512-HxalpNEo8JCYXfxKM5p3TrK8sEasTGMkGjBNLzD4TLye9IK2smdb5oTvp2yfkU1iuVBdmjr69uif4NaukOYo2g==", - "license": "MIT", - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.4" - } - }, - "node_modules/@polkadot-api/sdk-ink": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@polkadot-api/sdk-ink/-/sdk-ink-0.5.1.tgz", - "integrity": "sha512-9pRnghjigivvgq7375hzkoazstvPDbc0YB01Jzw1/MYKcX+YJn1p/H8SAQTWbKlz2ohFgi1nwU52a0bsmKqb/Q==", - "license": "MIT", - "dependencies": { - "@ethereumjs/rlp": "^10.0.0", - "@polkadot-api/common-sdk-utils": "0.1.0", - "@polkadot-api/substrate-bindings": "^0.16.3", - "abitype": "^1.1.1", - "viem": "^2.37.9" - }, - "peerDependencies": { - "@polkadot-api/ink-contracts": ">=0.4.0", - "polkadot-api": ">=1.19.0", - "rxjs": ">=7.8.0" - } - }, - "node_modules/@polkadot-api/sdk-ink/node_modules/@noble/curves": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", - "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot-api/sdk-ink/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot-api/sdk-ink/node_modules/isows": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", - "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "peerDependencies": { - "ws": "*" - } - }, - "node_modules/@polkadot-api/sdk-ink/node_modules/ox": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz", - "integrity": "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "dependencies": { - "@adraffy/ens-normalize": "^1.11.0", - "@noble/ciphers": "^1.3.0", - "@noble/curves": "1.9.1", - "@noble/hashes": "^1.8.0", - "@scure/bip32": "^1.7.0", - "@scure/bip39": "^1.6.0", - "abitype": "^1.0.9", - "eventemitter3": "5.0.1" - }, - "peerDependencies": { - "typescript": ">=5.4.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@polkadot-api/sdk-ink/node_modules/viem": { - "version": "2.41.2", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.41.2.tgz", - "integrity": "sha512-LYliajglBe1FU6+EH9mSWozp+gRA/QcHfxeD9Odf83AdH5fwUS7DroH4gHvlv6Sshqi1uXrYFA2B/EOczxd15g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "dependencies": { - "@noble/curves": "1.9.1", - "@noble/hashes": "1.8.0", - "@scure/bip32": "1.7.0", - "@scure/bip39": "1.6.0", - "abitype": "1.1.0", - "isows": "1.0.7", - "ox": "0.9.6", - "ws": "8.18.3" - }, - "peerDependencies": { - "typescript": ">=5.0.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@polkadot-api/sdk-ink/node_modules/viem/node_modules/abitype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz", - "integrity": "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/wevm" - }, - "peerDependencies": { - "typescript": ">=5.0.4", - "zod": "^3.22.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/@polkadot-api/signer": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.2.11.tgz", - "integrity": "sha512-32tqbJo6JDfc/lHg+nTveeunFRULonWoTQX9xbs70arr/tAyyZfljupdECRK8CVRx1777es/CQO3QVj8EpWtYg==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "^2.0.1", - "@polkadot-api/merkleize-metadata": "1.1.27", - "@polkadot-api/polkadot-signer": "0.1.6", - "@polkadot-api/signers-common": "0.1.18", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/signers-common": { - "version": "0.1.18", - "resolved": "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.1.18.tgz", - "integrity": "sha512-UQXuRZoQ+jMolEpIPF0mVXcoqQ/382fHrSOgfK5sIvjeH0HPf4P+s3IwcnwyAdpHY2gdHXYlHd/SAw7Q1gJ4EA==", - "license": "MIT", - "dependencies": { - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/polkadot-signer": "0.1.6", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/sm-provider": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.1.14.tgz", - "integrity": "sha512-QQvoeBSIwnEm8IUhGA6sBU6LNh2v7SOuVOnF77ZD7P5ELTrdmQH2Tcn0W15qGTmTG45b3Z52XsKpuQbIJ7c7XA==", - "license": "MIT", - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.4", - "@polkadot-api/json-rpc-provider-proxy": "0.2.7" - }, - "peerDependencies": { - "@polkadot-api/smoldot": ">=0.3" - } - }, - "node_modules/@polkadot-api/smoldot": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.3.14.tgz", - "integrity": "sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ==", - "license": "MIT", - "dependencies": { - "@types/node": "^24.5.2", - "smoldot": "2.0.39" - } - }, - "node_modules/@polkadot-api/smoldot/node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@polkadot-api/smoldot/node_modules/smoldot": { - "version": "2.0.39", - "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.39.tgz", - "integrity": "sha512-yFMSzI6nkqWFTNao99lBA/TguUFU+bR3A5UGTDd/QqqB12jqzvZnmW/No6l2rKmagt8Qx/KybMNowV/E28znhA==", - "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", - "dependencies": { - "ws": "^8.8.1" - } - }, - "node_modules/@polkadot-api/smoldot/node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/@polkadot-api/substrate-bindings": { - "version": "0.16.5", - "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.16.5.tgz", - "integrity": "sha512-QFgNlBmtLtiUGTCTurxcE6UZrbI2DaQ5/gyIiC2FYfEhStL8tl20b09FRYHcSjY+lxN42Rcf9HVX+MCFWLYlpQ==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "^2.0.1", - "@polkadot-api/utils": "0.2.0", - "@scure/base": "^2.0.0", - "scale-ts": "^1.6.1" - } - }, - "node_modules/@polkadot-api/substrate-bindings/node_modules/@scure/base": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", - "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot-api/substrate-client": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.4.7.tgz", - "integrity": "sha512-Mmx9VKincVqfVQmq89gzDk4DN3uKwf8CxoqYvq+EiPUZ1QmMUc7X4QMwG1MXIlYdnm5LSXzn+2Jn8ik8xMgL+w==", - "license": "MIT", - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.4", - "@polkadot-api/raw-client": "0.1.1", - "@polkadot-api/utils": "0.2.0" - } - }, - "node_modules/@polkadot-api/utils": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.2.0.tgz", - "integrity": "sha512-nY3i5fQJoAxU4n3bD7Fs208/KR2J95SGfVc58kDjbRYN5a84kWaGEqzjBNtP9oqht49POM8Bm9mbIrkvC1Bzuw==", - "license": "MIT" - }, - "node_modules/@polkadot-api/wasm-executor": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@polkadot-api/wasm-executor/-/wasm-executor-0.2.3.tgz", - "integrity": "sha512-B2h1o+Qlo9idpASaHvMSoViB2I5ko5OAfwfhYF8LQDkTADK0B+SeStzNj1Qn+FG34wqTuv7HzBCdjaUgzYINJQ==", - "license": "MIT" - }, - "node_modules/@polkadot-api/ws-provider": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.7.4.tgz", - "integrity": "sha512-mkk2p8wPht+ljU1xULCPMsLpNF7NHuGaufuDCIZZgopALaZpfVFJxc3qa9s6Xv8X3hM+TRoC5WknuD1ykRY99A==", - "license": "MIT", - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.4", - "@polkadot-api/json-rpc-provider-proxy": "0.2.7", - "@types/ws": "^8.18.1", - "ws": "^8.18.3" - } - }, - "node_modules/@polkadot-labs/hdkd": { - "version": "0.0.25", - "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd/-/hdkd-0.0.25.tgz", - "integrity": "sha512-+yZJC1TE4ZKdfoILw8nGxu3H/klrYXm9GdVB0kcyQDecq320ThUmM1M4l8d1F/3QD0Nez9NwHi9t5B++OgJU5A==", - "license": "MIT", - "dependencies": { - "@polkadot-labs/hdkd-helpers": "~0.0.26" - } - }, - "node_modules/@polkadot-labs/hdkd-helpers": { - "version": "0.0.25", - "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.25.tgz", - "integrity": "sha512-GwHayBuyHKfzvGD0vG47NbjFeiK6rRQHQAn1syut9nt0mhXMg4yb3tJ//IyM317qWuDU3HbD2OIp5jKDEQz2/A==", - "license": "MIT", - "dependencies": { - "@noble/curves": "^2.0.0", - "@noble/hashes": "^2.0.0", - "@scure/base": "^2.0.0", - "@scure/sr25519": "^0.3.0", - "scale-ts": "^1.6.1" - } - }, - "node_modules/@polkadot-labs/hdkd-helpers/node_modules/@noble/curves": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", - "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "2.0.1" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot-labs/hdkd-helpers/node_modules/@scure/base": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", - "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot-labs/hdkd/node_modules/@noble/curves": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", - "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "2.0.1" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot-labs/hdkd/node_modules/@polkadot-labs/hdkd-helpers": { - "version": "0.0.26", - "resolved": "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.26.tgz", - "integrity": "sha512-mp3GCSiOQeh4aPt+DYBQq6UnX/tKgYUH5F75knjW3ATSA90ifEEWWjRan0Bddt4QKYKamaDGadK9GbVREgzQFw==", - "license": "MIT", - "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" - } - }, - "node_modules/@polkadot-labs/hdkd/node_modules/@scure/base": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz", - "integrity": "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/api": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-16.5.3.tgz", - "integrity": "sha512-Ptwo0f5Qonmus7KIklsbFcGTdHtNjbTAwl5GGI8Mp0dmBc7Y/ISJpIJX49UrG6FhW6COMa0ItsU87XIWMRwI/Q==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/api-augment": "16.5.3", - "@polkadot/api-base": "16.5.3", - "@polkadot/api-derive": "16.5.3", - "@polkadot/keyring": "^13.5.9", - "@polkadot/rpc-augment": "16.5.3", - "@polkadot/rpc-core": "16.5.3", - "@polkadot/rpc-provider": "16.5.3", - "@polkadot/types": "16.5.3", - "@polkadot/types-augment": "16.5.3", - "@polkadot/types-codec": "16.5.3", - "@polkadot/types-create": "16.5.3", - "@polkadot/types-known": "16.5.3", - "@polkadot/util": "^13.5.9", - "@polkadot/util-crypto": "^13.5.9", - "eventemitter3": "^5.0.1", - "rxjs": "^7.8.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-augment": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-16.5.3.tgz", - "integrity": "sha512-9+8YKSS66x9qpWS+ZQ/FSm9P4mgE+icD53oAmeIykriPW2gcSTAiNufLwAjmAJAkOLcqbTD7LPjFW6xFlmtYsA==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/api-base": "16.5.3", - "@polkadot/rpc-augment": "16.5.3", - "@polkadot/types": "16.5.3", - "@polkadot/types-augment": "16.5.3", - "@polkadot/types-codec": "16.5.3", - "@polkadot/util": "^13.5.9", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-base": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-16.5.3.tgz", - "integrity": "sha512-M1+pY6OFQ1uOB73VQMt2JAGq/UVISVQJISqyfjiUllUc0qIzaDMkcZxRqE34Lwaib3fD3RuIpG6dXqCL9rdzJQ==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/rpc-core": "16.5.3", - "@polkadot/types": "16.5.3", - "@polkadot/util": "^13.5.9", - "rxjs": "^7.8.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-derive": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-16.5.3.tgz", - "integrity": "sha512-nMsnSC/N1SK1kNhgh2FhrrR1S8bTVH+3WsuBHFRzl+txKHq232IeIn9LpebSvgZdd77PaKaYBxbhYcNaA8Ypew==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/api": "16.5.3", - "@polkadot/api-augment": "16.5.3", - "@polkadot/api-base": "16.5.3", - "@polkadot/rpc-core": "16.5.3", - "@polkadot/types": "16.5.3", - "@polkadot/types-codec": "16.5.3", - "@polkadot/util": "^13.5.9", - "@polkadot/util-crypto": "^13.5.9", - "rxjs": "^7.8.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-derive/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/api-derive/node_modules/@polkadot/util-crypto": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", - "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "13.5.9", - "@polkadot/util": "13.5.9", - "@polkadot/wasm-crypto": "^7.5.3", - "@polkadot/wasm-util": "^7.5.3", - "@polkadot/x-bigint": "13.5.9", - "@polkadot/x-randomvalues": "13.5.9", - "@scure/base": "^1.1.7", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "13.5.9" - } - }, - "node_modules/@polkadot/api/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/api/node_modules/@polkadot/util-crypto": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", - "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "13.5.9", - "@polkadot/util": "13.5.9", - "@polkadot/wasm-crypto": "^7.5.3", - "@polkadot/wasm-util": "^7.5.3", - "@polkadot/x-bigint": "13.5.9", - "@polkadot/x-randomvalues": "13.5.9", - "@scure/base": "^1.1.7", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "13.5.9" - } - }, - "node_modules/@polkadot/keyring": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.9.tgz", - "integrity": "sha512-bMCpHDN7U8ytxawjBZ89/he5s3AmEZuOdkM/ABcorh/flXNPfyghjFK27Gy4OKoFxX52yJ2sTHR4NxM87GuFXQ==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/util": "13.5.9", - "@polkadot/util-crypto": "13.5.9", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "13.5.9", - "@polkadot/util-crypto": "13.5.9" - } - }, - "node_modules/@polkadot/keyring/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/keyring/node_modules/@polkadot/util-crypto": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", - "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "13.5.9", - "@polkadot/util": "13.5.9", - "@polkadot/wasm-crypto": "^7.5.3", - "@polkadot/wasm-util": "^7.5.3", - "@polkadot/x-bigint": "13.5.9", - "@polkadot/x-randomvalues": "13.5.9", - "@scure/base": "^1.1.7", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "13.5.9" - } - }, - "node_modules/@polkadot/networks": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz", - "integrity": "sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/util": "13.5.9", - "@substrate/ss58-registry": "^1.51.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-augment": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-16.5.3.tgz", - "integrity": "sha512-q3Y+b0FSwbYe8Qopd4In+9KCL3eH5QmGVvimX7Z8+cvQ9+h+JUA6TP1bfpWBmYJRKlolaljsBQPBWoubchmxSw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/rpc-core": "16.5.3", - "@polkadot/types": "16.5.3", - "@polkadot/types-codec": "16.5.3", - "@polkadot/util": "^13.5.9", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-core": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-16.5.3.tgz", - "integrity": "sha512-UYEIRhO/1uTz/rpWLwUN9Re3c4fuTs0I9RR8dHKpKsH3jZTs1M3CtqME3NNzpGqApY1xb9tZemU/0GfHjCpeBQ==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/rpc-augment": "16.5.3", - "@polkadot/rpc-provider": "16.5.3", - "@polkadot/types": "16.5.3", - "@polkadot/util": "^13.5.9", - "rxjs": "^7.8.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-provider": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-16.5.3.tgz", - "integrity": "sha512-O7hD82HwjT4XJ4i/G58B52RSDM7arHXSpzahZKz4/wtb4x6d6b4JVdfZoskInadARFi5RwIWCrftwPtpRH81Fw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/keyring": "^13.5.9", - "@polkadot/types": "16.5.3", - "@polkadot/types-support": "16.5.3", - "@polkadot/util": "^13.5.9", - "@polkadot/util-crypto": "^13.5.9", - "@polkadot/x-fetch": "^13.5.9", - "@polkadot/x-global": "^13.5.9", - "@polkadot/x-ws": "^13.5.9", - "eventemitter3": "^5.0.1", - "mock-socket": "^9.3.1", - "nock": "^13.5.5", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@substrate/connect": "0.8.11" - } - }, - "node_modules/@polkadot/rpc-provider/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/rpc-provider/node_modules/@polkadot/util-crypto": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", - "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "13.5.9", - "@polkadot/util": "13.5.9", - "@polkadot/wasm-crypto": "^7.5.3", - "@polkadot/wasm-util": "^7.5.3", - "@polkadot/x-bigint": "13.5.9", - "@polkadot/x-randomvalues": "13.5.9", - "@scure/base": "^1.1.7", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "13.5.9" - } - }, - "node_modules/@polkadot/types": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-16.5.3.tgz", - "integrity": "sha512-xy9uv/X4iT7uJ7TNCoqbcMkR8ePHwNW6DgpOU+1y1zc/KSu9ZC5i+haFOL68BpmR/QXk99YfuHoKwXvteDmykw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/keyring": "^13.5.9", - "@polkadot/types-augment": "16.5.3", - "@polkadot/types-codec": "16.5.3", - "@polkadot/types-create": "16.5.3", - "@polkadot/util": "^13.5.9", - "@polkadot/util-crypto": "^13.5.9", - "rxjs": "^7.8.1", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-augment": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-16.5.3.tgz", - "integrity": "sha512-SfS4arJUxW6BeCEhLMVPrZwWOLte69k5+/lvEKOKHQA8Mz0MEkD4uqGZGibDjgBgdnu8N+3b+rs+Fn3YfZu4yA==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/types": "16.5.3", - "@polkadot/types-codec": "16.5.3", - "@polkadot/util": "^13.5.9", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-codec": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-16.5.3.tgz", - "integrity": "sha512-b+oKMrIZrsFH4pPwvGQ6lMS8oFrYAGMy9QSbytA+KDmXAgTCtShz5XGvdQabvsGCjJ45EKgkKpKynVcYh3gk8g==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/util": "^13.5.9", - "@polkadot/x-bigint": "^13.5.9", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-create": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-16.5.3.tgz", - "integrity": "sha512-XGnBLNamPh7eQGcHNGFghA/prH7z2BsQ+9EVSbHCvw9ENr/Ow24mmmkZyMG5WM/5I6/4HRdfwFJucYt1GL/p9g==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/types-codec": "16.5.3", - "@polkadot/util": "^13.5.9", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-known": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-16.5.3.tgz", - "integrity": "sha512-ZLAZI24bQD0C9CJWYHxrLG8QSmzRzfWa51rlSNwZ9Atsc3R+GeX1YZGc9IljpQxYJCHrCqd6X8TXpAmEJdnbKw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/networks": "^13.5.9", - "@polkadot/types": "16.5.3", - "@polkadot/types-codec": "16.5.3", - "@polkadot/types-create": "16.5.3", - "@polkadot/util": "^13.5.9", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-support": { - "version": "16.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-16.5.3.tgz", - "integrity": "sha512-ggyIRV+4Kn+aG1PiVT0PE00pAqMveyS3CuFsW9gJnKxeev4VrGfr08R4vw/61D7uIfpilkQdkXNgXAbeN09Mxg==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/util": "^13.5.9", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/types/node_modules/@polkadot/util-crypto": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", - "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "13.5.9", - "@polkadot/util": "13.5.9", - "@polkadot/wasm-crypto": "^7.5.3", - "@polkadot/wasm-util": "^7.5.3", - "@polkadot/x-bigint": "13.5.9", - "@polkadot/x-randomvalues": "13.5.9", - "@scure/base": "^1.1.7", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "13.5.9" - } - }, - "node_modules/@polkadot/util": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz", - "integrity": "sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw==", - "license": "Apache-2.0", - "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.1.6", - "bn.js": "^5.2.1", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-14.0.1.tgz", - "integrity": "sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "14.0.1", - "@polkadot/util": "14.0.1", - "@polkadot/wasm-crypto": "^7.5.3", - "@polkadot/wasm-util": "^7.5.3", - "@polkadot/x-bigint": "14.0.1", - "@polkadot/x-randomvalues": "14.0.1", - "@scure/base": "^1.1.7", - "@scure/sr25519": "^0.2.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "14.0.1" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@polkadot/networks": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-14.0.1.tgz", - "integrity": "sha512-wGlBtXDkusRAj4P7uxfPz80gLO1+j99MLBaQi3bEym2xrFrFhgIWVHOZlBit/1PfaBjhX2Z8XjRxaM2w1p7w2w==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/util": "14.0.1", - "@substrate/ss58-registry": "^1.51.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@polkadot/util": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-14.0.1.tgz", - "integrity": "sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg==", - "license": "Apache-2.0", - "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.1.6", - "bn.js": "^5.2.1", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-bigint": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-14.0.1.tgz", - "integrity": "sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "14.0.1", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-global": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-14.0.1.tgz", - "integrity": "sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-randomvalues": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-14.0.1.tgz", - "integrity": "sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "14.0.1", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "14.0.1", - "@polkadot/wasm-util": "*" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textdecoder": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-14.0.1.tgz", - "integrity": "sha512-CcWiPCuPVJsNk4Vq43lgFHqLRBQHb4r9RD7ZIYgmwoebES8TNm4g2ew9ToCzakFKSpzKu6I07Ne9wv/dt5zLuw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "14.0.1", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@polkadot/x-textencoder": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-14.0.1.tgz", - "integrity": "sha512-VY51SpQmF1ccmAGLfxhYnAe95Spfz049WZ/+kK4NfsGF9WejxVdU53Im5C80l45r8qHuYQsCWU3+t0FNunh2Kg==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "14.0.1", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto/node_modules/@scure/sr25519": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.2.0.tgz", - "integrity": "sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg==", - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.9.2", - "@noble/hashes": "~1.8.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/wasm-bridge": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.3.tgz", - "integrity": "sha512-mUvwwNH+uP1wqpMuHjmEwHxRIaVc5csmb+ukycWQGhzwhpXe/0fvBEU2TQ8kwgqO2MU0FS3hN/QcIWKfPRJgxQ==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/wasm-util": "7.5.3", - "tslib": "^2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.5.3.tgz", - "integrity": "sha512-dmKUM9vw1wrnCHGuIeOtQo1pwuSF7fkyF4TYimTn3tAa0+3cDctYBErtGxgUeqP0Bo4Q0Of4/vnHlSk5Rbt9Uw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/wasm-bridge": "7.5.3", - "@polkadot/wasm-crypto-asmjs": "7.5.3", - "@polkadot/wasm-crypto-init": "7.5.3", - "@polkadot/wasm-crypto-wasm": "7.5.3", - "@polkadot/wasm-util": "7.5.3", - "tslib": "^2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-asmjs": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.3.tgz", - "integrity": "sha512-fSbbjI+4p0U3PQ8nOz/3p7euHriSdh+2CSywNuXHa8fMaYlMqCKt9K7+HI8CQ4RZNvZWDq+Py1nEDEkM4rZrvw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-init": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.3.tgz", - "integrity": "sha512-KvUpxqvW70XhuDiw/N6rM8fQ7zRjIFblw+vdJ0/wwyagwg9jrYNA9TMei5ksQd9sxGCGXN/xJmwHJXuUjkocmg==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/wasm-bridge": "7.5.3", - "@polkadot/wasm-crypto-asmjs": "7.5.3", - "@polkadot/wasm-crypto-wasm": "7.5.3", - "@polkadot/wasm-util": "7.5.3", - "tslib": "^2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-wasm": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.3.tgz", - "integrity": "sha512-fc88+HyVxebB/40GVgGUOLBqyO3C571DXWPTFmtt5EX9H8gw7Jg0Bkitz7hgSVP2x4FjXpqS9UNTJ8trVH0x1A==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/wasm-util": "7.5.3", - "tslib": "^2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-util": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.3.tgz", - "integrity": "sha512-hBr9bbjS+Yr7DrDUSkIIuvlTSoAlI8WXuo9YEB4C76j130u/cl+zyq6Iy/WnaTE6QH+8i9DhM8QTety6TqYnUQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/x-bigint": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz", - "integrity": "sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "13.5.9", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-fetch": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.5.9.tgz", - "integrity": "sha512-urwXQZtT4yYROiRdJS6zHu18J/jCoAGpbgPIAjwdqjT11t9XIq4SjuPMxD19xBRhbYe9ocWV8i1KHuoMbZgKbA==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "13.5.9", - "node-fetch": "^3.3.2", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-global": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.9.tgz", - "integrity": "sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-randomvalues": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz", - "integrity": "sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "13.5.9", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "13.5.9", - "@polkadot/wasm-util": "*" - } - }, - "node_modules/@polkadot/x-textdecoder": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.9.tgz", - "integrity": "sha512-W2HhVNUbC/tuFdzNMbnXAWsIHSg9SC9QWDNmFD3nXdSzlXNgL8NmuiwN2fkYvCQBtp/XSoy0gDLx0C+Fo19cfw==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "13.5.9", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-textencoder": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.9.tgz", - "integrity": "sha512-SG0MHnLUgn1ZxFdm0KzMdTHJ47SfqFhdIPMcGA0Mg/jt2rwrfrP3jtEIJMsHfQpHvfsNPfv55XOMmoPWuQnP/Q==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "13.5.9", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-ws": { - "version": "13.5.9", - "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.5.9.tgz", - "integrity": "sha512-NKVgvACTIvKT8CjaQu9d0dERkZsWIZngX/4NVSjc01WHmln4F4y/zyBdYn/Z2V0Zw28cISx+lB4qxRmqTe7gbg==", - "license": "Apache-2.0", - "dependencies": { - "@polkadot/x-global": "13.5.9", - "tslib": "^2.8.0", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", - "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", - "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", - "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", - "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", - "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", - "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", - "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", - "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", - "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", - "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", - "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", - "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", - "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", - "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", - "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", - "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", - "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", - "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", - "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", - "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", - "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", - "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rx-state/core": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz", - "integrity": "sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ==", - "license": "MIT", - "peerDependencies": { - "rxjs": ">=7" - } - }, - "node_modules/@scure/base": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", - "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", - "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.9.0", - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", - "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/sr25519": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.3.0.tgz", - "integrity": "sha512-SKsinX2sImunfcsH3seGrwH/OayBwwaJqVN8J1cJBNRCfbBq5q0jyTKGa9PcW1HWv9vXT6Yuq41JsxFLvF59ew==", - "license": "MIT", - "dependencies": { - "@noble/curves": "~2.0.0", - "@noble/hashes": "~2.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/sr25519/node_modules/@noble/curves": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz", - "integrity": "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "2.0.1" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "license": "MIT" - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@substrate/connect": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz", - "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", - "license": "GPL-3.0-only", - "optional": true, - "dependencies": { - "@substrate/connect-extension-protocol": "^2.0.0", - "@substrate/connect-known-chains": "^1.1.5", - "@substrate/light-client-extension-helpers": "^1.0.0", - "smoldot": "2.0.26" - } - }, - "node_modules/@substrate/connect-extension-protocol": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz", - "integrity": "sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==", - "license": "GPL-3.0-only", - "optional": true - }, - "node_modules/@substrate/connect-known-chains": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz", - "integrity": "sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==", - "license": "GPL-3.0-only", - "optional": true - }, - "node_modules/@substrate/light-client-extension-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz", - "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@polkadot-api/json-rpc-provider": "^0.0.1", - "@polkadot-api/json-rpc-provider-proxy": "^0.1.0", - "@polkadot-api/observable-client": "^0.3.0", - "@polkadot-api/substrate-client": "^0.1.2", - "@substrate/connect-extension-protocol": "^2.0.0", - "@substrate/connect-known-chains": "^1.1.5", - "rxjs": "^7.8.1" - }, - "peerDependencies": { - "smoldot": "2.x" - } - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "optional": true, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/json-rpc-provider": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz", - "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==", - "license": "MIT", - "optional": true - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/json-rpc-provider-proxy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz", - "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==", - "license": "MIT", - "optional": true - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/metadata-builders": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz", - "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@polkadot-api/substrate-bindings": "0.6.0", - "@polkadot-api/utils": "0.1.0" - } - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/observable-client": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz", - "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", - "license": "MIT", - "optional": true, - "dependencies": { - "@polkadot-api/metadata-builders": "0.3.2", - "@polkadot-api/substrate-bindings": "0.6.0", - "@polkadot-api/utils": "0.1.0" - }, - "peerDependencies": { - "@polkadot-api/substrate-client": "0.1.4", - "rxjs": ">=7.8.0" - } - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/substrate-bindings": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz", - "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@noble/hashes": "^1.3.1", - "@polkadot-api/utils": "0.1.0", - "@scure/base": "^1.1.1", - "scale-ts": "^1.6.0" - } - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/substrate-client": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz", - "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "@polkadot-api/json-rpc-provider": "0.0.1", - "@polkadot-api/utils": "0.1.0" - } - }, - "node_modules/@substrate/light-client-extension-helpers/node_modules/@polkadot-api/utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", - "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==", - "license": "MIT", - "optional": true - }, - "node_modules/@substrate/ss58-registry": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz", - "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", - "license": "Apache-2.0" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/bn.js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", - "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/bn.js/node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/bn.js/node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/mocha": { - "version": "10.0.10", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", - "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.19.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", - "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ws/node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/ws/node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" - }, - "node_modules/abitype": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.0.tgz", - "integrity": "sha512-fD3ROjckUrWsybaSor2AdWxzA0e/DSyV2dA4aYd7bd8orHsoJjl09fOgKfUkTDfk0BsDGBf4NBgu/c7JoS2Npw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/wevm" - }, - "peerDependencies": { - "typescript": ">=5.0.4", - "zod": "^3.22.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aes-js": { - "version": "4.0.0-beta.5", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", - "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", - "license": "MIT" - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/bn.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", - "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "license": "ISC" - }, - "node_modules/bundle-require": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", - "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", - "license": "MIT", - "dependencies": { - "load-tsconfig": "^0.2.3" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "esbuild": ">=0.18" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chai": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", - "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz", - "integrity": "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==", - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/detect-indent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", - "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "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" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ethers": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", - "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/ethers-io/" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "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" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/ethers/node_modules/@adraffy/ens-normalize": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", - "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", - "license": "MIT" - }, - "node_modules/ethers/node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.3.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ethers/node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ethers/node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/ethers/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "license": "0BSD" - }, - "node_modules/ethers/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "license": "MIT" - }, - "node_modules/ethers/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/execa": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", - "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^4.0.0", - "cross-spawn": "^7.0.6", - "figures": "^6.1.0", - "get-stream": "^9.0.0", - "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.2.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^4.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.5.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/figures": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fix-dts-default-cjs-exports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", - "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", - "license": "MIT", - "dependencies": { - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "rollup": "^4.34.8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/fs.promises.exists": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fs.promises.exists/-/fs.promises.exists-1.1.4.tgz", - "integrity": "sha512-lJzUGWbZn8vhGWBedA+RYjB/BeJ+3458ljUfmplqhIeb6ewzTFWNPCR1HCiYCkXV9zxcHz9zXkJzMsEgDLzh3Q==", - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/fs.promises.exists?sponsor=1" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "license": "MIT", - "dependencies": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hosted-git-info": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", - "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/human-signals": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", - "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/index-to-position": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", - "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/isows": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", - "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "peerDependencies": { - "ws": "*" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "license": "ISC" - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", - "license": "MIT", - "dependencies": { - "acorn": "^8.15.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.1" - } - }, - "node_modules/mocha": { - "version": "11.7.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", - "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/mocha/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/mock-socket": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", - "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nock": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", - "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/normalize-package-data": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz", - "integrity": "sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==", - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^9.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/npm-run-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz", - "integrity": "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==", - "license": "MIT", - "dependencies": { - "chalk": "^5.6.2", - "cli-cursor": "^5.0.0", - "cli-spinners": "^3.2.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.1.0", - "log-symbols": "^7.0.1", - "stdin-discarder": "^0.2.2", - "string-width": "^8.1.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/ox": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", - "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "dependencies": { - "@adraffy/ens-normalize": "^1.10.1", - "@noble/curves": "^1.6.0", - "@noble/hashes": "^1.5.0", - "@scure/bip32": "^1.5.0", - "@scure/bip39": "^1.4.0", - "abitype": "^1.0.6", - "eventemitter3": "5.0.1" - }, - "peerDependencies": { - "typescript": ">=5.4.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/ox/node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/parse-json": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", - "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "index-to-position": "^1.1.0", - "type-fest": "^4.39.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-ms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/polkadot-api": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/polkadot-api/-/polkadot-api-1.22.0.tgz", - "integrity": "sha512-uREBLroPbnJxBBQ+qSkKLF493qukX4PAg32iThlELrZdxfNNgro6nvWRdVmBv73tFHvf+nyWWHKTx1c57nbixg==", - "license": "MIT", - "dependencies": { - "@polkadot-api/cli": "0.16.3", - "@polkadot-api/ink-contracts": "0.4.3", - "@polkadot-api/json-rpc-provider": "0.0.4", - "@polkadot-api/known-chains": "0.9.15", - "@polkadot-api/logs-provider": "0.0.6", - "@polkadot-api/metadata-builders": "0.13.7", - "@polkadot-api/metadata-compatibility": "0.4.1", - "@polkadot-api/observable-client": "0.17.0", - "@polkadot-api/pjs-signer": "0.6.17", - "@polkadot-api/polkadot-sdk-compat": "2.3.3", - "@polkadot-api/polkadot-signer": "0.1.6", - "@polkadot-api/signer": "0.2.11", - "@polkadot-api/sm-provider": "0.1.14", - "@polkadot-api/smoldot": "0.3.14", - "@polkadot-api/substrate-bindings": "0.16.5", - "@polkadot-api/substrate-client": "0.4.7", - "@polkadot-api/utils": "0.2.0", - "@polkadot-api/ws-provider": "0.7.4", - "@rx-state/core": "^0.1.4" - }, - "bin": { - "papi": "bin/cli.mjs", - "polkadot-api": "bin/cli.mjs" - }, - "peerDependencies": { - "rxjs": ">=7.8.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "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 - } - } - }, - "node_modules/prettier": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-ms": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", - "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", - "license": "MIT", - "dependencies": { - "parse-ms": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/read-pkg": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-10.0.0.tgz", - "integrity": "sha512-A70UlgfNdKI5NSvTTfHzLQj7NJRpJ4mT5tGafkllJ4wh71oYuGm/pzphHcmW4s35iox56KSK721AihodoXSc/A==", - "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.4", - "normalize-package-data": "^8.0.0", - "parse-json": "^8.3.0", - "type-fest": "^5.2.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.3.0.tgz", - "integrity": "sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g==", - "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/rollup": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", - "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.3", - "@rollup/rollup-android-arm64": "4.53.3", - "@rollup/rollup-darwin-arm64": "4.53.3", - "@rollup/rollup-darwin-x64": "4.53.3", - "@rollup/rollup-freebsd-arm64": "4.53.3", - "@rollup/rollup-freebsd-x64": "4.53.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", - "@rollup/rollup-linux-arm-musleabihf": "4.53.3", - "@rollup/rollup-linux-arm64-gnu": "4.53.3", - "@rollup/rollup-linux-arm64-musl": "4.53.3", - "@rollup/rollup-linux-loong64-gnu": "4.53.3", - "@rollup/rollup-linux-ppc64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-musl": "4.53.3", - "@rollup/rollup-linux-s390x-gnu": "4.53.3", - "@rollup/rollup-linux-x64-gnu": "4.53.3", - "@rollup/rollup-linux-x64-musl": "4.53.3", - "@rollup/rollup-openharmony-arm64": "4.53.3", - "@rollup/rollup-win32-arm64-msvc": "4.53.3", - "@rollup/rollup-win32-ia32-msvc": "4.53.3", - "@rollup/rollup-win32-x64-gnu": "4.53.3", - "@rollup/rollup-win32-x64-msvc": "4.53.3", - "fsevents": "~2.3.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scale-ts": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz", - "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/smoldot": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz", - "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", - "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", - "optional": true, - "dependencies": { - "ws": "^8.8.1" - } - }, - "node_modules/sort-keys": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz", - "integrity": "sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==", - "license": "MIT", - "dependencies": { - "is-plain-obj": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "license": "BSD-3-Clause", - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.22", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", - "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", - "license": "CC0-1.0" - }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", - "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sucrase": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", - "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/tagged-tag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", - "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "license": "MIT", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "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 - } - } - }, - "node_modules/tsc-prog": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tsc-prog/-/tsc-prog-2.3.0.tgz", - "integrity": "sha512-ycET2d75EgcX7y8EmG4KiZkLAwUzbY4xRhA6NU0uVbHkY4ZjrAAuzTMxXI85kOwATqPnBI5C/7y7rlpY0xdqHA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "typescript": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsup": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", - "integrity": "sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==", - "license": "MIT", - "dependencies": { - "bundle-require": "^5.1.0", - "cac": "^6.7.14", - "chokidar": "^4.0.3", - "consola": "^3.4.0", - "debug": "^4.4.0", - "esbuild": "^0.25.0", - "fix-dts-default-cjs-exports": "^1.0.0", - "joycon": "^3.1.1", - "picocolors": "^1.1.1", - "postcss-load-config": "^6.0.1", - "resolve-from": "^5.0.0", - "rollup": "^4.34.8", - "source-map": "0.8.0-beta.0", - "sucrase": "^3.35.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.11", - "tree-kill": "^1.2.2" - }, - "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" - }, - "engines": { - "node": ">=18" - }, - "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 - } - } - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/viem": { - "version": "2.23.4", - "resolved": "https://registry.npmjs.org/viem/-/viem-2.23.4.tgz", - "integrity": "sha512-UQquuolKlS1w5H5e0Fd1KKoUlIPJryIEBzY5AUhGyV1ka+9O6+3uYVhUzj6RbvGK0PtsMKn2ddwPZFwjNDVU/A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/wevm" - } - ], - "license": "MIT", - "dependencies": { - "@noble/curves": "1.8.1", - "@noble/hashes": "1.7.1", - "@scure/bip32": "1.6.2", - "@scure/bip39": "1.5.4", - "abitype": "1.0.8", - "isows": "1.0.6", - "ox": "0.6.7", - "ws": "8.18.0" - }, - "peerDependencies": { - "typescript": ">=5.0.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/viem/node_modules/@noble/curves": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", - "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.7.1" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@scure/bip32": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", - "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.8.1", - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@scure/bip32/node_modules/@noble/curves": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", - "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.7.2" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@scure/bip32/node_modules/@noble/hashes": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", - "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/@scure/bip39": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", - "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.4" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/viem/node_modules/abitype": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", - "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/wevm" - }, - "peerDependencies": { - "typescript": ">=5.0.4", - "zod": "^3 >=3.22.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/viem/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "license": "MIT", - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/write-json-file": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-6.0.0.tgz", - "integrity": "sha512-MNHcU3f9WxnNyR6MxsYSj64Jz0+dwIpisWKWq9gqLj/GwmA9INg3BZ3vt70/HB3GEwrnDQWr4RPrywnhNzmUFA==", - "license": "MIT", - "dependencies": { - "detect-indent": "^7.0.1", - "is-plain-obj": "^4.1.0", - "sort-keys": "^5.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/write-package": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/write-package/-/write-package-7.2.0.tgz", - "integrity": "sha512-uMQTubF/vcu+Wd0b5BGtDmiXePd/+44hUWQz2nZPbs92/BnxRo74tqs+hqDo12RLiEd+CXFKUwxvvIZvtt34Jw==", - "license": "MIT", - "dependencies": { - "deepmerge-ts": "^7.1.0", - "read-pkg": "^9.0.1", - "sort-keys": "^5.0.0", - "type-fest": "^4.23.0", - "write-json-file": "^6.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/write-package/node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/write-package/node_modules/normalize-package-data": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", - "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^7.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/write-package/node_modules/read-pkg": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", - "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", - "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.3", - "normalize-package-data": "^6.0.0", - "parse-json": "^8.0.0", - "type-fest": "^4.6.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/write-package/node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/contract-tests/package.json b/contract-tests/package.json deleted file mode 100644 index 3acf069c1d..0000000000 --- a/contract-tests/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "scripts": { - "test": "TS_NODE_PREFER_TS_EXTS=1 TS_NODE_TRANSPILE_ONLY=1 mocha --timeout 999999 --retries 3 --file src/setup.ts --require ts-node/register --extension ts \"test/**/*.ts\"" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@polkadot-api/descriptors": "file:.papi/descriptors", - "@polkadot-api/ink-contracts": "^0.4.1", - "@polkadot-api/sdk-ink": "^0.5.1", - "@polkadot-labs/hdkd": "^0.0.25", - "@polkadot-labs/hdkd-helpers": "^0.0.25", - "@polkadot/api": "^16.4.6", - "@polkadot/util-crypto": "^14.0.1", - "@types/mocha": "^10.0.10", - "dotenv": "17.2.1", - "ethers": "^6.13.5", - "mocha": "^11.1.0", - "polkadot-api": "^1.22.0", - "rxjs": "^7.8.2", - "scale-ts": "^1.6.1", - "viem": "2.23.4", - "ws": "^8.18.2" - }, - "devDependencies": { - "@types/chai": "^5.0.1", - "@types/node": "^22.18.0", - "assert": "^2.1.0", - "chai": "^6.0.1", - "prettier": "^3.3.3", - "ts-node": "^10.9.2", - "typescript": "^5.7.2" - } -} diff --git a/contract-tests/run-ci.sh b/contract-tests/run-ci.sh deleted file mode 100755 index 0ea0e72297..0000000000 --- a/contract-tests/run-ci.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -echo "start run-ci.sh" - -cd contract-tests - -cd bittensor - -rustup component add rust-src -cargo install cargo-contract -cargo contract build --release - -cd ../.. - -scripts/localnet.sh &>/dev/null & - -i=1 -while [ $i -le 2000 ]; do - if nc -z localhost 9944; then - echo "node subtensor is running after $i seconds" - break - fi - sleep 1 - i=$((i + 1)) -done - -# port not available exit with error -if [ "$i" -eq 2000 ]; then - exit 1 -fi - -sleep 10 - -if ! nc -z localhost 9944; then - echo "node subtensor exit, port not available" - exit 1 -fi - -cd contract-tests - -# required for papi in get-metadata.sh, but we cannot run yarn before papi as it adds the descriptors to the package.json which won't resolve -npm i -g polkadot-api - -bash get-metadata.sh - -sleep 5 - -yarn install --frozen-lockfile - -yarn run test -TEST_EXIT_CODE=$? - -if [ $TEST_EXIT_CODE -ne 0 ]; then - echo "Tests failed with exit code $TEST_EXIT_CODE" - pkill node-subtensor - exit $TEST_EXIT_CODE -fi - -pkill node-subtensor - -exit 0 \ No newline at end of file diff --git a/contract-tests/src/address-utils.ts b/contract-tests/src/address-utils.ts deleted file mode 100644 index 753eed2530..0000000000 --- a/contract-tests/src/address-utils.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Address } from "viem" -import { encodeAddress } from "@polkadot/util-crypto"; -import { ss58Address, ss58Decode } from "@polkadot-labs/hdkd-helpers"; -import { hexToU8a } from "@polkadot/util"; -import { blake2AsU8a, decodeAddress } from "@polkadot/util-crypto"; -import { Binary } from "polkadot-api"; -import { SS58_PREFIX } from "./config" - -export function toViemAddress(address: string): Address { - let addressNoPrefix = address.replace("0x", "") - return `0x${addressNoPrefix}` -} - -export function convertH160ToSS58(ethAddress: string) { - // get the public key - const hash = convertH160ToPublicKey(ethAddress); - - // Convert the hash to SS58 format - const ss58Address = encodeAddress(hash, SS58_PREFIX); - return ss58Address; -} - -export function convertPublicKeyToSs58(publickey: Uint8Array) { - return ss58Address(publickey, SS58_PREFIX); -} - -export function convertH160ToPublicKey(ethAddress: string) { - const prefix = "evm:"; - const prefixBytes = new TextEncoder().encode(prefix); - const addressBytes = hexToU8a( - ethAddress.startsWith("0x") ? ethAddress : `0x${ethAddress}` - ); - const combined = new Uint8Array(prefixBytes.length + addressBytes.length); - - // Concatenate prefix and Ethereum address - combined.set(prefixBytes); - combined.set(addressBytes, prefixBytes.length); - - // Hash the combined data (the public key) - const hash = blake2AsU8a(combined); - return hash; -} - -export function ss58ToEthAddress(ss58Address: string) { - // Decode the SS58 address to a Uint8Array public key - const publicKey = decodeAddress(ss58Address); - - // Take the first 20 bytes of the hashed public key for the Ethereum address - const ethereumAddressBytes = publicKey.slice(0, 20); - - // Convert the 20 bytes into an Ethereum H160 address format (Hex string) - const ethereumAddress = '0x' + Buffer.from(ethereumAddressBytes).toString('hex'); - - return ethereumAddress; -} - -export function ss58ToH160(ss58Address: string): Binary { - // Decode the SS58 address to a Uint8Array public key - const publicKey = decodeAddress(ss58Address); - - // Take the first 20 bytes of the hashed public key for the Ethereum address - const ethereumAddressBytes = publicKey.slice(0, 20); - - - return new Binary(ethereumAddressBytes); -} - -export function ethAddressToH160(ethAddress: string): Binary { - // Decode the SS58 address to a Uint8Array public key - const publicKey = hexToU8a(ethAddress); - - // Take the first 20 bytes of the hashed public key for the Ethereum address - // const ethereumAddressBytes = publicKey.slice(0, 20); - - - return new Binary(publicKey); -} \ No newline at end of file diff --git a/contract-tests/src/balance-math.ts b/contract-tests/src/balance-math.ts deleted file mode 100644 index 8d6e86bd5a..0000000000 --- a/contract-tests/src/balance-math.ts +++ /dev/null @@ -1,26 +0,0 @@ -import assert from "assert" - -export const TAO = BigInt(1000000000) // 10^9 -export const ETH_PER_RAO = BigInt(1000000000) // 10^9 -export const GWEI = BigInt(1000000000) // 10^9 -export const MAX_TX_FEE = BigInt(21000000) * GWEI // 100 times EVM to EVM transfer fee - -export function bigintToRao(value: bigint) { - return TAO * value -} - -export function tao(value: number) { - return TAO * BigInt(value) -} - -export function raoToEth(value: bigint) { - return ETH_PER_RAO * value -} - -export function compareEthBalanceWithTxFee(balance1: bigint, balance2: bigint) { - if (balance1 > balance2) { - assert((balance1 - balance2) < MAX_TX_FEE) - } else { - assert((balance2 - balance1) < MAX_TX_FEE) - } -} diff --git a/contract-tests/src/config.ts b/contract-tests/src/config.ts deleted file mode 100644 index 4cc3b27608..0000000000 --- a/contract-tests/src/config.ts +++ /dev/null @@ -1,58 +0,0 @@ -export const ETH_LOCAL_URL = 'http://localhost:9944' -export const SUB_LOCAL_URL = 'ws://localhost:9944' -export const SS58_PREFIX = 42; -// set the tx timeout as 2 second when eable the fast-runtime feature. -export const TX_TIMEOUT = 3000; - -export const IED25519VERIFY_ADDRESS = "0x0000000000000000000000000000000000000402"; -export const IEd25519VerifyABI = [ - { - inputs: [ - { internalType: "bytes32", name: "message", type: "bytes32" }, - { internalType: "bytes32", name: "publicKey", type: "bytes32" }, - { internalType: "bytes32", name: "r", type: "bytes32" }, - { internalType: "bytes32", name: "s", type: "bytes32" }, - ], - name: "verify", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "pure", - type: "function", - }, -]; - -export const ISr25519VERIFY_ADDRESS = "0x0000000000000000000000000000000000000403"; -export const ISr25519VerifyABI = [ - { - inputs: [ - { internalType: "bytes32", name: "message", type: "bytes32" }, - { internalType: "bytes32", name: "publicKey", type: "bytes32" }, - { internalType: "bytes32", name: "r", type: "bytes32" }, - { internalType: "bytes32", name: "s", type: "bytes32" }, - ], - name: "verify", - outputs: [{ internalType: "bool", name: "", type: "bool" }], - stateMutability: "pure", - type: "function", - }, -]; - -export const IBALANCETRANSFER_ADDRESS = "0x0000000000000000000000000000000000000800"; -export const IBalanceTransferABI = [ - { - inputs: [ - { - internalType: "bytes32", - name: "data", - type: "bytes32", - }, - ], - name: "transfer", - outputs: [], - stateMutability: "payable", - type: "function", - }, -]; - -export const IDISPATCH_ADDRESS = "0x0000000000000000000000000000000000000006"; - -export const ISTORAGE_QUERY_ADDRESS = "0x0000000000000000000000000000000000000807"; diff --git a/contract-tests/src/contracts/addressMapping.ts b/contract-tests/src/contracts/addressMapping.ts deleted file mode 100644 index 114c111d1c..0000000000 --- a/contract-tests/src/contracts/addressMapping.ts +++ /dev/null @@ -1,23 +0,0 @@ -export const IADDRESS_MAPPING_ADDRESS = "0x000000000000000000000000000000000000080c"; - -export const IAddressMappingABI = [ - { - "inputs": [ - { - "internalType": "address", - "name": "target_address", - "type": "address" - } - ], - "name": "addressMapping", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - } -]; diff --git a/contract-tests/src/contracts/alpha.ts b/contract-tests/src/contracts/alpha.ts deleted file mode 100644 index ae24298048..0000000000 --- a/contract-tests/src/contracts/alpha.ts +++ /dev/null @@ -1,332 +0,0 @@ -export const IALPHA_ADDRESS = "0x0000000000000000000000000000000000000808"; - -export const IAlphaABI = [ - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAlphaInEmission", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAlphaInPool", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAlphaIssuance", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAlphaOutEmission", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAlphaOutPool", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAlphaPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getEMAPriceHalvingBlocks", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getMovingAlphaPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getRootNetuid", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetMechanism", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetVolume", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getTaoInEmission", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getTaoInPool", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getTaoWeight", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "alpha", - "type": "uint64" - } - ], - "name": "simSwapAlphaForTao", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "tao", - "type": "uint64" - } - ], - "name": "simSwapTaoForAlpha", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getSumAlphaPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getCKBurn", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } -] \ No newline at end of file diff --git a/contract-tests/src/contracts/alphaPool.sol b/contract-tests/src/contracts/alphaPool.sol deleted file mode 100644 index 6b5d9b8c0e..0000000000 --- a/contract-tests/src/contracts/alphaPool.sol +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.2 <0.9.0; - -interface IStaking { - function transferStake( - bytes32 coldkey, - bytes32 hotkey, - uint256 netuid1, - uint256 netuid2, - uint256 amount - ) external; - function moveStake( - bytes32 hotkey1, - bytes32 hotkey2, - uint256 netuid1, - uint256 netuid2, - uint256 amount - ) external; - function getStake( - bytes32 hotkey, - bytes32 coldkey, - uint256 netuid - ) external view returns (uint256); -} - -contract AlphaPool { - bytes32 public contract_coldkey; - bytes32 public contract_hotkey; - address public constant ISTAKING_V2_ADDRESS = - 0x0000000000000000000000000000000000000805; - - mapping(address => mapping(uint256 => uint256)) public alphaBalance; - - constructor(bytes32 _contract_hotkey) { - contract_hotkey = _contract_hotkey; - } - - function setContractColdkey(bytes32 _contract_coldkey) public { - contract_coldkey = _contract_coldkey; - } - - function getContractStake(uint256 netuid) public view returns (uint256) { - return - IStaking(ISTAKING_V2_ADDRESS).getStake( - contract_hotkey, - contract_coldkey, - netuid - ); - } - - function depositAlpha( - uint256 _netuid, - uint256 _alphaAmount, - bytes32 _hotkey - ) public { - require(contract_coldkey != 0x00, "contract coldkey not set"); - uint256 contractStake = getContractStake(_netuid); - - bytes memory data = abi.encodeWithSelector( - IStaking.transferStake.selector, - contract_coldkey, - _hotkey, - _netuid, - _netuid, - _alphaAmount - ); - (bool success, ) = address(ISTAKING_V2_ADDRESS).delegatecall{ - gas: gasleft() - }(data); - require(success, "user deposit alpha call failed"); - - uint256 newContractStake = getContractStake(_netuid); - - require( - newContractStake > contractStake, - "contract stake decreased after deposit" - ); - - // use the increased stake as the actual alpha amount, for the swap fee in the move stake call - // the contract will take it and get compensated by laster emission of alpha - uint256 actualAlphaAmount = newContractStake - contractStake; - - if (_hotkey != contract_hotkey) { - data = abi.encodeWithSelector( - IStaking.moveStake.selector, - _hotkey, - contract_hotkey, - _netuid, - _netuid, - actualAlphaAmount - ); - (success, ) = address(ISTAKING_V2_ADDRESS).call{gas: gasleft()}( - data - ); - require(success, "user deposit, move stake call failed"); - } - - alphaBalance[msg.sender][_netuid] += actualAlphaAmount; - } - - function withdrawAlpha( - uint256 _netuid, - uint256 _alphaAmount, - bytes32 _user_coldkey - ) public { - require(contract_coldkey != 0x00, "contract coldkey not set"); - require( - alphaBalance[msg.sender][_netuid] >= _alphaAmount, - "user withdraw, insufficient alpha balance" - ); - uint256 contractStake = getContractStake(_netuid); - - alphaBalance[msg.sender][_netuid] -= _alphaAmount; - - bytes memory data = abi.encodeWithSelector( - IStaking.transferStake.selector, - _user_coldkey, - contract_hotkey, - _netuid, - _netuid, - _alphaAmount - ); - (bool success, ) = address(ISTAKING_V2_ADDRESS).call{gas: gasleft()}( - data - ); - - uint256 newContractStake = getContractStake(_netuid); - require( - newContractStake < contractStake, - "contract stake increased after withdraw" - ); - require(success, "user withdraw alpha call failed"); - } -} diff --git a/contract-tests/src/contracts/alphaPool.ts b/contract-tests/src/contracts/alphaPool.ts deleted file mode 100644 index f52ff6436e..0000000000 --- a/contract-tests/src/contracts/alphaPool.ts +++ /dev/null @@ -1,156 +0,0 @@ -export const ALPHA_POOL_CONTRACT_ABI = [ - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_contract_hotkey", - "type": "bytes32" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "ISTAKING_V2_ADDRESS", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "alphaBalance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "contract_coldkey", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "contract_hotkey", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_alphaAmount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_hotkey", - "type": "bytes32" - } - ], - "name": "depositAlpha", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "getContractStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_contract_coldkey", - "type": "bytes32" - } - ], - "name": "setContractColdkey", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_alphaAmount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_user_coldkey", - "type": "bytes32" - } - ], - "name": "withdrawAlpha", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -]; - -export const ALPHA_POOL_CONTRACT_BYTECODE = "6080604052348015600e575f5ffd5b506040516110d83803806110d88339818101604052810190602e9190606c565b80600181905550506092565b5f5ffd5b5f819050919050565b604e81603e565b81146057575f5ffd5b50565b5f815190506066816047565b92915050565b5f60208284031215607e57607d603a565b5b5f608984828501605a565b91505092915050565b6110398061009f5f395ff3fe608060405234801561000f575f5ffd5b5060043610610086575f3560e01c8063cdcde3e911610059578063cdcde3e914610110578063d67c076114610140578063f0d6bb891461015e578063fdbcdce91461017a57610086565b80632849912d1461008a5780633af975ff146100a657806359948a67146100c4578063bee0bca1146100f4575b5f5ffd5b6100a4600480360381019061009f919061090d565b610198565b005b6100ae610518565b6040516100bb919061096c565b60405180910390f35b6100de60048036038101906100d991906109df565b61051d565b6040516100eb9190610a2c565b60405180910390f35b61010e6004803603810190610109919061090d565b61053d565b005b61012a60048036038101906101259190610a45565b610805565b6040516101379190610a2c565b60405180910390f35b61014861088e565b604051610155919061096c565b60405180910390f35b61017860048036038101906101739190610a70565b610894565b005b61018261089d565b60405161018f9190610aaa565b60405180910390f35b5f5f1b5f54036101dd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101d490610b1d565b60405180910390fd5b5f6101e784610805565b90505f6317ce5f6260e01b5f548487888860405160240161020c959493929190610b3b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516102949190610bde565b5f604051808303818686f4925050503d805f81146102cd576040519150601f19603f3d011682016040523d82523d5f602084013e6102d2565b606091505b5050905080610316576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030d90610c3e565b60405180910390fd5b5f61032087610805565b9050838111610364576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035b90610ccc565b60405180910390fd5b5f84826103719190610d17565b905060015486146104ac57631149f65960e01b866001548a8b8560405160240161039f959493929190610b3b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050935061080573ffffffffffffffffffffffffffffffffffffffff165a856040516104269190610bde565b5f604051808303815f8787f1925050503d805f8114610460576040519150601f19603f3d011682016040523d82523d5f602084013e610465565b606091505b505080935050826104ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104a290610dba565b60405180910390fd5b5b8060025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8a81526020019081526020015f205f8282546105079190610dd8565b925050819055505050505050505050565b5f5481565b6002602052815f5260405f20602052805f5260405f205f91509150505481565b5f5f1b5f5403610582576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057990610b1d565b60405180910390fd5b8160025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8581526020019081526020015f20541015610611576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060890610e7b565b60405180910390fd5b5f61061b84610805565b90508260025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8681526020019081526020015f205f8282546106789190610d17565b925050819055505f6317ce5f6260e01b836001548788886040516024016106a3959493929190610b3b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a8360405161072b9190610bde565b5f604051808303815f8787f1925050503d805f8114610765576040519150601f19603f3d011682016040523d82523d5f602084013e61076a565b606091505b505090505f61077887610805565b90508381106107bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b390610f09565b60405180910390fd5b816107fc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107f390610f71565b60405180910390fd5b50505050505050565b5f61080573ffffffffffffffffffffffffffffffffffffffff1663e3b598fa6001545f54856040518463ffffffff1660e01b815260040161084893929190610f8f565b602060405180830381865afa158015610863573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108879190610fd8565b9050919050565b60015481565b805f8190555050565b61080581565b5f5ffd5b5f819050919050565b6108b9816108a7565b81146108c3575f5ffd5b50565b5f813590506108d4816108b0565b92915050565b5f819050919050565b6108ec816108da565b81146108f6575f5ffd5b50565b5f81359050610907816108e3565b92915050565b5f5f5f60608486031215610924576109236108a3565b5b5f610931868287016108c6565b9350506020610942868287016108c6565b9250506040610953868287016108f9565b9150509250925092565b610966816108da565b82525050565b5f60208201905061097f5f83018461095d565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6109ae82610985565b9050919050565b6109be816109a4565b81146109c8575f5ffd5b50565b5f813590506109d9816109b5565b92915050565b5f5f604083850312156109f5576109f46108a3565b5b5f610a02858286016109cb565b9250506020610a13858286016108c6565b9150509250929050565b610a26816108a7565b82525050565b5f602082019050610a3f5f830184610a1d565b92915050565b5f60208284031215610a5a57610a596108a3565b5b5f610a67848285016108c6565b91505092915050565b5f60208284031215610a8557610a846108a3565b5b5f610a92848285016108f9565b91505092915050565b610aa4816109a4565b82525050565b5f602082019050610abd5f830184610a9b565b92915050565b5f82825260208201905092915050565b7f636f6e747261637420636f6c646b6579206e6f742073657400000000000000005f82015250565b5f610b07601883610ac3565b9150610b1282610ad3565b602082019050919050565b5f6020820190508181035f830152610b3481610afb565b9050919050565b5f60a082019050610b4e5f83018861095d565b610b5b602083018761095d565b610b686040830186610a1d565b610b756060830185610a1d565b610b826080830184610a1d565b9695505050505050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f610bb882610b8c565b610bc28185610b96565b9350610bd2818560208601610ba0565b80840191505092915050565b5f610be98284610bae565b915081905092915050565b7f75736572206465706f73697420616c7068612063616c6c206661696c656400005f82015250565b5f610c28601e83610ac3565b9150610c3382610bf4565b602082019050919050565b5f6020820190508181035f830152610c5581610c1c565b9050919050565b7f636f6e7472616374207374616b652064656372656173656420616674657220645f8201527f65706f7369740000000000000000000000000000000000000000000000000000602082015250565b5f610cb6602683610ac3565b9150610cc182610c5c565b604082019050919050565b5f6020820190508181035f830152610ce381610caa565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610d21826108a7565b9150610d2c836108a7565b9250828203905081811115610d4457610d43610cea565b5b92915050565b7f75736572206465706f7369742c206d6f7665207374616b652063616c6c2066615f8201527f696c656400000000000000000000000000000000000000000000000000000000602082015250565b5f610da4602483610ac3565b9150610daf82610d4a565b604082019050919050565b5f6020820190508181035f830152610dd181610d98565b9050919050565b5f610de2826108a7565b9150610ded836108a7565b9250828201905080821115610e0557610e04610cea565b5b92915050565b7f757365722077697468647261772c20696e73756666696369656e7420616c70685f8201527f612062616c616e63650000000000000000000000000000000000000000000000602082015250565b5f610e65602983610ac3565b9150610e7082610e0b565b604082019050919050565b5f6020820190508181035f830152610e9281610e59565b9050919050565b7f636f6e7472616374207374616b6520696e6372656173656420616674657220775f8201527f6974686472617700000000000000000000000000000000000000000000000000602082015250565b5f610ef3602783610ac3565b9150610efe82610e99565b604082019050919050565b5f6020820190508181035f830152610f2081610ee7565b9050919050565b7f7573657220776974686472617720616c7068612063616c6c206661696c6564005f82015250565b5f610f5b601f83610ac3565b9150610f6682610f27565b602082019050919050565b5f6020820190508181035f830152610f8881610f4f565b9050919050565b5f606082019050610fa25f83018661095d565b610faf602083018561095d565b610fbc6040830184610a1d565b949350505050565b5f81519050610fd2816108b0565b92915050565b5f60208284031215610fed57610fec6108a3565b5b5f610ffa84828501610fc4565b9150509291505056fea2646970667358221220a0d4b2eb5f0c7f74a27f987e803ae1c8465e0da35f09c240ddb6bac757ce422164736f6c634300081e0033"; diff --git a/contract-tests/src/contracts/bridgeToken.ts b/contract-tests/src/contracts/bridgeToken.ts deleted file mode 100644 index f8b3ea4d03..0000000000 --- a/contract-tests/src/contracts/bridgeToken.ts +++ /dev/null @@ -1,631 +0,0 @@ -export const BRIDGE_TOKEN_CONTRACT_ABI = [ - { - "inputs": [ - { - "internalType": "string", - "name": "name_", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol_", - "type": "string" - }, - { - "internalType": "address", - "name": "admin", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "AccessControlBadConfirmation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "neededRole", - "type": "bytes32" - } - ], - "name": "AccessControlUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "allowance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientBalance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "approver", - "type": "address" - } - ], - "name": "ERC20InvalidApprover", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "ERC20InvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "ERC20InvalidSender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "ERC20InvalidSpender", - "type": "error" - }, - { - "inputs": [], - "name": "UnauthorizedHandler", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "previousAdminRole", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newAdminRole", - "type": "bytes32" - } - ], - "name": "RoleAdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleRevoked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [], - "name": "DEFAULT_ADMIN_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "burnFrom", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleAdmin", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "grantRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "hasRole", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "isAdmin", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "mint", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "callerConfirmation", - "type": "address" - } - ], - "name": "renounceRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "revokeRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceId", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } -]; - -export const BRIDGE_TOKEN_CONTRACT_BYTECODE = "0x60806040523480156200001157600080fd5b5060405162000fac38038062000fac8339810160408190526200003491620001ea565b8282600362000044838262000308565b50600462000053828262000308565b5062000065915060009050826200006f565b50505050620003d4565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff16620001185760008381526005602090815260408083206001600160a01b03861684529091529020805460ff19166001179055620000cf3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016200011c565b5060005b92915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200014a57600080fd5b81516001600160401b038082111562000167576200016762000122565b604051601f8301601f19908116603f0116810190828211818310171562000192576200019262000122565b8160405283815260209250866020858801011115620001b057600080fd5b600091505b83821015620001d45785820183015181830184015290820190620001b5565b6000602085830101528094505050505092915050565b6000806000606084860312156200020057600080fd5b83516001600160401b03808211156200021857600080fd5b620002268783880162000138565b945060208601519150808211156200023d57600080fd5b506200024c8682870162000138565b604086015190935090506001600160a01b03811681146200026c57600080fd5b809150509250925092565b600181811c908216806200028c57607f821691505b602082108103620002ad57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000303576000816000526020600020601f850160051c81016020861015620002de5750805b601f850160051c820191505b81811015620002ff57828155600101620002ea565b5050505b505050565b81516001600160401b0381111562000324576200032462000122565b6200033c8162000335845462000277565b84620002b3565b602080601f8311600181146200037457600084156200035b5750858301515b600019600386901b1c1916600185901b178555620002ff565b600085815260208120601f198616915b82811015620003a55788860151825594840194600190910190840162000384565b5085821015620003c45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610bc880620003e46000396000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c806340c10f19116100ad57806395d89b411161007157806395d89b4114610288578063a217fddf14610290578063a9059cbb14610298578063d547741f146102ab578063dd62ed3e146102be57600080fd5b806340c10f191461021357806342966c681461022657806370a082311461023957806379cc67901461026257806391d148541461027557600080fd5b8063248a9ca3116100f4578063248a9ca3146101a657806324d7806c146101c95780632f2ff15d146101dc578063313ce567146101f157806336568abe1461020057600080fd5b806301ffc9a71461013157806306fdde0314610159578063095ea7b31461016e57806318160ddd1461018157806323b872dd14610193575b600080fd5b61014461013f3660046109ab565b6102f7565b60405190151581526020015b60405180910390f35b61016161032e565b60405161015091906109dc565b61014461017c366004610a47565b6103c0565b6002545b604051908152602001610150565b6101446101a1366004610a71565b6103d8565b6101856101b4366004610aad565b60009081526005602052604090206001015490565b6101446101d7366004610ac6565b6103fc565b6101ef6101ea366004610ae1565b610408565b005b60405160128152602001610150565b6101ef61020e366004610ae1565b610433565b6101ef610221366004610a47565b61046b565b6101ef610234366004610aad565b610480565b610185610247366004610ac6565b6001600160a01b031660009081526020819052604090205490565b6101ef610270366004610a47565b61048d565b610144610283366004610ae1565b6104a2565b6101616104cd565b610185600081565b6101446102a6366004610a47565b6104dc565b6101ef6102b9366004610ae1565b6104ea565b6101856102cc366004610b0d565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60006001600160e01b03198216637965db0b60e01b148061032857506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606003805461033d90610b37565b80601f016020809104026020016040519081016040528092919081815260200182805461036990610b37565b80156103b65780601f1061038b576101008083540402835291602001916103b6565b820191906000526020600020905b81548152906001019060200180831161039957829003601f168201915b5050505050905090565b6000336103ce81858561050f565b5060019392505050565b6000336103e685828561051c565b6103f1858585610599565b506001949350505050565b600061032881836104a2565b600082815260056020526040902060010154610423816105f8565b61042d8383610602565b50505050565b6001600160a01b038116331461045c5760405163334bd91960e11b815260040160405180910390fd5b6104668282610696565b505050565b6000610476816105f8565b6104668383610703565b61048a338261073d565b50565b6000610498816105f8565b610466838361073d565b60009182526005602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60606004805461033d90610b37565b6000336103ce818585610599565b600082815260056020526040902060010154610505816105f8565b61042d8383610696565b6104668383836001610773565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461042d578181101561058a57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61042d84848484036000610773565b6001600160a01b0383166105c357604051634b637e8f60e11b815260006004820152602401610581565b6001600160a01b0382166105ed5760405163ec442f0560e01b815260006004820152602401610581565b610466838383610848565b61048a8133610972565b600061060e83836104a2565b61068e5760008381526005602090815260408083206001600160a01b03861684529091529020805460ff191660011790556106463390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610328565b506000610328565b60006106a283836104a2565b1561068e5760008381526005602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610328565b6001600160a01b03821661072d5760405163ec442f0560e01b815260006004820152602401610581565b61073960008383610848565b5050565b6001600160a01b03821661076757604051634b637e8f60e11b815260006004820152602401610581565b61073982600083610848565b6001600160a01b03841661079d5760405163e602df0560e01b815260006004820152602401610581565b6001600160a01b0383166107c757604051634a1406b160e11b815260006004820152602401610581565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561042d57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161083a91815260200190565b60405180910390a350505050565b6001600160a01b0383166108735780600260008282546108689190610b71565b909155506108e59050565b6001600160a01b038316600090815260208190526040902054818110156108c65760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610581565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b03821661090157600280548290039055610920565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161096591815260200190565b60405180910390a3505050565b61097c82826104a2565b6107395760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610581565b6000602082840312156109bd57600080fd5b81356001600160e01b0319811681146109d557600080fd5b9392505050565b60006020808352835180602085015260005b81811015610a0a578581018301518582016040015282016109ee565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610a4257600080fd5b919050565b60008060408385031215610a5a57600080fd5b610a6383610a2b565b946020939093013593505050565b600080600060608486031215610a8657600080fd5b610a8f84610a2b565b9250610a9d60208501610a2b565b9150604084013590509250925092565b600060208284031215610abf57600080fd5b5035919050565b600060208284031215610ad857600080fd5b6109d582610a2b565b60008060408385031215610af457600080fd5b82359150610b0460208401610a2b565b90509250929050565b60008060408385031215610b2057600080fd5b610b2983610a2b565b9150610b0460208401610a2b565b600181811c90821680610b4b57607f821691505b602082108103610b6b57634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561032857634e487b7160e01b600052601160045260246000fdfea2646970667358221220e179fc58c926e64cb6e87416f8ca64c117044e3195b184afe45038857606c15364736f6c63430008160033" diff --git a/contract-tests/src/contracts/crowdloan.ts b/contract-tests/src/contracts/crowdloan.ts deleted file mode 100644 index e104c7d4bd..0000000000 --- a/contract-tests/src/contracts/crowdloan.ts +++ /dev/null @@ -1,261 +0,0 @@ -export const ICROWDLOAN_ADDRESS = "0x0000000000000000000000000000000000000809"; - -export const ICrowdloanABI = [ - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - }, - { - "internalType": "uint64", - "name": "amount", - "type": "uint64" - } - ], - "name": "contribute", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "deposit", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "minContribution", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "cap", - "type": "uint64" - }, - { - "internalType": "uint32", - "name": "end", - "type": "uint32" - }, - { - "internalType": "address", - "name": "targetAddress", - "type": "address" - } - ], - "name": "create", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - } - ], - "name": "dissolve", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - } - ], - "name": "finalize", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - }, - { - "internalType": "bytes32", - "name": "coldkey", - "type": "bytes32" - } - ], - "name": "getContribution", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - } - ], - "name": "getCrowdloan", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "creator", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "deposit", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "min_contribution", - "type": "uint64" - }, - { - "internalType": "uint32", - "name": "end", - "type": "uint32" - }, - { - "internalType": "uint64", - "name": "cap", - "type": "uint64" - }, - { - "internalType": "bytes32", - "name": "funds_account", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "raised", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "has_target_address", - "type": "bool" - }, - { - "internalType": "bytes32", - "name": "target_address", - "type": "bytes32" - }, - { - "internalType": "bool", - "name": "finalized", - "type": "bool" - }, - { - "internalType": "uint32", - "name": "contributors_count", - "type": "uint32" - } - ], - "internalType": "struct CrowdloanInfo", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - } - ], - "name": "refund", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - }, - { - "internalType": "uint64", - "name": "newCap", - "type": "uint64" - } - ], - "name": "updateCap", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "newEnd", - "type": "uint32" - } - ], - "name": "updateEnd", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - }, - { - "internalType": "uint64", - "name": "newMinContribution", - "type": "uint64" - } - ], - "name": "updateMinContribution", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - } - ], - "name": "withdraw", - "outputs": [], - "stateMutability": "payable", - "type": "function" - } -] \ No newline at end of file diff --git a/contract-tests/src/contracts/incremental.sol b/contract-tests/src/contracts/incremental.sol deleted file mode 100644 index 2b3bc2fd49..0000000000 --- a/contract-tests/src/contracts/incremental.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.8.2 <0.9.0; - -contract Storage { - uint256 number; - - /** - * @dev Store value in variable - * @param num value to store - */ - function store(uint256 num) public { - number = num; - } - - /** - * @dev Return value - * @return value of 'number' - */ - function retrieve() public view returns (uint256) { - return number; - } -} diff --git a/contract-tests/src/contracts/incremental.ts b/contract-tests/src/contracts/incremental.ts deleted file mode 100644 index b19909e491..0000000000 --- a/contract-tests/src/contracts/incremental.ts +++ /dev/null @@ -1,39 +0,0 @@ -export const INCREMENTAL_CONTRACT_ABI = [ - { - "inputs": [], - "name": "retrieve", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "num", - "type": "uint256" - } - ], - "name": "store", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -]; - -/* -"compiler": { - "version": "0.8.26+commit.8a97fa7a" - }, -*/ - -export const INCREMENTAL_CONTRACT_BYTECODE = "6080604052348015600e575f80fd5b506101438061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80632e64cec1146100385780636057361d14610056575b5f80fd5b610040610072565b60405161004d919061009b565b60405180910390f35b610070600480360381019061006b91906100e2565b61007a565b005b5f8054905090565b805f8190555050565b5f819050919050565b61009581610083565b82525050565b5f6020820190506100ae5f83018461008c565b92915050565b5f80fd5b6100c181610083565b81146100cb575f80fd5b50565b5f813590506100dc816100b8565b92915050565b5f602082840312156100f7576100f66100b4565b5b5f610104848285016100ce565b9150509291505056fea26469706673582212209a0dd35336aff1eb3eeb11db76aa60a1427a12c1b92f945ea8c8d1dfa337cf2264736f6c634300081a0033" - - - diff --git a/contract-tests/src/contracts/leasing.ts b/contract-tests/src/contracts/leasing.ts deleted file mode 100644 index 80321c7462..0000000000 --- a/contract-tests/src/contracts/leasing.ts +++ /dev/null @@ -1,174 +0,0 @@ -export const ILEASING_ADDRESS = "0x000000000000000000000000000000000000080a"; - -export const ILeasingABI = [ - { - "inputs": [ - { - "internalType": "uint64", - "name": "crowdloanDeposit", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "crowdloanMinContribution", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "crowdloanCap", - "type": "uint64" - }, - { - "internalType": "uint32", - "name": "crowdloanEnd", - "type": "uint32" - }, - { - "internalType": "uint8", - "name": "leasingEmissionsShare", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "hasLeasingEndBlock", - "type": "bool" - }, - { - "internalType": "uint32", - "name": "leasingEndBlock", - "type": "uint32" - } - ], - "name": "createLeaseCrowdloan", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "leaseId", - "type": "uint32" - }, - { - "internalType": "bytes32", - "name": "contributor", - "type": "bytes32" - } - ], - "name": "getContributorShare", - "outputs": [ - { - "internalType": "uint128", - "name": "", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "leaseId", - "type": "uint32" - } - ], - "name": "getLease", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "beneficiary", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "coldkey", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "emissions_share", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "has_end_block", - "type": "bool" - }, - { - "internalType": "uint32", - "name": "end_block", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "cost", - "type": "uint64" - } - ], - "internalType": "struct LeaseInfo", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getLeaseIdForSubnet", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "leaseId", - "type": "uint32" - }, - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - } - ], - "name": "terminateLease", - "outputs": [], - "stateMutability": "payable", - "type": "function" - } -] \ No newline at end of file diff --git a/contract-tests/src/contracts/metagraph.ts b/contract-tests/src/contracts/metagraph.ts deleted file mode 100644 index d0c3bf5154..0000000000 --- a/contract-tests/src/contracts/metagraph.ts +++ /dev/null @@ -1,391 +0,0 @@ -export const IMETAGRAPH_ADDRESS = "0x0000000000000000000000000000000000000802"; - -export const IMetagraphABI = [ - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getAxon", - outputs: [ - { - components: [ - { - internalType: "uint64", - name: "block", - type: "uint64", - }, - { - internalType: "uint32", - name: "version", - type: "uint32", - }, - { - internalType: "uint128", - name: "ip", - type: "uint128", - }, - { - internalType: "uint16", - name: "port", - type: "uint16", - }, - { - internalType: "uint8", - name: "ip_type", - type: "uint8", - }, - { - internalType: "uint8", - name: "protocol", - type: "uint8", - }, - ], - internalType: "struct AxonInfo", - name: "", - type: "tuple", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getColdkey", - outputs: [ - { - internalType: "bytes32", - name: "", - type: "bytes32", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getConsensus", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getDividends", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getEmission", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getHotkey", - outputs: [ - { - internalType: "bytes32", - name: "", - type: "bytes32", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getIncentive", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getIsActive", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getLastUpdate", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getRank", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getStake", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getTrust", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getUidCount", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getValidatorStatus", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "uid", - type: "uint16", - }, - ], - name: "getVtrust", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, -]; \ No newline at end of file diff --git a/contract-tests/src/contracts/neuron.ts b/contract-tests/src/contracts/neuron.ts deleted file mode 100644 index 4a8fb47e4c..0000000000 --- a/contract-tests/src/contracts/neuron.ts +++ /dev/null @@ -1,235 +0,0 @@ -export const INEURON_ADDRESS = "0x0000000000000000000000000000000000000804"; - -export const INeuronABI = [ - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "bytes32", - name: "commitHash", - type: "bytes32", - }, - ], - name: "commitWeights", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16[]", - name: "uids", - type: "uint16[]", - }, - { - internalType: "uint16[]", - name: "values", - type: "uint16[]", - }, - { - internalType: "uint16[]", - name: "salt", - type: "uint16[]", - }, - { - internalType: "uint64", - name: "versionKey", - type: "uint64", - }, - ], - name: "revealWeights", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16[]", - name: "dests", - type: "uint16[]", - }, - { - internalType: "uint16[]", - name: "weights", - type: "uint16[]", - }, - { - internalType: "uint64", - name: "versionKey", - type: "uint64", - }, - ], - name: "setWeights", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint32", - name: "version", - type: "uint32", - }, - { - internalType: "uint128", - name: "ip", - type: "uint128", - }, - { - internalType: "uint16", - name: "port", - type: "uint16", - }, - { - internalType: "uint8", - name: "ipType", - type: "uint8", - }, - { - internalType: "uint8", - name: "protocol", - type: "uint8", - }, - { - internalType: "uint8", - name: "placeholder1", - type: "uint8", - }, - { - internalType: "uint8", - name: "placeholder2", - type: "uint8", - }, - ], - name: "serveAxon", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint32", - name: "version", - type: "uint32", - }, - { - internalType: "uint128", - name: "ip", - type: "uint128", - }, - { - internalType: "uint16", - name: "port", - type: "uint16", - }, - { - internalType: "uint8", - name: "ipType", - type: "uint8", - }, - { - internalType: "uint8", - name: "protocol", - type: "uint8", - }, - { - internalType: "uint8", - name: "placeholder1", - type: "uint8", - }, - { - internalType: "uint8", - name: "placeholder2", - type: "uint8", - }, - { - internalType: "bytes", - name: "certificate", - type: "bytes", - }, - ], - name: "serveAxonTls", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint32", - name: "version", - type: "uint32", - }, - { - internalType: "uint128", - name: "ip", - type: "uint128", - }, - { - internalType: "uint16", - name: "port", - type: "uint16", - }, - { - internalType: "uint8", - name: "ipType", - type: "uint8", - }, - ], - name: "servePrometheus", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "bytes32", - name: "hotkey", - type: "bytes32", - }, - ], - name: "burnedRegister", - outputs: [], - stateMutability: "payable", - type: "function", - }, -]; \ No newline at end of file diff --git a/contract-tests/src/contracts/precompileGas.sol b/contract-tests/src/contracts/precompileGas.sol deleted file mode 100644 index ee31bce61d..0000000000 --- a/contract-tests/src/contracts/precompileGas.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -interface ISR25519Verify { - function verify( - bytes32 message, - bytes32 publicKey, - bytes32 r, - bytes32 s - ) external pure returns (bool); -} - -interface IED25519Verify { - function verify( - bytes32 message, - bytes32 publicKey, - bytes32 r, - bytes32 s - ) external pure returns (bool); -} - -contract PrecompileGas { - address constant IED25519VERIFY_ADDRESS = - 0x0000000000000000000000000000000000000402; - address constant ISR25519VERIFY_ADDRESS = - 0x0000000000000000000000000000000000000403; - IED25519Verify constant ed25519 = IED25519Verify(IED25519VERIFY_ADDRESS); - ISR25519Verify constant sr25519 = ISR25519Verify(ISR25519VERIFY_ADDRESS); - - event Log(string message); - - bytes32 message = - 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - bytes32 publicKey = - 0x0000000000000000000000000000000000000000000000000000000000000000; - bytes32 r = - 0x0000000000000000000000000000000000000000000000000000000000000000; - bytes32 s = - 0x0000000000000000000000000000000000000000000000000000000000000000; - - /** - * @notice Call the precompile using hardcoded signature data - * @param iterations Number of times to call the precompile - */ - function callED25519(uint64 iterations) external { - for (uint64 i = 0; i < iterations; i++) { - ed25519.verify(message, publicKey, r, s); - } - emit Log("callED25519"); - } - - /** - * @notice Call the precompile using hardcoded signature data - * @param iterations Number of times to call the precompile - */ - function callSR25519(uint64 iterations) external { - for (uint64 i = 0; i < iterations; i++) { - sr25519.verify(message, publicKey, r, s); - } - emit Log("callSR25519"); - } -} diff --git a/contract-tests/src/contracts/precompileGas.ts b/contract-tests/src/contracts/precompileGas.ts deleted file mode 100644 index d94cfd5d26..0000000000 --- a/contract-tests/src/contracts/precompileGas.ts +++ /dev/null @@ -1,44 +0,0 @@ - -export const PrecompileGas_CONTRACT_ABI = [ - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "string", - "name": "message", - "type": "string" - } - ], - "name": "Log", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "iterations", - "type": "uint64" - } - ], - "name": "callED25519", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "iterations", - "type": "uint64" - } - ], - "name": "callSR25519", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] - -export const PrecompileGas_CONTRACT_BYTECODE = "60806040527f1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef5f1b5f555f5f1b6001555f5f1b6002555f5f1b6003553480156045575f5ffd5b5061048b806100535f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806356554a5714610038578063bd9cac2b14610054575b5f5ffd5b610052600480360381019061004d919061028f565b610070565b005b61006e6004803603810190610069919061028f565b61015f565b005b5f5f90505b8167ffffffffffffffff168167ffffffffffffffff1610156101265761040373ffffffffffffffffffffffffffffffffffffffff1663869adcb95f546001546002546003546040518563ffffffff1660e01b81526004016100d994939291906102d2565b602060405180830381865afa1580156100f4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610118919061034a565b508080600101915050610075565b507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab604051610154906103cf565b60405180910390a150565b5f5f90505b8167ffffffffffffffff168167ffffffffffffffff1610156102155761040273ffffffffffffffffffffffffffffffffffffffff1663869adcb95f546001546002546003546040518563ffffffff1660e01b81526004016101c894939291906102d2565b602060405180830381865afa1580156101e3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610207919061034a565b508080600101915050610164565b507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab60405161024390610437565b60405180910390a150565b5f5ffd5b5f67ffffffffffffffff82169050919050565b61026e81610252565b8114610278575f5ffd5b50565b5f8135905061028981610265565b92915050565b5f602082840312156102a4576102a361024e565b5b5f6102b18482850161027b565b91505092915050565b5f819050919050565b6102cc816102ba565b82525050565b5f6080820190506102e55f8301876102c3565b6102f260208301866102c3565b6102ff60408301856102c3565b61030c60608301846102c3565b95945050505050565b5f8115159050919050565b61032981610315565b8114610333575f5ffd5b50565b5f8151905061034481610320565b92915050565b5f6020828403121561035f5761035e61024e565b5b5f61036c84828501610336565b91505092915050565b5f82825260208201905092915050565b7f63616c6c535232353531390000000000000000000000000000000000000000005f82015250565b5f6103b9600b83610375565b91506103c482610385565b602082019050919050565b5f6020820190508181035f8301526103e6816103ad565b9050919050565b7f63616c6c454432353531390000000000000000000000000000000000000000005f82015250565b5f610421600b83610375565b915061042c826103ed565b602082019050919050565b5f6020820190508181035f83015261044e81610415565b905091905056fea26469706673582212202addcdae9c59ee78157cddedc3148678edf455132bdfc62347f85e7c660b4d2164736f6c634300081e0033" \ No newline at end of file diff --git a/contract-tests/src/contracts/precompileWrapper.sol b/contract-tests/src/contracts/precompileWrapper.sol deleted file mode 100644 index 9f5fe242c1..0000000000 --- a/contract-tests/src/contracts/precompileWrapper.sol +++ /dev/null @@ -1,362 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -// Precompile addresses -address constant ISUBTENSOR_BALANCE_TRANSFER_ADDRESS = 0x0000000000000000000000000000000000000800; -address constant IMETAGRAPH_ADDRESS = 0x0000000000000000000000000000000000000802; -address constant ISUBNET_ADDRESS = 0x0000000000000000000000000000000000000803; -address constant INEURON_ADDRESS = 0x0000000000000000000000000000000000000804; -address constant ISTAKING_V2_ADDRESS = 0x0000000000000000000000000000000000000805; -address constant IUID_LOOKUP_ADDRESS = 0x0000000000000000000000000000000000000806; -address constant IALPHA_ADDRESS = 0x0000000000000000000000000000000000000808; -address constant ICROWDLOAN_ADDRESS = 0x0000000000000000000000000000000000000809; -address constant ILEASING_ADDRESS = 0x000000000000000000000000000000000000080a; -address constant IPROXY_ADDRESS = 0x000000000000000000000000000000000000080b; -address constant IADDRESS_MAPPING_ADDRESS = 0x000000000000000000000000000000000000080C; - -// Interface definitions -interface ISubtensorBalanceTransfer { - function transfer(bytes32 data) external payable; -} - -interface IMetagraph { - function getUidCount(uint16 netuid) external view returns (uint16); -} - -interface ISubnet { - function registerNetwork( - bytes32 hotkey, - string memory subnetName, - string memory githubRepo, - string memory subnetContact, - string memory subnetUrl, - string memory discord, - string memory description, - string memory additional - ) external payable; - function getServingRateLimit(uint16 netuid) external view returns (uint64); -} - -interface INeuron { - function burnedRegister(uint16 netuid, bytes32 hotkey) external payable; -} - -interface IStaking { - function addStake( - bytes32 hotkey, - uint256 amount, - uint256 netuid - ) external payable; - function removeStake( - bytes32 hotkey, - uint256 amount, - uint256 netuid - ) external payable; - function getTotalColdkeyStake( - bytes32 coldkey - ) external view returns (uint256); - function getTotalHotkeyStake( - bytes32 hotkey - ) external view returns (uint256); -} - -struct LookupItem { - uint16 uid; - uint64 block_associated; -} - -interface IUidLookup { - function uidLookup( - uint16 netuid, - address evm_address, - uint16 limit - ) external view returns (LookupItem[] memory); -} - -interface IAlpha { - function getAlphaPrice(uint16 netuid) external view returns (uint256); -} - -struct CrowdloanInfo { - bytes32 creator; - uint64 deposit; - uint64 min_contribution; - uint32 end; - uint64 cap; - bytes32 funds_account; - uint64 raised; - bool has_target_address; - bytes32 target_address; - bool finalized; - uint32 contributors_count; -} - -interface ICrowdloan { - function getCrowdloan( - uint32 crowdloanId - ) external view returns (CrowdloanInfo memory); - function getContribution( - uint32 crowdloanId, - bytes32 coldkey - ) external view returns (uint64); - function create( - uint64 deposit, - uint64 minContribution, - uint64 cap, - uint32 end, - address targetAddress - ) external payable; -} - -struct LeaseInfo { - bytes32 beneficiary; - bytes32 coldkey; - bytes32 hotkey; - uint8 emissions_share; - bool has_end_block; - uint32 end_block; - uint16 netuid; - uint64 cost; -} - -interface ILeasing { - function getContributorShare( - uint32 leaseId, - bytes32 contributor - ) external view returns (uint128, uint128); - function createLeaseCrowdloan( - uint64 crowdloanDeposit, - uint64 crowdloanMinContribution, - uint64 crowdloanCap, - uint32 crowdloanEnd, - uint8 leasingEmissionsShare, - bool hasLeasingEndBlock, - uint32 leasingEndBlock - ) external payable; -} - -interface IProxy { - struct ProxyInfo { - bytes32 delegate; - uint256 proxy_type; - uint256 delay; - } - - function addProxy( - bytes32 delegate, - uint8 proxy_type, - uint32 delay - ) external; - function proxyCall( - bytes32 real, - uint8[] memory force_proxy_type, - uint8[] memory call - ) external; - function getProxies( - bytes32 account - ) external view returns (ProxyInfo[] memory); -} - -interface IAddressMapping { - function addressMapping( - address target_address - ) external view returns (bytes32); -} - -/** - * @title PrecompileWrapper - * @dev A wrapper contract that calls all precompile functions directly - * instead of using low-level calls like address.call() - */ -contract PrecompileWrapper { - ISubtensorBalanceTransfer public constant balanceTransfer = - ISubtensorBalanceTransfer(ISUBTENSOR_BALANCE_TRANSFER_ADDRESS); - IMetagraph public constant metagraph = IMetagraph(IMETAGRAPH_ADDRESS); - ISubnet public constant subnet = ISubnet(ISUBNET_ADDRESS); - INeuron public constant neuron = INeuron(INEURON_ADDRESS); - IStaking public constant staking = IStaking(ISTAKING_V2_ADDRESS); - IUidLookup public constant uidLookupPrecompile = - IUidLookup(IUID_LOOKUP_ADDRESS); - IAlpha public constant alpha = IAlpha(IALPHA_ADDRESS); - ICrowdloan public constant crowdloan = ICrowdloan(ICROWDLOAN_ADDRESS); - ILeasing public constant leasing = ILeasing(ILEASING_ADDRESS); - IProxy public constant proxy = IProxy(IPROXY_ADDRESS); - IAddressMapping public constant addressMappingPrecompile = - IAddressMapping(IADDRESS_MAPPING_ADDRESS); - - // ============ SubtensorBalanceTransfer Functions ============ - function transfer(bytes32 data) external payable { - balanceTransfer.transfer{value: msg.value}(data); - } - - // ============ Metagraph Functions ============ - - function getUidCount(uint16 netuid) external view returns (uint16) { - return metagraph.getUidCount(netuid); - } - - // ============ Subnet Functions ============ - - function registerNetworkWithDetails( - bytes32 hotkey, - string memory subnetName, - string memory githubRepo, - string memory subnetContact, - string memory subnetUrl, - string memory discord, - string memory description, - string memory additional - ) external payable { - subnet.registerNetwork( - hotkey, - subnetName, - githubRepo, - subnetContact, - subnetUrl, - discord, - description, - additional - ); - } - - function getServingRateLimit(uint16 netuid) external view returns (uint64) { - return subnet.getServingRateLimit(netuid); - } - - // ============ Neuron Functions ============ - - function burnedRegister(uint16 netuid, bytes32 hotkey) external payable { - neuron.burnedRegister{value: msg.value}(netuid, hotkey); - } - - // ============ Staking Functions ============ - function addStake( - bytes32 hotkey, - uint256 amount, - uint256 netuid - ) external payable { - staking.addStake(hotkey, amount, netuid); - } - - function removeStake( - bytes32 hotkey, - uint256 amount, - uint256 netuid - ) external payable { - staking.removeStake(hotkey, amount, netuid); - } - - function getTotalColdkeyStake( - bytes32 coldkey - ) external view returns (uint256) { - return staking.getTotalColdkeyStake(coldkey); - } - - function getTotalHotkeyStake( - bytes32 hotkey - ) external view returns (uint256) { - return staking.getTotalHotkeyStake(hotkey); - } - - // ============ Alpha Functions ============ - - function getAlphaPrice(uint16 netuid) external view returns (uint256) { - return alpha.getAlphaPrice(netuid); - } - - // ============ Address Mapping Functions ============ - - function addressMapping( - address target_address - ) external view returns (bytes32) { - return addressMappingPrecompile.addressMapping(target_address); - } - - // ============ Proxy Functions ============ - - function proxyCall( - bytes32 real, - uint8[] memory force_proxy_type, - uint8[] memory call - ) external { - proxy.proxyCall(real, force_proxy_type, call); - } - - function addProxy( - bytes32 delegate, - uint8 proxy_type, - uint32 delay - ) external { - proxy.addProxy(delegate, proxy_type, delay); - } - - function getProxies( - bytes32 account - ) external view returns (IProxy.ProxyInfo[] memory) { - return proxy.getProxies(account); - } - - // ============ UID Lookup Functions ============ - - function uidLookup( - uint16 netuid, - address evm_address, - uint16 limit - ) external view returns (LookupItem[] memory) { - return uidLookupPrecompile.uidLookup(netuid, evm_address, limit); - } - - // ============ Crowdloan Functions ============ - - function getCrowdloan( - uint32 crowdloanId - ) external view returns (CrowdloanInfo memory) { - return crowdloan.getCrowdloan(crowdloanId); - } - - function getContribution( - uint32 crowdloanId, - bytes32 coldkey - ) external view returns (uint64) { - return crowdloan.getContribution(crowdloanId, coldkey); - } - - function createCrowdloan( - uint64 deposit, - uint64 minContribution, - uint64 cap, - uint32 end, - address targetAddress - ) external payable { - crowdloan.create(deposit, minContribution, cap, end, targetAddress); - } - - // ============ Leasing Functions ============ - - function getContributorShare( - uint32 leaseId, - bytes32 contributor - ) external view returns (uint128, uint128) { - return leasing.getContributorShare(leaseId, contributor); - } - - function createLeaseCrowdloan( - uint64 crowdloanDeposit, - uint64 crowdloanMinContribution, - uint64 crowdloanCap, - uint32 crowdloanEnd, - uint8 leasingEmissionsShare, - bool hasLeasingEndBlock, - uint32 leasingEndBlock - ) external payable { - leasing.createLeaseCrowdloan( - crowdloanDeposit, - crowdloanMinContribution, - crowdloanCap, - crowdloanEnd, - leasingEmissionsShare, - hasLeasingEndBlock, - leasingEndBlock - ); - } -} diff --git a/contract-tests/src/contracts/precompileWrapper.ts b/contract-tests/src/contracts/precompileWrapper.ts deleted file mode 100644 index 9916b735e9..0000000000 --- a/contract-tests/src/contracts/precompileWrapper.ts +++ /dev/null @@ -1,714 +0,0 @@ -export const PRECOMPILE_WRAPPER_ABI = [ - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - } - ], - "name": "addProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "addStake", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target_address", - "type": "address" - } - ], - "name": "addressMapping", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "addressMappingPrecompile", - "outputs": [ - { - "internalType": "contract IAddressMapping", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "alpha", - "outputs": [ - { - "internalType": "contract IAlpha", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "balanceTransfer", - "outputs": [ - { - "internalType": "contract ISubtensorBalanceTransfer", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - } - ], - "name": "burnedRegister", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "deposit", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "minContribution", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "cap", - "type": "uint64" - }, - { - "internalType": "uint32", - "name": "end", - "type": "uint32" - }, - { - "internalType": "address", - "name": "targetAddress", - "type": "address" - } - ], - "name": "createCrowdloan", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "crowdloanDeposit", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "crowdloanMinContribution", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "crowdloanCap", - "type": "uint64" - }, - { - "internalType": "uint32", - "name": "crowdloanEnd", - "type": "uint32" - }, - { - "internalType": "uint8", - "name": "leasingEmissionsShare", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "hasLeasingEndBlock", - "type": "bool" - }, - { - "internalType": "uint32", - "name": "leasingEndBlock", - "type": "uint32" - } - ], - "name": "createLeaseCrowdloan", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "crowdloan", - "outputs": [ - { - "internalType": "contract ICrowdloan", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAlphaPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - }, - { - "internalType": "bytes32", - "name": "coldkey", - "type": "bytes32" - } - ], - "name": "getContribution", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "leaseId", - "type": "uint32" - }, - { - "internalType": "bytes32", - "name": "contributor", - "type": "bytes32" - } - ], - "name": "getContributorShare", - "outputs": [ - { - "internalType": "uint128", - "name": "", - "type": "uint128" - }, - { - "internalType": "uint128", - "name": "", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "crowdloanId", - "type": "uint32" - } - ], - "name": "getCrowdloan", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "creator", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "deposit", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "min_contribution", - "type": "uint64" - }, - { - "internalType": "uint32", - "name": "end", - "type": "uint32" - }, - { - "internalType": "uint64", - "name": "cap", - "type": "uint64" - }, - { - "internalType": "bytes32", - "name": "funds_account", - "type": "bytes32" - }, - { - "internalType": "uint64", - "name": "raised", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "has_target_address", - "type": "bool" - }, - { - "internalType": "bytes32", - "name": "target_address", - "type": "bytes32" - }, - { - "internalType": "bool", - "name": "finalized", - "type": "bool" - }, - { - "internalType": "uint32", - "name": "contributors_count", - "type": "uint32" - } - ], - "internalType": "struct CrowdloanInfo", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "account", - "type": "bytes32" - } - ], - "name": "getProxies", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "proxy_type", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "delay", - "type": "uint256" - } - ], - "internalType": "struct IProxy.ProxyInfo[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getServingRateLimit", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "coldkey", - "type": "bytes32" - } - ], - "name": "getTotalColdkeyStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - } - ], - "name": "getTotalHotkeyStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getUidCount", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "leasing", - "outputs": [ - { - "internalType": "contract ILeasing", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "metagraph", - "outputs": [ - { - "internalType": "contract IMetagraph", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "neuron", - "outputs": [ - { - "internalType": "contract INeuron", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxy", - "outputs": [ - { - "internalType": "contract IProxy", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "real", - "type": "bytes32" - }, - { - "internalType": "uint8[]", - "name": "force_proxy_type", - "type": "uint8[]" - }, - { - "internalType": "uint8[]", - "name": "call", - "type": "uint8[]" - } - ], - "name": "proxyCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "string", - "name": "subnetName", - "type": "string" - }, - { - "internalType": "string", - "name": "githubRepo", - "type": "string" - }, - { - "internalType": "string", - "name": "subnetContact", - "type": "string" - }, - { - "internalType": "string", - "name": "subnetUrl", - "type": "string" - }, - { - "internalType": "string", - "name": "discord", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "additional", - "type": "string" - } - ], - "name": "registerNetworkWithDetails", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "removeStake", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "staking", - "outputs": [ - { - "internalType": "contract IStaking", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "subnet", - "outputs": [ - { - "internalType": "contract ISubnet", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "data", - "type": "bytes32" - } - ], - "name": "transfer", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "evm_address", - "type": "address" - }, - { - "internalType": "uint16", - "name": "limit", - "type": "uint16" - } - ], - "name": "uidLookup", - "outputs": [ - { - "components": [ - { - "internalType": "uint16", - "name": "uid", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "block_associated", - "type": "uint64" - } - ], - "internalType": "struct LookupItem[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "uidLookupPrecompile", - "outputs": [ - { - "internalType": "contract IUidLookup", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - } -]; - -export const PRECOMPILE_WRAPPER_BYTECODE = "6080604052348015600e575f5ffd5b50612bb98061001c5f395ff3fe6080604052600436106101d6575f3560e01c80637d691e3011610101578063b1f789ef11610094578063d75e3e0d11610063578063d75e3e0d146106a9578063db1d0fd5146106d3578063ec556889146106fd578063fc6679fb14610727576101d6565b8063b1f789ef146105fd578063bfe252a214610639578063caf2ebf214610663578063cd6f4eb11461068d576101d6565b80639f246f6f116100d05780639f246f6f14610551578063a21762761461058d578063ac3166bf146105b7578063afed65f9146105e1576101d6565b80637d691e30146104815780638bba466c1461049d57806394e3ac6f146104d9578063998538c414610515576101d6565b80634c378a96116101795780635e25f3f8116101485780635e25f3f8146103d157806369e38bc3146103ed57806371214e27146104295780637444dadc14610445576101d6565b80634c378a96146103175780634cf088d9146103415780635b53ddde1461036b5780635b7210c514610395576101d6565b80631f193572116101b55780631f193572146102665780631fc9b141146102a25780633175bd98146102be5780634054ecca146102fb576101d6565b80620ae759146101da5780630494cd9a146102025780630cadeda51461023e575b5f5ffd5b3480156101e5575f5ffd5b5061020060048036038101906101fb91906113ab565b610751565b005b34801561020d575f5ffd5b506102286004803603810190610223919061148d565b6107c1565b60405161023591906114c7565b60405180910390f35b348015610249575f5ffd5b50610264600480360381019061025f9190611519565b610843565b005b348015610271575f5ffd5b5061028c600480360381019061028791906115a0565b6108b4565b60405161029991906115da565b60405180910390f35b6102bc60048036038101906102b79190611626565b610936565b005b3480156102c9575f5ffd5b506102e460048036038101906102df9190611676565b6109a7565b6040516102f29291906116de565b60405180910390f35b61031560048036038101906103109190611705565b610a2f565b005b348015610322575f5ffd5b5061032b610a9d565b604051610338919061179e565b60405180910390f35b34801561034c575f5ffd5b50610355610aa3565b60405161036291906117d7565b60405180910390f35b348015610376575f5ffd5b5061037f610aa9565b60405161038c9190611810565b60405180910390f35b3480156103a0575f5ffd5b506103bb60048036038101906103b69190611676565b610aaf565b6040516103c8919061184b565b60405180910390f35b6103eb60048036038101906103e69190611914565b610b34565b005b3480156103f8575f5ffd5b50610413600480360381019061040e91906115a0565b610bb4565b6040516104209190611a98565b60405180910390f35b610443600480360381019061043e9190611adb565b610c36565b005b348015610450575f5ffd5b5061046b600480360381019061046691906115a0565b610cad565b604051610478919061184b565b60405180910390f35b61049b60048036038101906104969190611626565b610d2f565b005b3480156104a8575f5ffd5b506104c360048036038101906104be9190611b52565b610da0565b6040516104d09190611ca3565b60405180910390f35b3480156104e4575f5ffd5b506104ff60048036038101906104fa9190611cbd565b610e2a565b60405161050c9190611ddf565b60405180910390f35b348015610520575f5ffd5b5061053b60048036038101906105369190611cbd565b610eb0565b6040516105489190611a98565b60405180910390f35b34801561055c575f5ffd5b5061057760048036038101906105729190611cbd565b610f32565b6040516105849190611a98565b60405180910390f35b348015610598575f5ffd5b506105a1610fb4565b6040516105ae9190611e1f565b60405180910390f35b3480156105c2575f5ffd5b506105cb610fba565b6040516105d89190611e58565b60405180910390f35b6105fb60048036038101906105f69190611e9b565b610fc0565b005b348015610608575f5ffd5b50610623600480360381019061061e9190611f38565b61103d565b604051610630919061206c565b60405180910390f35b348015610644575f5ffd5b5061064d6110c9565b60405161065a91906120ac565b60405180910390f35b34801561066e575f5ffd5b506106776110cf565b60405161068491906120e5565b60405180910390f35b6106a760048036038101906106a29190611cbd565b6110d5565b005b3480156106b4575f5ffd5b506106bd611142565b6040516106ca919061211e565b60405180910390f35b3480156106de575f5ffd5b506106e7611148565b6040516106f49190612157565b60405180910390f35b348015610708575f5ffd5b5061071161114e565b60405161071e9190612190565b60405180910390f35b348015610732575f5ffd5b5061073b611154565b60405161074891906121c9565b60405180910390f35b61080b73ffffffffffffffffffffffffffffffffffffffff16620ae7598484846040518463ffffffff1660e01b815260040161078f93929190612299565b5f604051808303815f87803b1580156107a6575f5ffd5b505af11580156107b8573d5f5f3e3d5ffd5b50505050505050565b5f61080c73ffffffffffffffffffffffffffffffffffffffff16630494cd9a836040518263ffffffff1660e01b81526004016107fd91906122eb565b602060405180830381865afa158015610818573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083c9190612318565b9050919050565b61080b73ffffffffffffffffffffffffffffffffffffffff16630cadeda58484846040518463ffffffff1660e01b815260040161088293929190612361565b5f604051808303815f87803b158015610899575f5ffd5b505af11580156108ab573d5f5f3e3d5ffd5b50505050505050565b5f61080273ffffffffffffffffffffffffffffffffffffffff16631f193572836040518263ffffffff1660e01b81526004016108f091906115da565b602060405180830381865afa15801561090b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061092f91906123aa565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16631fc9b1418484846040518463ffffffff1660e01b8152600401610975939291906123d5565b5f604051808303815f87803b15801561098c575f5ffd5b505af115801561099e573d5f5f3e3d5ffd5b50505050505050565b5f5f61080a73ffffffffffffffffffffffffffffffffffffffff16633175bd9885856040518363ffffffff1660e01b81526004016109e692919061240a565b6040805180830381865afa158015610a00573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a24919061245b565b915091509250929050565b61080473ffffffffffffffffffffffffffffffffffffffff16634054ecca83836040518363ffffffff1660e01b8152600401610a6c929190612499565b5f604051808303815f87803b158015610a83575f5ffd5b505af1158015610a95573d5f5f3e3d5ffd5b505050505050565b61080481565b61080581565b61080a81565b5f61080973ffffffffffffffffffffffffffffffffffffffff16635b7210c584846040518363ffffffff1660e01b8152600401610aed92919061240a565b602060405180830381865afa158015610b08573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b2c91906124d4565b905092915050565b61080373ffffffffffffffffffffffffffffffffffffffff16631cf98c6b89898989898989896040518963ffffffff1660e01b8152600401610b7d98979695949392919061255f565b5f604051808303815f87803b158015610b94575f5ffd5b505af1158015610ba6573d5f5f3e3d5ffd5b505050505050505050505050565b5f61080873ffffffffffffffffffffffffffffffffffffffff166369e38bc3836040518263ffffffff1660e01b8152600401610bf091906115da565b602060405180830381865afa158015610c0b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c2f9190612620565b9050919050565b61080973ffffffffffffffffffffffffffffffffffffffff1663127e1adb86868686866040518663ffffffff1660e01b8152600401610c7995949392919061264b565b5f604051808303815f87803b158015610c90575f5ffd5b505af1158015610ca2573d5f5f3e3d5ffd5b505050505050505050565b5f61080373ffffffffffffffffffffffffffffffffffffffff16637444dadc836040518263ffffffff1660e01b8152600401610ce991906115da565b602060405180830381865afa158015610d04573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d2891906124d4565b9050919050565b61080573ffffffffffffffffffffffffffffffffffffffff16637d691e308484846040518463ffffffff1660e01b8152600401610d6e939291906123d5565b5f604051808303815f87803b158015610d85575f5ffd5b505af1158015610d97573d5f5f3e3d5ffd5b50505050505050565b610da861115a565b61080973ffffffffffffffffffffffffffffffffffffffff16638bba466c836040518263ffffffff1660e01b8152600401610de3919061269c565b61016060405180830381865afa158015610dff573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e2391906127ea565b9050919050565b606061080b73ffffffffffffffffffffffffffffffffffffffff166394e3ac6f836040518263ffffffff1660e01b8152600401610e6791906114c7565b5f60405180830381865afa158015610e81573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f82011682018060405250810190610ea99190612937565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff1663998538c4836040518263ffffffff1660e01b8152600401610eec91906114c7565b602060405180830381865afa158015610f07573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f2b9190612620565b9050919050565b5f61080573ffffffffffffffffffffffffffffffffffffffff16639f246f6f836040518263ffffffff1660e01b8152600401610f6e91906114c7565b602060405180830381865afa158015610f89573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fad9190612620565b9050919050565b61080681565b61080c81565b61080a73ffffffffffffffffffffffffffffffffffffffff1663afed65f9888888888888886040518863ffffffff1660e01b8152600401611007979695949392919061298d565b5f604051808303815f87803b15801561101e575f5ffd5b505af1158015611030573d5f5f3e3d5ffd5b5050505050505050505050565b606061080673ffffffffffffffffffffffffffffffffffffffff1663b1f789ef8585856040518463ffffffff1660e01b815260040161107e939291906129fa565b5f60405180830381865afa158015611098573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f820116820180604052508101906110c09190612b3c565b90509392505050565b61080981565b61080381565b61080073ffffffffffffffffffffffffffffffffffffffff1663cd6f4eb134836040518363ffffffff1660e01b815260040161111191906114c7565b5f604051808303818588803b158015611128575f5ffd5b505af115801561113a573d5f5f3e3d5ffd5b505050505050565b61080081565b61080881565b61080b81565b61080281565b6040518061016001604052805f81526020015f67ffffffffffffffff1681526020015f67ffffffffffffffff1681526020015f63ffffffff1681526020015f67ffffffffffffffff1681526020015f81526020015f67ffffffffffffffff1681526020015f151581526020015f81526020015f151581526020015f63ffffffff1681525090565b5f604051905090565b5f5ffd5b5f5ffd5b5f819050919050565b611204816111f2565b811461120e575f5ffd5b50565b5f8135905061121f816111fb565b92915050565b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b61126f82611229565b810181811067ffffffffffffffff8211171561128e5761128d611239565b5b80604052505050565b5f6112a06111e1565b90506112ac8282611266565b919050565b5f67ffffffffffffffff8211156112cb576112ca611239565b5b602082029050602081019050919050565b5f5ffd5b5f60ff82169050919050565b6112f5816112e0565b81146112ff575f5ffd5b50565b5f81359050611310816112ec565b92915050565b5f611328611323846112b1565b611297565b9050808382526020820190506020840283018581111561134b5761134a6112dc565b5b835b8181101561137457806113608882611302565b84526020840193505060208101905061134d565b5050509392505050565b5f82601f83011261139257611391611225565b5b81356113a2848260208601611316565b91505092915050565b5f5f5f606084860312156113c2576113c16111ea565b5b5f6113cf86828701611211565b935050602084013567ffffffffffffffff8111156113f0576113ef6111ee565b5b6113fc8682870161137e565b925050604084013567ffffffffffffffff81111561141d5761141c6111ee565b5b6114298682870161137e565b9150509250925092565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61145c82611433565b9050919050565b61146c81611452565b8114611476575f5ffd5b50565b5f8135905061148781611463565b92915050565b5f602082840312156114a2576114a16111ea565b5b5f6114af84828501611479565b91505092915050565b6114c1816111f2565b82525050565b5f6020820190506114da5f8301846114b8565b92915050565b5f63ffffffff82169050919050565b6114f8816114e0565b8114611502575f5ffd5b50565b5f81359050611513816114ef565b92915050565b5f5f5f606084860312156115305761152f6111ea565b5b5f61153d86828701611211565b935050602061154e86828701611302565b925050604061155f86828701611505565b9150509250925092565b5f61ffff82169050919050565b61157f81611569565b8114611589575f5ffd5b50565b5f8135905061159a81611576565b92915050565b5f602082840312156115b5576115b46111ea565b5b5f6115c28482850161158c565b91505092915050565b6115d481611569565b82525050565b5f6020820190506115ed5f8301846115cb565b92915050565b5f819050919050565b611605816115f3565b811461160f575f5ffd5b50565b5f81359050611620816115fc565b92915050565b5f5f5f6060848603121561163d5761163c6111ea565b5b5f61164a86828701611211565b935050602061165b86828701611612565b925050604061166c86828701611612565b9150509250925092565b5f5f6040838503121561168c5761168b6111ea565b5b5f61169985828601611505565b92505060206116aa85828601611211565b9150509250929050565b5f6fffffffffffffffffffffffffffffffff82169050919050565b6116d8816116b4565b82525050565b5f6040820190506116f15f8301856116cf565b6116fe60208301846116cf565b9392505050565b5f5f6040838503121561171b5761171a6111ea565b5b5f6117288582860161158c565b925050602061173985828601611211565b9150509250929050565b5f819050919050565b5f61176661176161175c84611433565b611743565b611433565b9050919050565b5f6117778261174c565b9050919050565b5f6117888261176d565b9050919050565b6117988161177e565b82525050565b5f6020820190506117b15f83018461178f565b92915050565b5f6117c18261176d565b9050919050565b6117d1816117b7565b82525050565b5f6020820190506117ea5f8301846117c8565b92915050565b5f6117fa8261176d565b9050919050565b61180a816117f0565b82525050565b5f6020820190506118235f830184611801565b92915050565b5f67ffffffffffffffff82169050919050565b61184581611829565b82525050565b5f60208201905061185e5f83018461183c565b92915050565b5f5ffd5b5f67ffffffffffffffff82111561188257611881611239565b5b61188b82611229565b9050602081019050919050565b828183375f83830152505050565b5f6118b86118b384611868565b611297565b9050828152602081018484840111156118d4576118d3611864565b5b6118df848285611898565b509392505050565b5f82601f8301126118fb576118fa611225565b5b813561190b8482602086016118a6565b91505092915050565b5f5f5f5f5f5f5f5f610100898b031215611931576119306111ea565b5b5f61193e8b828c01611211565b985050602089013567ffffffffffffffff81111561195f5761195e6111ee565b5b61196b8b828c016118e7565b975050604089013567ffffffffffffffff81111561198c5761198b6111ee565b5b6119988b828c016118e7565b965050606089013567ffffffffffffffff8111156119b9576119b86111ee565b5b6119c58b828c016118e7565b955050608089013567ffffffffffffffff8111156119e6576119e56111ee565b5b6119f28b828c016118e7565b94505060a089013567ffffffffffffffff811115611a1357611a126111ee565b5b611a1f8b828c016118e7565b93505060c089013567ffffffffffffffff811115611a4057611a3f6111ee565b5b611a4c8b828c016118e7565b92505060e089013567ffffffffffffffff811115611a6d57611a6c6111ee565b5b611a798b828c016118e7565b9150509295985092959890939650565b611a92816115f3565b82525050565b5f602082019050611aab5f830184611a89565b92915050565b611aba81611829565b8114611ac4575f5ffd5b50565b5f81359050611ad581611ab1565b92915050565b5f5f5f5f5f60a08688031215611af457611af36111ea565b5b5f611b0188828901611ac7565b9550506020611b1288828901611ac7565b9450506040611b2388828901611ac7565b9350506060611b3488828901611505565b9250506080611b4588828901611479565b9150509295509295909350565b5f60208284031215611b6757611b666111ea565b5b5f611b7484828501611505565b91505092915050565b611b86816111f2565b82525050565b611b9581611829565b82525050565b611ba4816114e0565b82525050565b5f8115159050919050565b611bbe81611baa565b82525050565b61016082015f820151611bd95f850182611b7d565b506020820151611bec6020850182611b8c565b506040820151611bff6040850182611b8c565b506060820151611c126060850182611b9b565b506080820151611c256080850182611b8c565b5060a0820151611c3860a0850182611b7d565b5060c0820151611c4b60c0850182611b8c565b5060e0820151611c5e60e0850182611bb5565b50610100820151611c73610100850182611b7d565b50610120820151611c88610120850182611bb5565b50610140820151611c9d610140850182611b9b565b50505050565b5f61016082019050611cb75f830184611bc4565b92915050565b5f60208284031215611cd257611cd16111ea565b5b5f611cdf84828501611211565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611d1a816115f3565b82525050565b606082015f820151611d345f850182611b7d565b506020820151611d476020850182611d11565b506040820151611d5a6040850182611d11565b50505050565b5f611d6b8383611d20565b60608301905092915050565b5f602082019050919050565b5f611d8d82611ce8565b611d978185611cf2565b9350611da283611d02565b805f5b83811015611dd2578151611db98882611d60565b9750611dc483611d77565b925050600181019050611da5565b5085935050505092915050565b5f6020820190508181035f830152611df78184611d83565b905092915050565b5f611e098261176d565b9050919050565b611e1981611dff565b82525050565b5f602082019050611e325f830184611e10565b92915050565b5f611e428261176d565b9050919050565b611e5281611e38565b82525050565b5f602082019050611e6b5f830184611e49565b92915050565b611e7a81611baa565b8114611e84575f5ffd5b50565b5f81359050611e9581611e71565b92915050565b5f5f5f5f5f5f5f60e0888a031215611eb657611eb56111ea565b5b5f611ec38a828b01611ac7565b9750506020611ed48a828b01611ac7565b9650506040611ee58a828b01611ac7565b9550506060611ef68a828b01611505565b9450506080611f078a828b01611302565b93505060a0611f188a828b01611e87565b92505060c0611f298a828b01611505565b91505092959891949750929550565b5f5f5f60608486031215611f4f57611f4e6111ea565b5b5f611f5c8682870161158c565b9350506020611f6d86828701611479565b9250506040611f7e8682870161158c565b9150509250925092565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611fba81611569565b82525050565b604082015f820151611fd45f850182611fb1565b506020820151611fe76020850182611b8c565b50505050565b5f611ff88383611fc0565b60408301905092915050565b5f602082019050919050565b5f61201a82611f88565b6120248185611f92565b935061202f83611fa2565b805f5b8381101561205f5781516120468882611fed565b975061205183612004565b925050600181019050612032565b5085935050505092915050565b5f6020820190508181035f8301526120848184612010565b905092915050565b5f6120968261176d565b9050919050565b6120a68161208c565b82525050565b5f6020820190506120bf5f83018461209d565b92915050565b5f6120cf8261176d565b9050919050565b6120df816120c5565b82525050565b5f6020820190506120f85f8301846120d6565b92915050565b5f6121088261176d565b9050919050565b612118816120fe565b82525050565b5f6020820190506121315f83018461210f565b92915050565b5f6121418261176d565b9050919050565b61215181612137565b82525050565b5f60208201905061216a5f830184612148565b92915050565b5f61217a8261176d565b9050919050565b61218a81612170565b82525050565b5f6020820190506121a35f830184612181565b92915050565b5f6121b38261176d565b9050919050565b6121c3816121a9565b82525050565b5f6020820190506121dc5f8301846121ba565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b612214816112e0565b82525050565b5f612225838361220b565b60208301905092915050565b5f602082019050919050565b5f612247826121e2565b61225181856121ec565b935061225c836121fc565b805f5b8381101561228c578151612273888261221a565b975061227e83612231565b92505060018101905061225f565b5085935050505092915050565b5f6060820190506122ac5f8301866114b8565b81810360208301526122be818561223d565b905081810360408301526122d2818461223d565b9050949350505050565b6122e581611452565b82525050565b5f6020820190506122fe5f8301846122dc565b92915050565b5f81519050612312816111fb565b92915050565b5f6020828403121561232d5761232c6111ea565b5b5f61233a84828501612304565b91505092915050565b61234c816112e0565b82525050565b61235b816114e0565b82525050565b5f6060820190506123745f8301866114b8565b6123816020830185612343565b61238e6040830184612352565b949350505050565b5f815190506123a481611576565b92915050565b5f602082840312156123bf576123be6111ea565b5b5f6123cc84828501612396565b91505092915050565b5f6060820190506123e85f8301866114b8565b6123f56020830185611a89565b6124026040830184611a89565b949350505050565b5f60408201905061241d5f830185612352565b61242a60208301846114b8565b9392505050565b61243a816116b4565b8114612444575f5ffd5b50565b5f8151905061245581612431565b92915050565b5f5f60408385031215612471576124706111ea565b5b5f61247e85828601612447565b925050602061248f85828601612447565b9150509250929050565b5f6040820190506124ac5f8301856115cb565b6124b960208301846114b8565b9392505050565b5f815190506124ce81611ab1565b92915050565b5f602082840312156124e9576124e86111ea565b5b5f6124f6848285016124c0565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f612531826124ff565b61253b8185612509565b935061254b818560208601612519565b61255481611229565b840191505092915050565b5f610100820190506125735f83018b6114b8565b8181036020830152612585818a612527565b905081810360408301526125998189612527565b905081810360608301526125ad8188612527565b905081810360808301526125c18187612527565b905081810360a08301526125d58186612527565b905081810360c08301526125e98185612527565b905081810360e08301526125fd8184612527565b90509998505050505050505050565b5f8151905061261a816115fc565b92915050565b5f60208284031215612635576126346111ea565b5b5f6126428482850161260c565b91505092915050565b5f60a08201905061265e5f83018861183c565b61266b602083018761183c565b612678604083018661183c565b6126856060830185612352565b61269260808301846122dc565b9695505050505050565b5f6020820190506126af5f830184612352565b92915050565b5f5ffd5b5f815190506126c7816114ef565b92915050565b5f815190506126db81611e71565b92915050565b5f61016082840312156126f7576126f66126b5565b5b612702610160611297565b90505f61271184828501612304565b5f830152506020612724848285016124c0565b6020830152506040612738848285016124c0565b604083015250606061274c848285016126b9565b6060830152506080612760848285016124c0565b60808301525060a061277484828501612304565b60a08301525060c0612788848285016124c0565b60c08301525060e061279c848285016126cd565b60e0830152506101006127b184828501612304565b610100830152506101206127c7848285016126cd565b610120830152506101406127dd848285016126b9565b6101408301525092915050565b5f6101608284031215612800576127ff6111ea565b5b5f61280d848285016126e1565b91505092915050565b5f67ffffffffffffffff8211156128305761282f611239565b5b602082029050602081019050919050565b5f60608284031215612856576128556126b5565b5b6128606060611297565b90505f61286f84828501612304565b5f8301525060206128828482850161260c565b60208301525060406128968482850161260c565b60408301525092915050565b5f6128b46128af84612816565b611297565b905080838252602082019050606084028301858111156128d7576128d66112dc565b5b835b8181101561290057806128ec8882612841565b8452602084019350506060810190506128d9565b5050509392505050565b5f82601f83011261291e5761291d611225565b5b815161292e8482602086016128a2565b91505092915050565b5f6020828403121561294c5761294b6111ea565b5b5f82015167ffffffffffffffff811115612969576129686111ee565b5b6129758482850161290a565b91505092915050565b61298781611baa565b82525050565b5f60e0820190506129a05f83018a61183c565b6129ad602083018961183c565b6129ba604083018861183c565b6129c76060830187612352565b6129d46080830186612343565b6129e160a083018561297e565b6129ee60c0830184612352565b98975050505050505050565b5f606082019050612a0d5f8301866115cb565b612a1a60208301856122dc565b612a2760408301846115cb565b949350505050565b5f67ffffffffffffffff821115612a4957612a48611239565b5b602082029050602081019050919050565b5f60408284031215612a6f57612a6e6126b5565b5b612a796040611297565b90505f612a8884828501612396565b5f830152506020612a9b848285016124c0565b60208301525092915050565b5f612ab9612ab484612a2f565b611297565b90508083825260208201905060408402830185811115612adc57612adb6112dc565b5b835b81811015612b055780612af18882612a5a565b845260208401935050604081019050612ade565b5050509392505050565b5f82601f830112612b2357612b22611225565b5b8151612b33848260208601612aa7565b91505092915050565b5f60208284031215612b5157612b506111ea565b5b5f82015167ffffffffffffffff811115612b6e57612b6d6111ee565b5b612b7a84828501612b0f565b9150509291505056fea2646970667358221220768c64014d2253c661e44d07f480f7a203eb9e422f680d00272498325a4f6ad964736f6c634300081e0033"; diff --git a/contract-tests/src/contracts/proxy.ts b/contract-tests/src/contracts/proxy.ts deleted file mode 100644 index 1a3eab3594..0000000000 --- a/contract-tests/src/contracts/proxy.ts +++ /dev/null @@ -1,184 +0,0 @@ -export const IPROXY_ADDRESS = "0x000000000000000000000000000000000000080b"; - -export const IProxyABI = [ - { - "inputs": [ - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - }, - { - "internalType": "uint16", - "name": "index", - "type": "uint16" - } - ], - "name": "createPureProxy", - "outputs": [ - { - "internalType": "bytes32", - "name": "proxy", - "type": "bytes32" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "spawner", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "index", - "type": "uint16" - }, - { - "internalType": "uint32", - "name": "height", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "ext_index", - "type": "uint32" - } - ], - "name": "killPureProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "real", - "type": "bytes32" - }, - { - "internalType": "uint8[]", - "name": "force_proxy_type", // optional - "type": "uint8[]" - }, - { - "internalType": "uint8[]", - "name": "call", - "type": "uint8[]" - } - ], - "name": "proxyCall", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "removeProxies", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { - "inputs": [], - "name": "pokeDeposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - } - ], - "name": "removeProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint8", - "name": "proxy_type", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "delay", - "type": "uint32" - } - ], - "name": "addProxy", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "account", - "type": "bytes32" - } - ], - "name": "getProxies", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "proxy_type", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "delay", - "type": "uint256" - } - ], - "internalType": "struct IProxy.ProxyInfo[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - } -]; diff --git a/contract-tests/src/contracts/stakeWrap.sol b/contract-tests/src/contracts/stakeWrap.sol deleted file mode 100644 index c7d11d42a0..0000000000 --- a/contract-tests/src/contracts/stakeWrap.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -// need use the compiler version 0.8.20 for this contract, otherwise there is an issue -// opcode(94) swap5 not supported. -pragma solidity >=0.8.0 <0.8.2; - -address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000805; - -interface Staking { - function addStakeLimit( - bytes32 hotkey, - uint256 amount, - uint256 limit_price, - bool allow_partial, - uint256 netuid - ) external; - - function addStake(bytes32 hotkey, uint256 amount, uint256 netuid) external; - - function removeStake( - bytes32 hotkey, - uint256 amount, - uint256 netuid - ) external; -} - -contract StakeWrap { - address public owner; - constructor() { - owner = msg.sender; - } - - modifier onlyOwner() { - require(msg.sender == owner, "Only owner can call this function"); - _; - } - - receive() external payable {} - - function stake( - bytes32 hotkey, - uint256 netuid, - uint256 amount - ) external onlyOwner { - // can't call precompile like this way, the call never go to runtime precompile - //Staking(ISTAKING_ADDRESS).addStake(hotkey, amount, netuid); - - bytes memory data = abi.encodeWithSelector( - Staking.addStake.selector, - hotkey, - amount, - netuid - ); - (bool success, ) = ISTAKING_ADDRESS.call{gas: gasleft()}(data); - require(success, "addStake call failed"); - } - - function stakeLimit( - bytes32 hotkey, - uint256 netuid, - uint256 limitPrice, - uint256 amount, - bool allowPartial - ) external onlyOwner { - // can't call precompile like this way, the call never go to runtime precompile - // Staking(ISTAKING_ADDRESS).addStakeLimit( - // hotkey, - // amount, - // limitPrice, - // allowPartial, - // netuid - // ); - - bytes memory data = abi.encodeWithSelector( - Staking.addStakeLimit.selector, - hotkey, - amount, - limitPrice, - allowPartial, - netuid - ); - (bool success, ) = ISTAKING_ADDRESS.call{gas: gasleft()}(data); - require(success, "addStakeLimit call failed"); - } - - function removeStake( - bytes32 hotkey, - uint256 netuid, - uint256 amount - ) external onlyOwner { - bytes memory data = abi.encodeWithSelector( - Staking.removeStake.selector, - hotkey, - amount, - netuid - ); - (bool success, ) = ISTAKING_ADDRESS.call{gas: gasleft()}(data); - require(success, "addStake call failed"); - } -} diff --git a/contract-tests/src/contracts/stakeWrap.ts b/contract-tests/src/contracts/stakeWrap.ts deleted file mode 100644 index 07853470a1..0000000000 --- a/contract-tests/src/contracts/stakeWrap.ts +++ /dev/null @@ -1,106 +0,0 @@ -export const abi = [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "removeStake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "stake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limitPrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "allowPartial", - "type": "bool" - } - ], - "name": "stakeLimit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -]; - -// compiled with 0.8.20 -export const bytecode = "6080604052348015600e575f5ffd5b50335f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610ad08061005b5f395ff3fe608060405260043610610042575f3560e01c80632daedd521461004d5780637d691e30146100755780638da5cb5b1461009d57806390b9d534146100c757610049565b3661004957005b5f5ffd5b348015610058575f5ffd5b50610073600480360381019061006e91906106bd565b6100ef565b005b348015610080575f5ffd5b5061009b600480360381019061009691906106bd565b6102ad565b005b3480156100a8575f5ffd5b506100b161046b565b6040516100be919061074c565b60405180910390f35b3480156100d2575f5ffd5b506100ed60048036038101906100e8919061079a565b61048f565b005b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461017d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161017490610891565b60405180910390fd5b5f631fc9b14160e01b84838560405160240161019b939291906108cd565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516102239190610954565b5f604051808303815f8787f1925050503d805f811461025d576040519150601f19603f3d011682016040523d82523d5f602084013e610262565b606091505b50509050806102a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161029d906109b4565b60405180910390fd5b5050505050565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461033b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161033290610891565b60405180910390fd5b5f637d691e3060e01b848385604051602401610359939291906108cd565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516103e19190610954565b5f604051808303815f8787f1925050503d805f811461041b576040519150601f19603f3d011682016040523d82523d5f602084013e610420565b606091505b5050905080610464576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045b906109b4565b60405180910390fd5b5050505050565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461051d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161051490610891565b60405180910390fd5b5f635beb6b7460e01b868486858960405160240161053f9594939291906109e1565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516105c79190610954565b5f604051808303815f8787f1925050503d805f8114610601576040519150601f19603f3d011682016040523d82523d5f602084013e610606565b606091505b505090508061064a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064190610a7c565b60405180910390fd5b50505050505050565b5f5ffd5b5f819050919050565b61066981610657565b8114610673575f5ffd5b50565b5f8135905061068481610660565b92915050565b5f819050919050565b61069c8161068a565b81146106a6575f5ffd5b50565b5f813590506106b781610693565b92915050565b5f5f5f606084860312156106d4576106d3610653565b5b5f6106e186828701610676565b93505060206106f2868287016106a9565b9250506040610703868287016106a9565b9150509250925092565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6107368261070d565b9050919050565b6107468161072c565b82525050565b5f60208201905061075f5f83018461073d565b92915050565b5f8115159050919050565b61077981610765565b8114610783575f5ffd5b50565b5f8135905061079481610770565b92915050565b5f5f5f5f5f60a086880312156107b3576107b2610653565b5b5f6107c088828901610676565b95505060206107d1888289016106a9565b94505060406107e2888289016106a9565b93505060606107f3888289016106a9565b925050608061080488828901610786565b9150509295509295909350565b5f82825260208201905092915050565b7f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f5f8201527f6e00000000000000000000000000000000000000000000000000000000000000602082015250565b5f61087b602183610811565b915061088682610821565b604082019050919050565b5f6020820190508181035f8301526108a88161086f565b9050919050565b6108b881610657565b82525050565b6108c78161068a565b82525050565b5f6060820190506108e05f8301866108af565b6108ed60208301856108be565b6108fa60408301846108be565b949350505050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f61092e82610902565b610938818561090c565b9350610948818560208601610916565b80840191505092915050565b5f61095f8284610924565b915081905092915050565b7f6164645374616b652063616c6c206661696c65640000000000000000000000005f82015250565b5f61099e601483610811565b91506109a98261096a565b602082019050919050565b5f6020820190508181035f8301526109cb81610992565b9050919050565b6109db81610765565b82525050565b5f60a0820190506109f45f8301886108af565b610a0160208301876108be565b610a0e60408301866108be565b610a1b60608301856109d2565b610a2860808301846108be565b9695505050505050565b7f6164645374616b654c696d69742063616c6c206661696c6564000000000000005f82015250565b5f610a66601983610811565b9150610a7182610a32565b602082019050919050565b5f6020820190508181035f830152610a9381610a5a565b905091905056fea2646970667358221220f8ad692d7919fb10f08e5311c64a0aa705a4e665689967633c2ddade5398076664736f6c634300081e0033" diff --git a/contract-tests/src/contracts/staking.ts b/contract-tests/src/contracts/staking.ts deleted file mode 100644 index ad21b31be8..0000000000 --- a/contract-tests/src/contracts/staking.ts +++ /dev/null @@ -1,594 +0,0 @@ -export const ISTAKING_ADDRESS = "0x0000000000000000000000000000000000000801"; -export const ISTAKING_V2_ADDRESS = "0x0000000000000000000000000000000000000805"; - -export const IStakingABI = [ - { - inputs: [ - { - internalType: "bytes32", - name: "delegate", - type: "bytes32", - }, - ], - name: "addProxy", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "hotkey", - type: "bytes32", - }, - { - internalType: "uint256", - name: "netuid", - type: "uint256", - }, - ], - name: "addStake", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "delegate", - type: "bytes32", - }, - ], - name: "removeProxy", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "hotkey", - type: "bytes32", - }, - { - internalType: "bytes32", - name: "coldkey", - type: "bytes32", - }, - { - internalType: "uint256", - name: "netuid", - type: "uint256", - }, - ], - name: "getStake", - outputs: [ - { - internalType: "uint256", - name: "", - type: "uint256", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "hotkey", - type: "bytes32", - }, - { - internalType: "uint256", - name: "amount", - type: "uint256", - }, - { - internalType: "uint256", - name: "netuid", - type: "uint256", - }, - ], - name: "removeStake", - outputs: [], - stateMutability: "payable", - type: "function", - }, -]; - -export const IStakingV2ABI = [ - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - } - ], - "name": "addProxy", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "addStake", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "getAlphaStakedValidators", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "coldkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "getStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "getTotalAlphaStaked", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "coldkey", - "type": "bytes32" - } - ], - "name": "getTotalColdkeyStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "coldkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "getTotalColdkeyStakeOnSubnet", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - } - ], - "name": "getTotalHotkeyStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getNominatorMinRequiredStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "delegate", - "type": "bytes32" - } - ], - "name": "removeProxy", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "removeStake", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit_price", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "allow_partial", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "addStakeLimit", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit_price", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "allow_partial", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "removeStakeLimit", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - ], - "name": "removeStakeFull", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "limit_price", - "type": "uint256" - } - ], - "name": "removeStakeFullLimit", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "burnAlpha", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spenderAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "absoluteAmount", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [], - "stateMutability": "", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sourceAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "spenderAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spenderAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "increaseAmount", - "type": "uint256" - } - ], - "name": "increaseAllowance", - "outputs": [], - "stateMutability": "", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spenderAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "netuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "decreaseAmount", - "type": "uint256" - } - ], - "name": "decreaseAllowance", - "outputs": [], - "stateMutability": "", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sourceAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "destinationAddress", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "originNetuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "destinationNetuid", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferStakeFrom", - "outputs": [], - "stateMutability": "", - "type": "function" - } -]; diff --git a/contract-tests/src/contracts/subnet.ts b/contract-tests/src/contracts/subnet.ts deleted file mode 100644 index a55bd5030f..0000000000 --- a/contract-tests/src/contracts/subnet.ts +++ /dev/null @@ -1,961 +0,0 @@ -export const ISUBNET_ADDRESS = "0x0000000000000000000000000000000000000803"; - -export const ISubnetABI = [ - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getAdjustmentAlpha", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getAlphaValues", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getBondsMovingAverage", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getCommitRevealWeightsEnabled", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getDifficulty", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - name: "getImmunityPeriod", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - name: "getKappa", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getMaxBurn", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getMaxDifficulty", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getMaxWeightLimit", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getMinAllowedWeights", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getMinBurn", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getMinDifficulty", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getNetworkRegistrationAllowed", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - name: "getRho", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getServingRateLimit", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getWeightsSetRateLimit", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getWeightsVersionKey", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "activityCutoff", - type: "uint16", - }, - ], - name: "setActivityCutoff", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getActivityCutoff", - outputs: [ - { - internalType: "uint16", - name: "", - type: "uint16", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "adjustmentAlpha", - type: "uint64", - }, - ], - name: "setAdjustmentAlpha", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "alphaLow", - type: "uint16", - }, - { - internalType: "uint16", - name: "alphaHigh", - type: "uint16", - }, - ], - name: "setAlphaValues", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "bondsMovingAverage", - type: "uint64", - }, - ], - name: "setBondsMovingAverage", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "bool", - name: "commitRevealWeightsEnabled", - type: "bool", - }, - ], - name: "setCommitRevealWeightsEnabled", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getCommitRevealWeightsInterval", - outputs: [ - { - internalType: "uint64", - name: "", - type: "uint64", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "commitRevealWeightsInterval", - type: "uint64", - }, - ], - name: "setCommitRevealWeightsInterval", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "difficulty", - type: "uint64", - }, - ], - name: "setDifficulty", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "immunityPeriod", - type: "uint16", - }, - ], - name: "setImmunityPeriod", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "kappa", - type: "uint16", - }, - ], - name: "setKappa", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getLiquidAlphaEnabled", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getYuma3Enabled", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "bool", - name: "yuma3Enabled", - type: "bool", - }, - ], - name: "setYuma3Enabled", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "bool", - name: "liquidAlphaEnabled", - type: "bool", - }, - ], - name: "setLiquidAlphaEnabled", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "maxBurn", - type: "uint64", - }, - ], - name: "setMaxBurn", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "maxDifficulty", - type: "uint64", - }, - ], - name: "setMaxDifficulty", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "minAllowedWeights", - type: "uint16", - }, - ], - name: "setMinAllowedWeights", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "minBurn", - type: "uint64", - }, - ], - name: "setMinBurn", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "minDifficulty", - type: "uint64", - }, - ], - name: "setMinDifficulty", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - ], - name: "getNetworkPowRegistrationAllowed", - outputs: [ - { - internalType: "bool", - name: "", - type: "bool", - }, - ], - stateMutability: "view", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "bool", - name: "networkPowRegistrationAllowed", - type: "bool", - }, - ], - name: "setNetworkPowRegistrationAllowed", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "bool", - name: "networkRegistrationAllowed", - type: "bool", - }, - ], - name: "setNetworkRegistrationAllowed", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint16", - name: "rho", - type: "uint16", - }, - ], - name: "setRho", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "servingRateLimit", - type: "uint64", - }, - ], - name: "setServingRateLimit", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "weightsSetRateLimit", - type: "uint64", - }, - ], - name: "setWeightsSetRateLimit", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16", - }, - { - internalType: "uint64", - name: "weightsVersionKey", - type: "uint64", - }, - ], - name: "setWeightsVersionKey", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "hotkey", - type: "bytes32", - }, - ], - name: "registerNetwork", - outputs: [], - stateMutability: "payable", - type: "function", - }, - { - inputs: [ - { - internalType: "bytes32", - name: "hotkey", - type: "bytes32" - }, - { - internalType: "string", - name: "subnetName", - type: "string" - }, - { - internalType: "string", - name: "githubRepo", - type: "string" - }, - { - internalType: "string", - name: "subnetContact", - type: "string" - }, - { - internalType: "string", - name: "subnetUrl", - type: "string" - }, - { - internalType: "string", - name: "discord", - type: "string" - }, - { - internalType: "string", - name: "description", - type: "string" - }, - { - internalType: "string", - name: "additional", - type: "string" - } - ], - name: "registerNetwork", - outputs: [], - stateMutability: "payable", - type: "function" - }, - { - inputs: [ - { - internalType: "bytes32", - name: "hotkey", - type: "bytes32" - }, - { - internalType: "string", - name: "subnetName", - type: "string" - }, - { - internalType: "string", - name: "githubRepo", - type: "string" - }, - { - internalType: "string", - name: "subnetContact", - type: "string" - }, - { - internalType: "string", - name: "subnetUrl", - type: "string" - }, - { - internalType: "string", - name: "discord", - type: "string" - }, - { - internalType: "string", - name: "description", - type: "string" - }, - { - internalType: "string", - name: "logoUrl", - type: "string" - }, - { - internalType: "string", - name: "additional", - type: "string" - } - ], - name: "registerNetwork", - outputs: [], - stateMutability: "payable", - type: "function" - }, -]; diff --git a/contract-tests/src/contracts/uidLookup.ts b/contract-tests/src/contracts/uidLookup.ts deleted file mode 100644 index 06c68805e6..0000000000 --- a/contract-tests/src/contracts/uidLookup.ts +++ /dev/null @@ -1,45 +0,0 @@ -export const IUID_LOOKUP_ADDRESS = "0x0000000000000000000000000000000000000806"; - -export const IUIDLookupABI = [ - { - inputs: [ - { - internalType: "uint16", - name: "netuid", - type: "uint16" - }, - { - internalType: "address", - name: "evm_address", - type: "address" - }, - { - internalType: "uint16", - name: "limit", - type: "uint16" - } - ], - name: "uidLookup", - outputs: [ - { - components: [ - { - internalType: "uint16", - name: "uid", - type: "uint16" - }, - { - internalType: "uint64", - name: "block_associated", - type: "uint64" - } - ], - internalType: "struct LookupItem[]", - name: "", - type: "tuple[]" - } - ], - stateMutability: "view", - type: "function" - } -]; diff --git a/contract-tests/src/contracts/votingPower.ts b/contract-tests/src/contracts/votingPower.ts deleted file mode 100644 index bbcc3ca6e6..0000000000 --- a/contract-tests/src/contracts/votingPower.ts +++ /dev/null @@ -1,104 +0,0 @@ -export const IVOTING_POWER_ADDRESS = "0x000000000000000000000000000000000000080d"; - -export const IVotingPowerABI = [ - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "bytes32", - "name": "hotkey", - "type": "bytes32" - } - ], - "name": "getVotingPower", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "isVotingPowerTrackingEnabled", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getVotingPowerDisableAtBlock", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getVotingPowerEmaAlpha", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getTotalVotingPower", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/contract-tests/src/contracts/withdraw.sol b/contract-tests/src/contracts/withdraw.sol deleted file mode 100644 index 3945661e09..0000000000 --- a/contract-tests/src/contracts/withdraw.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity >=0.7.0 <0.9.0; - -contract Withdraw { - constructor() {} - - function withdraw(uint256 value) public payable { - payable(msg.sender).transfer(value); - } - - receive() external payable {} -} diff --git a/contract-tests/src/contracts/withdraw.ts b/contract-tests/src/contracts/withdraw.ts deleted file mode 100644 index 46fe66bf24..0000000000 --- a/contract-tests/src/contracts/withdraw.ts +++ /dev/null @@ -1,31 +0,0 @@ -export const WITHDRAW_CONTRACT_ABI = [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "withdraw", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } -]; - -// "compiler": { -// "version": "0.8.26+commit.8a97fa7a" -// }, - -export const WITHDRAW_CONTRACT_BYTECODE = "6080604052348015600e575f80fd5b506101148061001c5f395ff3fe608060405260043610601e575f3560e01c80632e1a7d4d146028576024565b36602457005b5f80fd5b603e6004803603810190603a919060b8565b6040565b005b3373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050501580156082573d5f803e3d5ffd5b5050565b5f80fd5b5f819050919050565b609a81608a565b811460a3575f80fd5b50565b5f8135905060b2816093565b92915050565b5f6020828403121560ca5760c96086565b5b5f60d58482850160a6565b9150509291505056fea2646970667358221220f43400858bfe4fcc0bf3c1e2e06d3a9e6ced86454a00bd7e4866b3d4d64e46bb64736f6c634300081a0033" - diff --git a/contract-tests/src/eth.ts b/contract-tests/src/eth.ts deleted file mode 100644 index a34e33bc2d..0000000000 --- a/contract-tests/src/eth.ts +++ /dev/null @@ -1,16 +0,0 @@ - -import { ethers, Provider, TransactionRequest, Wallet } from "ethers"; -export async function estimateTransactionCost(provider: Provider, tx: TransactionRequest) { - const feeData = await provider.getFeeData(); - const estimatedGas = BigInt(await provider.estimateGas(tx)); - const gasPrice = feeData.gasPrice || feeData.maxFeePerGas; - if (gasPrice === null) - return estimatedGas - else - return estimatedGas * BigInt(gasPrice); -} - -export function getContract(contractAddress: string, abi: {}[], wallet: Wallet) { - const contract = new ethers.Contract(contractAddress, abi, wallet); - return contract -} diff --git a/contract-tests/src/setup.ts b/contract-tests/src/setup.ts deleted file mode 100644 index 1ef872cd5a..0000000000 --- a/contract-tests/src/setup.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import { createClient, TypedApi, PolkadotClient, Binary } from 'polkadot-api'; -import { SUB_LOCAL_URL } from "./config" -import { getWsProvider } from 'polkadot-api/ws-provider/web'; - -let client: PolkadotClient | undefined = undefined - -export async function getClient() { - if (client === undefined) { - const provider = getWsProvider(SUB_LOCAL_URL); - client = createClient(provider); - } - return client; -} - -after(() => { - client?.destroy() -}); - diff --git a/contract-tests/src/substrate.ts b/contract-tests/src/substrate.ts deleted file mode 100644 index c7c9efe3d9..0000000000 --- a/contract-tests/src/substrate.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { devnet, MultiAddress } from '@polkadot-api/descriptors'; -import { TypedApi, Transaction, PolkadotSigner, Binary } from 'polkadot-api'; -import { sr25519CreateDerive } from "@polkadot-labs/hdkd" -import { DEV_PHRASE, entropyToMiniSecret, mnemonicToEntropy, KeyPair } from "@polkadot-labs/hdkd-helpers" -import { getPolkadotSigner } from "polkadot-api/signer" -import { randomBytes } from 'crypto'; -import { Keyring } from '@polkadot/keyring'; -import { SS58_PREFIX, TX_TIMEOUT } from "./config"; -import { getClient } from "./setup" - -let api: TypedApi | undefined = undefined - -// define url string as type to extend in the future -// export type ClientUrlType = 'ws://localhost:9944' | 'wss://test.finney.opentensor.ai:443' | 'wss://dev.chain.opentensor.ai:443' | 'wss://archive.chain.opentensor.ai'; -export type ClientUrlType = 'ws://localhost:9944' - -export async function getDevnetApi() { - if (api === undefined) { - let client = await getClient() - - api = client.getTypedApi(devnet) - } - return api -} - -export function getKeypairFromPath(path: string) { - const entropy = mnemonicToEntropy(DEV_PHRASE) - const miniSecret = entropyToMiniSecret(entropy) - const derive = sr25519CreateDerive(miniSecret) - const hdkdKeyPair = derive(path) - - return hdkdKeyPair -} - -export const getAlice = () => getKeypairFromPath("//Alice") -export const getBob = () => getKeypairFromPath("//Bob") -export const getCharlie = () => getKeypairFromPath("//Charlie") -export const getDave = () => getKeypairFromPath("//Dave") - -export function getSignerFromPath(path: string) { - const keypair = getKeypairFromPath(path) - const polkadotSigner = getPolkadotSigner( - keypair.publicKey, - "Sr25519", - keypair.sign, - ) - - return polkadotSigner -} - -export const getAliceSigner = () => getSignerFromPath("//Alice") -export const getBobSigner = () => getSignerFromPath("//Bob") -export const getCharlieSigner = () => getSignerFromPath("//Charlie") -export const getDaveSigner = () => getSignerFromPath("//Dave") - -export function getRandomSubstrateSigner() { - const keypair = getRandomSubstrateKeypair(); - return getSignerFromKeypair(keypair) -} - -export function getSignerFromKeypair(keypair: KeyPair) { - const polkadotSigner = getPolkadotSigner( - keypair.publicKey, - "Sr25519", - keypair.sign, - ) - return polkadotSigner -} - -export function getRandomSubstrateKeypair() { - const seed = randomBytes(32); - const miniSecret = entropyToMiniSecret(seed) - const derive = sr25519CreateDerive(miniSecret) - const hdkdKeyPair = derive("") - - return hdkdKeyPair -} - -export async function getBalance(api: TypedApi, ss58Address: string) { - const value = await api.query.System.Account.getValue(ss58Address) - return value.data.free -} - -export async function getNonce(api: TypedApi, ss58Address: string): Promise { - const value = await api.query.System.Account.getValue(ss58Address); - return value.nonce -} - -export async function getNonceChangePromise(api: TypedApi, ss58Address: string) { - // api.query.System.Account.getValue() - const initValue = await api.query.System.Account.getValue(ss58Address); - return new Promise((resolve, reject) => { - const subscription = api.query.System.Account.watchValue(ss58Address).subscribe({ - next(value) { - if (value.nonce > initValue.nonce) { - subscription.unsubscribe(); - // Resolve the promise when the transaction is finalized - resolve(); - } - }, - - error(err: Error) { - console.error("Transaction failed:", err); - subscription.unsubscribe(); - // Reject the promise in case of an error - reject(err); - }, - complete() { - console.log("Subscription complete"); - } - }) - - setTimeout(() => { - subscription.unsubscribe(); - console.log('unsubscribed!'); - resolve() - }, TX_TIMEOUT); - - }) -} - -export function convertPublicKeyToMultiAddress(publicKey: Uint8Array, ss58Format: number = SS58_PREFIX): MultiAddress { - // Create a keyring instance - const keyring = new Keyring({ type: 'sr25519', ss58Format }); - - // Add the public key to the keyring - const address = keyring.encodeAddress(publicKey); - - return MultiAddress.Id(address); -} - -export async function waitForTransactionWithRetry( - api: TypedApi, - tx: Transaction<{}, string, string, void>, - signer: PolkadotSigner, -) { - let success = false; - let retries = 0; - - // set max retries times - while (!success && retries < 5) { - await waitForTransactionCompletion(api, tx, signer) - .then(() => { success = true }) - .catch((error) => { - console.log(`transaction error ${error}`); - }); - await new Promise((resolve) => setTimeout(resolve, 1000)); - retries += 1; - } - - if (!success) { - console.log("Transaction failed after 5 retries"); - } -} - -export async function waitForTransactionCompletion(api: TypedApi, tx: Transaction<{}, string, string, void>, signer: PolkadotSigner,) { - const transactionPromise = await getTransactionWatchPromise(tx, signer) - return transactionPromise - - // If we can't always get the finalized event, then add nonce subscribe as other evidence for tx is finalized. - // Don't need it based on current testing. - // const ss58Address = convertPublicKeyToSs58(signer.publicKey) - // const noncePromise = await getNonceChangePromise(api, ss58Address) - - // return new Promise((resolve, reject) => { - // Promise.race([transactionPromise, noncePromise]) - // .then(resolve) - // .catch(reject); - // }) -} - - -export async function getTransactionWatchPromise(tx: Transaction<{}, string, string, void>, signer: PolkadotSigner,) { - return new Promise((resolve, reject) => { - // store the txHash, then use it in timeout. easier to know which tx is not finalized in time - let txHash = "" - const subscription = tx.signSubmitAndWatch(signer).subscribe({ - next(value) { - txHash = value.txHash - - // TODO investigate why finalized not for each extrinsic - if (value.type === "finalized") { - console.log("Transaction is finalized in block:", value.txHash); - subscription.unsubscribe(); - clearTimeout(timeoutId); - if (!value.ok) { - console.log("Transaction threw an error:", value.dispatchError) - } - // Resolve the promise when the transaction is finalized - resolve(); - } - }, - error(err) { - console.error("Transaction failed:", err); - subscription.unsubscribe(); - clearTimeout(timeoutId); - // Reject the promise in case of an error - reject(err); - - }, - complete() { - console.log("Subscription complete"); - } - }); - - const timeoutId = setTimeout(() => { - subscription.unsubscribe(); - console.log('unsubscribed because of timeout for tx {}', txHash); - reject() - }, TX_TIMEOUT); - }); -} - -// second solution to wait for transaction finalization. pass the raw data to avoid the complex transaction type definition -export async function waitForTransactionCompletion2(api: TypedApi, raw: Binary, signer: PolkadotSigner,) { - const tx = await api.txFromCallData(raw); - return new Promise((resolve, reject) => { - const subscription = tx.signSubmitAndWatch(signer).subscribe({ - next(value) { - console.log("Event:", value); - - if (value.type === "txBestBlocksState") { - console.log("Transaction is finalized in block:", value.txHash); - subscription.unsubscribe(); - // Resolve the promise when the transaction is finalized - resolve(); - - } - }, - error(err: Error) { - console.error("Transaction failed:", err); - subscription.unsubscribe(); - // Reject the promise in case of an error - reject(err); - - }, - complete() { - console.log("Subscription complete"); - } - }); - }); -} - -export async function waitForNonceChange(api: TypedApi, ss58Address: string) { - const initNonce = await getNonce(api, ss58Address) - while (true) { - const currentNonce = await getNonce(api, ss58Address) - if (currentNonce > initNonce) { - break - } - - await new Promise(resolve => setTimeout(resolve, 200)); - } -} - -export function waitForFinalizedBlock(api: TypedApi, end: number) { - return new Promise((resolve) => { - const subscription = api.query.System.Number.watchValue("finalized").subscribe((current) => { - if (current > end) { - subscription.unsubscribe(); - resolve(); - } - }) - }) -} \ No newline at end of file diff --git a/contract-tests/src/subtensor.ts b/contract-tests/src/subtensor.ts deleted file mode 100644 index 478148909d..0000000000 --- a/contract-tests/src/subtensor.ts +++ /dev/null @@ -1,588 +0,0 @@ -import * as assert from "assert"; -import { devnet, MultiAddress } from '@polkadot-api/descriptors'; -import { TypedApi, TxCallData, Binary, Enum, getTypedCodecs } from 'polkadot-api'; -import { KeyPair } from "@polkadot-labs/hdkd-helpers" -import { getAliceSigner, waitForTransactionCompletion, getSignerFromKeypair, waitForTransactionWithRetry } from './substrate' -import { convertH160ToSS58, convertPublicKeyToSs58, ethAddressToH160 } from './address-utils' -import { tao } from './balance-math' -import internal from "stream"; -import { createCodec } from "scale-ts"; - -// create a new subnet and return netuid -export async function addNewSubnetwork(api: TypedApi, hotkey: KeyPair, coldkey: KeyPair) { - const alice = getAliceSigner() - const totalNetworks = await api.query.SubtensorModule.TotalNetworks.getValue() - - const defaultNetworkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue() - - 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) - } - - const signer = getSignerFromKeypair(coldkey) - const registerNetworkTx = api.tx.SubtensorModule.register_network({ hotkey: convertPublicKeyToSs58(hotkey.publicKey) }) - await waitForTransactionWithRetry(api, registerNetworkTx, signer) - - const newTotalNetworks = await api.query.SubtensorModule.TotalNetworks.getValue() - // could create multiple subnetworks during retry, just return the first created one - assert.ok(newTotalNetworks > totalNetworks) - - // reset network last lock cost to 0, to avoid the lock cost calculation error - await setNetworkLastLockCost(api, defaultNetworkLastLockCost) - return totalNetworks -} - -// force set balance for a ss58 address -export async function forceSetBalanceToSs58Address(api: TypedApi, ss58Address: string) { - const alice = getAliceSigner() - const balance = tao(1e10) - const internalCall = api.tx.Balances.force_set_balance({ who: MultiAddress.Id(ss58Address), new_free: balance }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - - const balanceOnChain = (await api.query.System.Account.getValue(ss58Address)).data.free - // check the balance except for sudo account becasue of tx fee - if (ss58Address !== convertPublicKeyToSs58(alice.publicKey)) { - assert.equal(balance, balanceOnChain) - } -} - -// set balance for an eth address -export async function forceSetBalanceToEthAddress(api: TypedApi, ethAddress: string) { - const ss58Address = convertH160ToSS58(ethAddress) - await forceSetBalanceToSs58Address(api, ss58Address) -} - -export async function setCommitRevealWeightsEnabled(api: TypedApi, netuid: number, enabled: boolean) { - const value = await api.query.SubtensorModule.CommitRevealWeightsEnabled.getValue(netuid) - if (value === enabled) { - return; - } - - const alice = getAliceSigner() - const internalCall = api.tx.AdminUtils.sudo_set_commit_reveal_weights_enabled({ netuid: netuid, enabled: enabled }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(enabled, await api.query.SubtensorModule.CommitRevealWeightsEnabled.getValue(netuid)) -} - -export async function setWeightsSetRateLimit(api: TypedApi, netuid: number, rateLimit: bigint) { - const value = await api.query.SubtensorModule.WeightsSetRateLimit.getValue(netuid) - if (value === rateLimit) { - return; - } - - const alice = getAliceSigner() - const internalCall = api.tx.AdminUtils.sudo_set_weights_set_rate_limit({ netuid: netuid, weights_set_rate_limit: rateLimit }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(rateLimit, await api.query.SubtensorModule.WeightsSetRateLimit.getValue(netuid)) -} - -// tempo is u16 in rust, but we just number in js. so value should be less than u16::Max -export async function setTempo(api: TypedApi, netuid: number, tempo: number) { - const value = await api.query.SubtensorModule.Tempo.getValue(netuid) - console.log("init avlue is ", value) - if (value === tempo) { - return; - } - - 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) - assert.equal(tempo, await api.query.SubtensorModule.Tempo.getValue(netuid)) -} - -export async function setCommitRevealWeightsInterval(api: TypedApi, netuid: number, interval: bigint) { - const value = await api.query.SubtensorModule.RevealPeriodEpochs.getValue(netuid) - if (value === interval) { - return; - } - - const alice = getAliceSigner() - const internalCall = api.tx.AdminUtils.sudo_set_commit_reveal_weights_interval({ netuid: netuid, interval: interval }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(interval, await api.query.SubtensorModule.RevealPeriodEpochs.getValue(netuid)) -} - - -export async function forceSetChainID(api: TypedApi, chainId: bigint) { - const value = await api.query.EVMChainId.ChainId.getValue() - if (value === chainId) { - return; - } - - const alice = getAliceSigner() - const internalCall = api.tx.AdminUtils.sudo_set_evm_chain_id({ chain_id: chainId }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(chainId, await api.query.EVMChainId.ChainId.getValue()) -} - -export async function disableWhiteListCheck(api: TypedApi, disabled: boolean) { - const value = await api.query.EVM.DisableWhitelistCheck.getValue() - if (value === disabled) { - return; - } - - const alice = getAliceSigner() - const internalCall = api.tx.EVM.disable_whitelist({ disabled: disabled }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(disabled, await api.query.EVM.DisableWhitelistCheck.getValue()) -} - -export async function burnedRegister(api: TypedApi, netuid: number, ss58Address: string, keypair: KeyPair) { - const registered = await api.query.SubtensorModule.Uids.getValue(netuid, ss58Address); - // just return if already registered - if (registered !== undefined) { - console.log("hotkey ", ss58Address, " already registered in netuid ", netuid) - return; - } - - await new Promise((resolve) => setTimeout(resolve, 1000)); - const uids = await api.query.SubtensorModule.SubnetworkN.getValue(netuid) - const signer = getSignerFromKeypair(keypair) - const tx = api.tx.SubtensorModule.burned_register({ hotkey: ss58Address, netuid: netuid }) - await waitForTransactionWithRetry(api, tx, signer) - assert.equal(uids + 1, await api.query.SubtensorModule.SubnetworkN.getValue(netuid)) -} - - -export async function sendProxyCall(api: TypedApi, calldata: TxCallData, ss58Address: string, keypair: KeyPair) { - const signer = getSignerFromKeypair(keypair) - const tx = api.tx.Proxy.proxy({ - call: calldata, - real: MultiAddress.Id(ss58Address), - force_proxy_type: undefined - }); - await waitForTransactionWithRetry(api, tx, signer) -} - - -export async function setTxRateLimit(api: TypedApi, txRateLimit: bigint) { - const value = await api.query.SubtensorModule.TxRateLimit.getValue() - if (value === txRateLimit) { - return; - } - const alice = getAliceSigner() - - const internalCall = api.tx.AdminUtils.sudo_set_tx_rate_limit({ tx_rate_limit: txRateLimit }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - - await waitForTransactionWithRetry(api, tx, alice) -} - -export async function setMaxAllowedValidators(api: TypedApi, netuid: number, maxAllowedValidators: number) { - const value = await api.query.SubtensorModule.MaxAllowedValidators.getValue(netuid) - if (value === maxAllowedValidators) { - return; - } - - const alice = getAliceSigner() - - const internalCall = api.tx.AdminUtils.sudo_set_max_allowed_validators({ - netuid: netuid, - max_allowed_validators: maxAllowedValidators - }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(maxAllowedValidators, await api.query.SubtensorModule.MaxAllowedValidators.getValue(netuid)) -} - -export async function setSubnetOwnerCut(api: TypedApi, subnetOwnerCut: number) { - const value = await api.query.SubtensorModule.SubnetOwnerCut.getValue() - if (value === subnetOwnerCut) { - return; - } - - const alice = getAliceSigner() - - const internalCall = api.tx.AdminUtils.sudo_set_subnet_owner_cut({ - subnet_owner_cut: subnetOwnerCut - }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(subnetOwnerCut, await api.query.SubtensorModule.SubnetOwnerCut.getValue()) -} - -export async function setActivityCutoff(api: TypedApi, netuid: number, activityCutoff: number) { - const value = await api.query.SubtensorModule.ActivityCutoff.getValue(netuid) - if (value === activityCutoff) { - return; - } - - const alice = getAliceSigner() - - const internalCall = api.tx.AdminUtils.sudo_set_activity_cutoff({ - netuid: netuid, - activity_cutoff: activityCutoff - }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(activityCutoff, await api.query.SubtensorModule.ActivityCutoff.getValue(netuid)) -} - -export async function setMinDelegateTake(api: TypedApi, minDelegateTake: number) { - const value = await api.query.SubtensorModule.MinDelegateTake.getValue() - if (value === minDelegateTake) { - return; - } - - const alice = getAliceSigner() - - const internalCall = api.tx.AdminUtils.sudo_set_min_delegate_take({ - take: minDelegateTake - }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) - assert.equal(minDelegateTake, await api.query.SubtensorModule.MinDelegateTake.getValue()) -} - -export async function addStake(api: TypedApi, netuid: number, ss58Address: string, amount_staked: bigint, keypair: KeyPair) { - const signer = getSignerFromKeypair(keypair) - let tx = api.tx.SubtensorModule.add_stake({ - netuid: netuid, - hotkey: ss58Address, - amount_staked: amount_staked - }) - - await waitForTransactionWithRetry(api, tx, signer) -} - -export async function setWeight(api: TypedApi, netuid: number, dests: number[], weights: number[], version_key: bigint, keypair: KeyPair) { - const signer = getSignerFromKeypair(keypair) - let tx = api.tx.SubtensorModule.set_weights({ - netuid: netuid, - dests: dests, - weights: weights, - version_key: version_key - }) - - await waitForTransactionWithRetry(api, tx, signer) -} - -export async function rootRegister(api: TypedApi, ss58Address: string, keypair: KeyPair) { - const signer = getSignerFromKeypair(keypair) - let tx = api.tx.SubtensorModule.root_register({ - hotkey: ss58Address - }) - - await waitForTransactionWithRetry(api, tx, signer) -} - -export async function setSubtokenEnable(api: TypedApi, netuid: number, subtokenEnable: boolean) { - const signer = getAliceSigner() - let internalTx = api.tx.AdminUtils.sudo_set_subtoken_enabled({ - netuid: netuid, - subtoken_enabled: subtokenEnable - }) - let tx = api.tx.Sudo.sudo({ call: internalTx.decodedCall }) - - await waitForTransactionWithRetry(api, tx, signer) -} - -export async function startCall(api: TypedApi, netuid: number, keypair: KeyPair) { - 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() - } - // wait for chain to run coinbase - await new Promise((resolve) => setTimeout(resolve, 2000)); - - const signer = getSignerFromKeypair(keypair) - let tx = api.tx.SubtensorModule.start_call({ - netuid: netuid, - }) - - await waitForTransactionWithRetry(api, tx, signer) - - await new Promise((resolve) => setTimeout(resolve, 1000)); - const callStarted = await api.query.SubtensorModule.FirstEmissionBlockNumber - .getValue(netuid); - assert.notEqual(callStarted, undefined); -} - -export async function setMaxChildkeyTake(api: TypedApi, take: number) { - const alice = getAliceSigner() - const internalCall = api.tx.SubtensorModule.sudo_set_max_childkey_take({ take }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - - await waitForTransactionWithRetry(api, tx, alice) -} - -// Swap coldkey to contract address -export async function swapColdkey( - api: TypedApi, - coldkey: KeyPair, - contractAddress: string, -) { - const alice = getAliceSigner(); - const internal_tx = api.tx.SubtensorModule.swap_coldkey({ - old_coldkey: convertPublicKeyToSs58(coldkey.publicKey), - new_coldkey: convertH160ToSS58(contractAddress), - swap_cost: tao(10), - }); - const tx = api.tx.Sudo.sudo({ - call: internal_tx.decodedCall, - }); - await waitForTransactionWithRetry(api, tx, alice); -} - -// Set target registrations per interval to 1000 -export async function setTargetRegistrationsPerInterval( - api: TypedApi, - netuid: number, -) { - const alice = getAliceSigner(); - const internal_tx = api.tx.AdminUtils - .sudo_set_target_registrations_per_interval({ - netuid, - target_registrations_per_interval: 1000, - }); - const tx = api.tx.Sudo.sudo({ - call: internal_tx.decodedCall, - }); - await waitForTransactionWithRetry(api, tx, alice); - - const value = await api.query.SubtensorModule.TargetRegistrationsPerInterval.getValue(netuid) - assert.equal(1000, value) -} - -// Disable admin freeze window and owner hyperparam rate limiting for tests -export async function disableAdminFreezeWindowAndOwnerHyperparamRateLimit(api: TypedApi) { - const alice = getAliceSigner() - - const currentAdminFreezeWindow = await api.query.SubtensorModule.AdminFreezeWindow.getValue() - if (currentAdminFreezeWindow !== 0) { - // Set AdminFreezeWindow to 0 - const setFreezeWindow = api.tx.AdminUtils.sudo_set_admin_freeze_window({ window: 0 }) - const sudoFreezeTx = api.tx.Sudo.sudo({ call: setFreezeWindow.decodedCall }) - await waitForTransactionWithRetry(api, sudoFreezeTx, alice) - } - - const currentOwnerHyperparamRateLimit = await api.query.SubtensorModule.OwnerHyperparamRateLimit.getValue() - if (currentOwnerHyperparamRateLimit !== 0) { - // Set OwnerHyperparamRateLimit to 0 - const setOwnerRateLimit = api.tx.AdminUtils.sudo_set_owner_hparam_rate_limit({ epochs: 0 }) - const sudoOwnerRateTx = api.tx.Sudo.sudo({ call: setOwnerRateLimit.decodedCall }) - await waitForTransactionWithRetry(api, sudoOwnerRateTx, alice) - } - - assert.equal(0, await api.query.SubtensorModule.AdminFreezeWindow.getValue()) - assert.equal(BigInt(0), await api.query.SubtensorModule.OwnerHyperparamRateLimit.getValue()) -} - -export async function sendWasmContractExtrinsic(api: TypedApi, coldkey: KeyPair, contractAddress: string, data: Binary) { - const signer = getSignerFromKeypair(coldkey) - const tx = await api.tx.Contracts.call({ - value: BigInt(0), - dest: MultiAddress.Id(contractAddress), - data: Binary.fromBytes(data.asBytes()), - gas_limit: { - ref_time: BigInt(10000000000), - proof_size: BigInt(10000000), - }, - storage_deposit_limit: BigInt(1000000000) - }) - await waitForTransactionWithRetry(api, tx, signer) -} - -export async function setNetworkLastLockCost(api: TypedApi, defaultNetworkLastLockCost: bigint) { - const alice = getAliceSigner() - const key = await api.query.SubtensorModule.NetworkLastLockCost.getKey() - const codec = await getTypedCodecs(devnet); - const value = codec.query.SubtensorModule.NetworkLastLockCost.value.enc(defaultNetworkLastLockCost) - const internalCall = api.tx.System.set_storage({ - items: [[Binary.fromHex(key), Binary.fromBytes(value)]] - }) - const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }) - await waitForTransactionWithRetry(api, tx, alice) - - const valueOnChain = await api.query.SubtensorModule.NetworkLastLockCost.getValue() - assert.equal(defaultNetworkLastLockCost, valueOnChain) -} - -export function getSubnetAccountId(netuid: number): string { - // Hardcode to speed up tests - const NETUID_TO_ACCOUNT_ID: Record = { - 0: "5EYCAe5jLQhn6ofDSvqF6iY53erXNkwhyE1aCEgvi1NNs91F", - 1: "5EYCAe5jLQhn6ofDSvqWqk5fA9XiqK3ahtx5kBNmAqF78mqL", - 2: "5EYCAe5jLQhn6ofDSvqnamdFGeCvHs9TSZtbJ84bdf7qQRc6", - 3: "5EYCAe5jLQhn6ofDSvr4KoAqP8t7kRFLBEq6r4kS6UzZgCb5", - 4: "5EYCAe5jLQhn6ofDSvrL4piRVdZKCyMCuumcQ1SGZJsHwmeE", - 5: "5EYCAe5jLQhn6ofDSvrborG1c8EWfXT5eai7wx8728k2DHK7", - 6: "5EYCAe5jLQhn6ofDSvrsYsobicui85YxPFedVtowUxckUuF8", - 7: "5EYCAe5jLQhn6ofDSvs9HuMBq7auadeq7vb93qVmwnVUkg5A", - 8: "5EYCAe5jLQhn6ofDSvsR2vtmwcG73BkhrbXebnBcQcND2Bdh", - 9: "5EYCAe5jLQhn6ofDSvsgmxSN46wJVjrabGUA9isSsSEwHnFy", - 10: "5EYCAe5jLQhn6ofDSvsxWyyxAbcVxHxTKwQfhfZHLG7fZUJG", - 11: "5EYCAe5jLQhn6ofDSvtEG1XYH6HhQr4L4cMBFcF7o5zPpyA3", - 12: "5EYCAe5jLQhn6ofDSvtW1358PaxtsQACoHHgoYvxFus86kJK", - 13: "5EYCAe5jLQhn6ofDSvtmk4ciW5e6KxG5XxECMVcnijjrN8rz", - 14: "5EYCAe5jLQhn6ofDSvu3V6AJcaKHnWMxGdAhuSJdBZcadwDn", - 15: "5EYCAe5jLQhn6ofDSvuKE7htj4zVF4Tq1J7DTNzTePVJucfX", - 16: "5EYCAe5jLQhn6ofDSvuay9FUqZfghcZhjy3j1KgJ7DN3BDc2", - 17: "5EYCAe5jLQhn6ofDSvuriAo4x4LtAAfaUdzEZGN8a3EmSncG", - 18: "5EYCAe5jLQhn6ofDSvv8TCLf4Z25cimTDJvk7D3y2s7ViZEm", - 19: "5EYCAe5jLQhn6ofDSvvQCDtFB3hH5GsKwysFf9joVgzDytnb", - 20: "5EYCAe5jLQhn6ofDSvvfwFRqHYNUXpyCgeomD6RdxWrxFpQR", - 21: "5EYCAe5jLQhn6ofDSvvwgGyRQ33fzP55RKkGm37URLjgXG7M", - 22: "5EYCAe5jLQhn6ofDSvwDRJX1WXisSwAx9zgnJyoJtAcQo59Y", - 23: "5EYCAe5jLQhn6ofDSvwVAL4bd2Q4uVGptfdHrvV9LzV94VBb", - 24: "5EYCAe5jLQhn6ofDSvwkuMcBjX5GN3NhdLZoQsAyopMsL7A7", - 25: "5EYCAe5jLQhn6ofDSvx2eP9mr1kTpbUaN1WJxorpGeEbbfgG", - 26: "5EYCAe5jLQhn6ofDSvxJPQhMxWRfH9aT6gSpWkYejU7KsbGp", - 27: "5EYCAe5jLQhn6ofDSvxa8SEx516rjhgKqMPL4hEVCHz49DPw", - 28: "5EYCAe5jLQhn6ofDSvxqsTnYBVn4CFnCa2KqcdvKf7rnQo7f", - 29: "5EYCAe5jLQhn6ofDSvy7cVL8HzTFeot5JhGMAacA7wjWgPix", - 30: "5EYCAe5jLQhn6ofDSvyPMWsiQV8T7Myx3NCriXHzamcEwyqa", - 31: "5EYCAe5jLQhn6ofDSvyf6YRJWyoeZv5pn39NGTyq3bUyDc8k", - 32: "5EYCAe5jLQhn6ofDSvyvqZxtdUUr2UBhWi5spQffWRMhV5hU", - 33: "5EYCAe5jLQhn6ofDSvzCabWUjyA3V2HaFP2PNMMVyFERkxPm", - 34: "5EYCAe5jLQhn6ofDSvzUKd44rTqEwaPSz3xtvJ3LS57A2Td3", - 35: "5EYCAe5jLQhn6ofDSvzk4ebexxWSQ8VKiiuQUEjAttytJ8Nx", - 36: "5EYCAe5jLQhn6ofDSw11og9F5TBdrgbCTPqv2BR1MircZp68", - 37: "5EYCAe5jLQhn6ofDSw1HYhgqBwrqKEh5C4nRa86qpYjLqCQd", - 38: "5EYCAe5jLQhn6ofDSw1ZHjERJSY2mnnwvjiw84ngHNc56t9n", - 39: "5EYCAe5jLQhn6ofDSw1q2kn1QwDEELtpfQfSg1UWkCUoNVFh", - 40: "5EYCAe5jLQhn6ofDSw26mnKbXRtRgtzhQ5bxDxAMD2MXeL6A", - 41: "5EYCAe5jLQhn6ofDSw2NWosBdvZd9T6a8kYTmtrBfrEFusmX", - 42: "5EYCAe5jLQhn6ofDSw2eFqQmkREpc1CSsRUyKqY28g6zBbxD", - 43: "5EYCAe5jLQhn6ofDSw2uzrxMruv24ZJKc6RUsnDrbVyiT4uZ", - 44: "5EYCAe5jLQhn6ofDSw3BjtVwyQbDX7QCLmMzRiuh4KrSienC", - 45: "5EYCAe5jLQhn6ofDSw3TUv3Y5uGQyfW55SJVyfbXX9jAzHBc", - 46: "5EYCAe5jLQhn6ofDSw3jDwb8CPwcSDbwp7F1XcHMyybuFsV6", - 47: "5EYCAe5jLQhn6ofDSw3zxy8iJtcotmhpYnBX5YyCSoUdXS9C", - 48: "5EYCAe5jLQhn6ofDSw4GhzgJRPJ1MKohHT82dVf2udMMo854", - 49: "5EYCAe5jLQhn6ofDSw4YT2DtXsyCosua284YBSLsNTE64sjn", - 50: "5EYCAe5jLQhn6ofDSw4pC3mUeNeQGS1Sko13jP2hqH6pLJc1", - 51: "5EYCAe5jLQhn6ofDSw55w5K4ksKbiz7KVTwZHKiYJ6yYc62p", - 52: "5EYCAe5jLQhn6ofDSw5Mg6resMzoBYDCE8t4qGQNkvrGsYLi", - 53: "5EYCAe5jLQhn6ofDSw5dR8QEyrfze6K4xopaPD6DDkj19BcH", - 54: "5EYCAe5jLQhn6ofDSw5uA9wq6MMC6eQwhUm5w9n3gabjR243", - 55: "5EYCAe5jLQhn6ofDSw6AuBVRCr2PZCWpS9hbV6Tt9QUTghER", - 56: "5EYCAe5jLQhn6ofDSw6SeD31KLhb1kchApe7339icEMBxKr7", - 57: "5EYCAe5jLQhn6ofDSw6iPEabRqNnUJiZuVacayqZ54DvDhGB", - 58: "5EYCAe5jLQhn6ofDSw6z8G8BYL3yvrpSeAX88vXPXt6eVRoY", - 59: "5EYCAe5jLQhn6ofDSw7FsHfmepjBPQvKNqTdgsDDzhyNky3e", - 60: "5EYCAe5jLQhn6ofDSw7XcKDMmKQNqy2C7WQ9Eou4TXr72f1B", - 61: "5EYCAe5jLQhn6ofDSw7oMLkwsp5aJX84rBLenkatvMiqJC2W", - 62: "5EYCAe5jLQhn6ofDSw856NJXzJkmm5DwarHALhGjPBbZa5wW", - 63: "5EYCAe5jLQhn6ofDSw8LqPr86oRyDdKpKXDftdxZr1UHqWzq", - 64: "5EYCAe5jLQhn6ofDSw8caRPiDJ7AgBRh4CABSaeQJqM278gq", - 65: "5EYCAe5jLQhn6ofDSw8tKSwJKnnN8jXZns6gzXLEmfDkNtXu", - 66: "5EYCAe5jLQhn6ofDSw9A4UUtSHTZbHdSXY3CYU25EV6UeLH2", - 67: "5EYCAe5jLQhn6ofDSw9RoW2UYn8m3qjKGCyi6QhuhJyCv9nu", - 68: "5EYCAe5jLQhn6ofDSw9hYXa4fGoxWPqBzsvDeMPkA8qwBecQ", - 69: "5EYCAe5jLQhn6ofDSw9yHZ7emmV9xww4jYrjCJ5acxifTH7b", - 70: "5EYCAe5jLQhn6ofDSwAF2afEtGAMRW2wUDoEkEmR5nbPiuFf", - 71: "5EYCAe5jLQhn6ofDSwAWmcCpzkqYt48pCtjkJBTFYcU7ziWG", - 72: "5EYCAe5jLQhn6ofDSwAnWdkR7FWkLcEgwZgFr8961SLrGPJp", - 73: "5EYCAe5jLQhn6ofDSwB4FfJ1DkBwoALZgEcmQ4pvUGDaXxGw", - 74: "5EYCAe5jLQhn6ofDSwBKzgqbLEs9FiSSQuZGx1Wkw66JoNQY", - 75: "5EYCAe5jLQhn6ofDSwBbjiPBSjYLiGYK9aVnVxCbPuy357eQ", - 76: "5EYCAe5jLQhn6ofDSwBsUjvmZEDYApeBtFSJ3ttRrjqmLmRP", - 77: "5EYCAe5jLQhn6ofDSwC9DmUMfitjdNk4cvNobqaGKZiVcSd4", - 78: "5EYCAe5jLQhn6ofDSwCQxo1wnDZw5vqwMbKK9nG6nPbDsr3v", - 79: "5EYCAe5jLQhn6ofDSwCghpZXtiF8YUwp6GFphiwwFDTx9ZXw", - 80: "5EYCAe5jLQhn6ofDSwCxSr781CvL133gpwCLFfdmi3LgRGUs", - 81: "5EYCAe5jLQhn6ofDSwDEBsei7hbXTb9ZZc8qocKcAsDQgmDH", - 82: "5EYCAe5jLQhn6ofDSwDVvuCJECGiv9FSJH5MMZ1Sdh68xe6G", - 83: "5EYCAe5jLQhn6ofDSwDmfvjtLgwvNhMK2x1ruVhH6WxsE2Rh", - 84: "5EYCAe5jLQhn6ofDSwE3QxHUTBd7qFTBmcxNTSP7ZLqbVqHX", - 85: "5EYCAe5jLQhn6ofDSwEK9yq4ZgJKHoZ4WHtt1P4x2AiKmP2V", - 86: "5EYCAe5jLQhn6ofDSwEau1NegAyWkMewExqPZKknUzb42r36", - 87: "5EYCAe5jLQhn6ofDSwEre2vEnfeiCukoydmu7GScwpTnJa5d", - 88: "5EYCAe5jLQhn6ofDSwF8P4TpuAKufTrgiJiQfD8TQeLWaGop", - 89: "5EYCAe5jLQhn6ofDSwFQ861R1f1781xZSyevD9pHsUDEqiBR", - 90: "5EYCAe5jLQhn6ofDSwFfs7Z189gJaa4SBebRm6W8LJ5y7dfH", - 91: "5EYCAe5jLQhn6ofDSwFwc96bEeMW38AJvKXwK3Bxo7xhP3yn", - 92: "5EYCAe5jLQhn6ofDSwGDMAeBM92hVgGBezUSrysoFwqReqrS", - 93: "5EYCAe5jLQhn6ofDSwGV6CBmTdhtxEN4PfQxQvZdimi9vW9r", - 94: "5EYCAe5jLQhn6ofDSwGkqDjMa8P6QnTw8LMTxsFUBbatC8C5", - 95: "5EYCAe5jLQhn6ofDSwH2aFGwgd4HsLZos1HyWowJeRTcTVsg", - 96: "5EYCAe5jLQhn6ofDSwHJKGpXo7jVKtfgbgEV4kd97FLLjBeJ", - 97: "5EYCAe5jLQhn6ofDSwHa4JN7ucQgnSmZLMAzchJya5D4zq8v", - 98: "5EYCAe5jLQhn6ofDSwHqoKui275tEzsS527WAdzp2u5oGNSd", - 99: "5EYCAe5jLQhn6ofDSwJ7YMTJ8bm5hYyJoh41iageVixXYH59", - 100: "5EYCAe5jLQhn6ofDSwJPHNztF6SHA75BYMzXGXNUxYqFoj9g", - 101: "5EYCAe5jLQhn6ofDSwJf2QYUMb7UcfB4H2w2pU4KRNhz5GP5", - 102: "5EYCAe5jLQhn6ofDSwJvmS64U5ng5DGw1hsYNQk9tCaiLvoS", - 103: "5EYCAe5jLQhn6ofDSwKCWTdeaaTsXmNokNp3vMRzM2TScknA", - 104: "5EYCAe5jLQhn6ofDSwKUFVBEh594zKUgV3kZUJ7porLAtE76", - 105: "5EYCAe5jLQhn6ofDSwKjzWipoZpGSsaZDih52EofGgCu9mbP", - 106: "5EYCAe5jLQhn6ofDSwL1jYGQv4VTuRgRxPdaaBVVjW5dRU9u", - 107: "5EYCAe5jLQhn6ofDSwLHUZp12ZAfMynJh4a688BLCKxMhEMq", - 108: "5EYCAe5jLQhn6ofDSwLZDbMb93qrpXtBRjWbg4sAf9q5xtB8", - 109: "5EYCAe5jLQhn6ofDSwLpxcuBFYX4H5z4AQT7E1Z17yhpELLK", - 110: "5EYCAe5jLQhn6ofDSwM6heSmN3CFje5vu5PcmxEqaoaYW1KP", - 111: "5EYCAe5jLQhn6ofDSwMNSfzMUXsTCCBodkL8Ktvg3dTGmYbX", - 112: "5EYCAe5jLQhn6ofDSwMeBhXwb2YeekHgNRGdsqcWWTL13NLP", - 113: "5EYCAe5jLQhn6ofDSwMuvj5XhXDr7JPZ76D9RnJLyHCjK2Zy", - 114: "5EYCAe5jLQhn6ofDSwNBfkd7p1u3ZrVRqm9eyizBS75TaPgK", - 115: "5EYCAe5jLQhn6ofDSwNTQnAhvWaF2QbJaS6AXfg1tvxBrDUN", - 116: "5EYCAe5jLQhn6ofDSwNj9oiJ31FSUxhBK72g5cMrMkpv7iJx", - 117: "5EYCAe5jLQhn6ofDSwNztqFt9VvdwWo43myBdZ3gpahePQpf", - 118: "5EYCAe5jLQhn6ofDSwPGdroUFzbqQ4tvnSuhBVjXHQaNet2o", - 119: "5EYCAe5jLQhn6ofDSwPYNtM4NVH2rczoX7rCjSRMkET6vioH", - 120: "5EYCAe5jLQhn6ofDSwPp7uteUyxEKB6gFnniHP7CD4KqCQDN", - 121: "5EYCAe5jLQhn6ofDSwQ5rwSEbUdRmjCYzTjDqKo2ftCZTubr", - 122: "5EYCAe5jLQhn6ofDSwQMbxyphyJdEHJRj8fjPGUs8i5HjcA3", - 123: "5EYCAe5jLQhn6ofDSwQdLzXQpTypgqQJTocEwDAhbXx21Awy", - 124: "5EYCAe5jLQhn6ofDSwQu624zvxf29PWBCUYkV9rY4MpkGu1f", - 125: "5EYCAe5jLQhn6ofDSwRAq3cb3TLDbwc3w9VG36YNXBhUYKDi", - 126: "5EYCAe5jLQhn6ofDSwRSa5AB9x1R4VhvfpRmb3ECz1aCp2ze", - 127: "5EYCAe5jLQhn6ofDSwRiK6hmGSgcX3ooQVNH8yv3SqSw5mpH", - 128: "5EYCAe5jLQhn6ofDSwRz48FMNwMoybug9AJngvbsufKfME2t", - } - - return NETUID_TO_ACCOUNT_ID[netuid]; -} - -export async function getStake(api: TypedApi, hotkey: string, coldkey: string, netuid: number): Promise { - 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; -} - -export async function setAdminFreezeWindow(api: TypedApi) { - const alice = getAliceSigner() - const window = 0; - 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) - assert.equal(window, await api.query.SubtensorModule.AdminFreezeWindow.getValue()) -} \ No newline at end of file diff --git a/contract-tests/src/utils.ts b/contract-tests/src/utils.ts deleted file mode 100644 index 1ba191d833..0000000000 --- a/contract-tests/src/utils.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { defineChain, http, publicActions, createPublicClient } from "viem" -import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts' -import { ethers } from "ethers" -import { ETH_LOCAL_URL } from "./config" -import { FixedSizeBinary } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; - -export type ClientUrlType = 'http://localhost:9944'; - -export const chain = (id: number, url: string) => defineChain({ - id: id, - name: 'bittensor', - network: 'bittensor', - nativeCurrency: { - name: 'tao', - symbol: 'TAO', - decimals: 9, - }, - rpcUrls: { - default: { - http: [url], - }, - }, - testnet: true, -}) - - -export async function getPublicClient(url: ClientUrlType) { - const wallet = createPublicClient({ - chain: chain(42, url), - transport: http(), - - }) - - return wallet.extend(publicActions) -} - -/** - * Generates a random Ethereum wallet - * @returns wallet keyring - */ -export function generateRandomEthWallet() { - let privateKey = generatePrivateKey().toString(); - privateKey = privateKey.replace('0x', ''); - - const account = privateKeyToAccount(`0x${privateKey}`) - return account -} - - -export function generateRandomEthersWallet() { - const account = ethers.Wallet.createRandom(); - const provider = new ethers.JsonRpcProvider(ETH_LOCAL_URL); - - const wallet = new ethers.Wallet(account.privateKey, provider); - return wallet; -} - -export function convertToFixedSizeBinary(hexString: string, size: T): FixedSizeBinary { - // Convert hex string to a byte array - const byteArray = hexToU8a(hexString); - - // Ensure the byte array is exactly the specified size - if (byteArray.length !== size) { - throw new Error(`The provided string "${hexString}" does not convert to exactly ${size} bytes.`); - } - - return new FixedSizeBinary(byteArray); -} diff --git a/contract-tests/test/alphaPool.test.ts b/contract-tests/test/alphaPool.test.ts deleted file mode 100644 index d8ecdc8a00..0000000000 --- a/contract-tests/test/alphaPool.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { devnet } from "@polkadot-api/descriptors"; -import { u8aToHex } from "@polkadot/util"; -import * as assert from "assert"; -import { ethers } from "ethers"; -import { TypedApi } from "polkadot-api"; -import { PublicClient } from "viem"; -import { convertH160ToPublicKey, convertH160ToSS58, convertPublicKeyToSs58, toViemAddress } from "../src/address-utils"; -import { tao } from "../src/balance-math"; -import { ETH_LOCAL_URL } from "../src/config"; -import { ALPHA_POOL_CONTRACT_ABI, ALPHA_POOL_CONTRACT_BYTECODE } from "../src/contracts/alphaPool"; -import { ISTAKING_V2_ADDRESS, IStakingV2ABI } from "../src/contracts/staking"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"; -import { addNewSubnetwork, burnedRegister, disableWhiteListCheck, forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, getStake, startCall } from "../src/subtensor"; -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; -// import { KeyPair } from "@polkadot-labs/hdkd-helpers"; -describe("bridge token contract deployment", () => { - // init eth part - const wallet = generateRandomEthersWallet(); - let publicClient: PublicClient; - - // init substrate part - let api: TypedApi - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - - before(async () => { - // init variables got from await and async - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - - let value = await api.constants.SubtensorModule.InitialMinStake; - - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await addNewSubnetwork(api, hotkey, coldkey) - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - await startCall(api, netuid, coldkey) - console.log("will test in subnet: ", netuid) - - await burnedRegister(api, netuid, convertH160ToSS58(wallet.address), coldkey) - - await forceSetBalanceToEthAddress(api, wallet.address) - await disableWhiteListCheck(api, true) - }); - - it("Can add stake V2", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - // the unit in V2 is RAO, not ETH - let stakeBalance = tao(20) - 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() - - const stakeFromContract = BigInt( - await contract.getStake(hotkey.publicKey, convertH160ToPublicKey(wallet.address), netuid) - ); - - assert.ok(stakeFromContract > stakeBefore) - const stakeAfter = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) - assert.ok(stakeAfter > stakeBefore) - assert.ok(stakeFromContract > tao(20)) - }) - - - it("Can deploy alpha pool smart contract", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - const stakingPrecompile = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, wallet); - - 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) - await contract.waitForDeployment() - assert.notEqual(contract.target, undefined) - - const contractAddress = contract.target.toString() - const contractPublicKey = convertH160ToPublicKey(contractAddress) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(contractPublicKey)) - - const code = await publicClient.getCode({ address: toViemAddress(contractAddress) }) - if (code === undefined) { - throw new Error("code not available") - } - assert.ok(code.length > 100) - assert.ok(code.includes("0x60806040523480156")) - - console.log("deployment contractAddress: ", contractAddress) - - const contractForCall = new ethers.Contract(contractAddress, ALPHA_POOL_CONTRACT_ABI, wallet) - const setContractColdkeyTx = await contractForCall.setContractColdkey(contractPublicKey) - await setContractColdkeyTx.wait() - - // check contract coldkey and hotkey - const contractColdkey = await contractForCall.contract_coldkey() - assert.equal(contractColdkey, u8aToHex(contractPublicKey)) - const contractHotkey = await contractForCall.contract_hotkey() - assert.equal(contractHotkey, u8aToHex(hotkey.publicKey)) - - const alphaInPool = await contractForCall.getContractStake(netuid) - assert.equal(alphaInPool, BigInt(0)) - - const depositAlphaTx = await contractForCall.depositAlpha(netuid, tao(10).toString(), hotkey.publicKey) - await depositAlphaTx.wait() - - // compare wallet stake - const stakeAftereDeposit = await getStake(api, convertPublicKeyToSs58(hotkey.publicKey), convertH160ToSS58(wallet.address), netuid) - assert.ok(stakeAftereDeposit < stakeBeforeDeposit) - - // check the contract stake - 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 - const alphaBalanceOnContract = await contractForCall.alphaBalance(wallet.address, netuid) - assert.ok(tao(10) - alphaBalanceOnContract < BigInt(1000)) - - // check the contract stake from the staking precompile - const stakeFromContract = BigInt( - await stakingPrecompile.getStake(hotkey.publicKey, contractPublicKey, netuid) - ); - assert.equal(stakeFromContract, await contractForCall.getContractStake(netuid)) - - }); - -}); diff --git a/contract-tests/test/crowdloan.precompile.test.ts b/contract-tests/test/crowdloan.precompile.test.ts deleted file mode 100644 index 2a0655bc63..0000000000 --- a/contract-tests/test/crowdloan.precompile.test.ts +++ /dev/null @@ -1,204 +0,0 @@ -import * as assert from "assert"; - -import { ethers } from "ethers"; -import { Binary, TypedApi } from "polkadot-api"; -import { devnet } from "@polkadot-api/descriptors"; -import { ICROWDLOAN_ADDRESS, ICrowdloanABI } from "../src/contracts/crowdloan"; -import { convertH160ToSS58 } from "../src/address-utils"; -import { generateRandomEthersWallet } from "../src/utils"; -import { - getAliceSigner, - getDevnetApi, - waitForFinalizedBlock, -} from "../src/substrate"; -import { forceSetBalanceToEthAddress } from "../src/subtensor"; - -describe("Crowdloan precompile E2E balance smoke", () => { - let api: TypedApi; - - const alice = getAliceSigner(); - const wallet1 = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - const wallet3 = generateRandomEthersWallet(); - const wallet4 = generateRandomEthersWallet(); - - const crowdloanContract = new ethers.Contract( - ICROWDLOAN_ADDRESS, - ICrowdloanABI, - wallet1, - ); - - before(async () => { - api = await getDevnetApi(); - - await forceSetBalanceToEthAddress(api, wallet1.address); - await forceSetBalanceToEthAddress(api, wallet2.address); - await forceSetBalanceToEthAddress(api, wallet3.address); - await forceSetBalanceToEthAddress(api, wallet4.address); - }); - - it("charges and refunds balances through create, contribute, withdraw, refund, and dissolve", async () => { - const deposit = BigInt(20_000_000_000); - const minContribution = BigInt(2_000_000_000); - const cap = BigInt(100_000_000_000); - const end = (await api.query.System.Number.getValue()) + 100; - const targetAddress = generateRandomEthersWallet(); - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - const creatorBalanceBeforeCreate = await api.query.System.Account.getValue( - convertH160ToSS58(wallet1.address), - ); - let tx = await crowdloanContract.create( - deposit, - minContribution, - cap, - end, - targetAddress, - ); - await tx.wait(); - - const creatorBalanceAfterCreate = await api.query.System.Account.getValue( - convertH160ToSS58(wallet1.address), - ); - assert.ok( - Number( - creatorBalanceBeforeCreate.data.free - - creatorBalanceAfterCreate.data.free, - ) - - Number(deposit) < - 1_000_000, - ); - - const contribution = BigInt(20_000_000_000); - const crowdloanContract2 = new ethers.Contract( - ICROWDLOAN_ADDRESS, - ICrowdloanABI, - wallet2, - ); - const contributorBalanceBefore = await api.query.System.Account.getValue( - convertH160ToSS58(wallet2.address), - ); - tx = await crowdloanContract2.contribute(nextId, contribution); - await tx.wait(); - - let contributorBalanceAfter = await api.query.System.Account.getValue( - convertH160ToSS58(wallet2.address), - ); - assert.ok( - Number( - contributorBalanceBefore.data.free - contributorBalanceAfter.data.free, - ) - - Number(contribution) < - 1_000_000, - ); - - tx = await crowdloanContract2.withdraw(nextId); - await tx.wait(); - - contributorBalanceAfter = await api.query.System.Account.getValue( - convertH160ToSS58(wallet2.address), - ); - assert.ok( - Number( - contributorBalanceBefore.data.free - contributorBalanceAfter.data.free, - ) < 1_000_000, - ); - - const crowdloanContract3 = new ethers.Contract( - ICROWDLOAN_ADDRESS, - ICrowdloanABI, - wallet3, - ); - const crowdloanContract4 = new ethers.Contract( - ICROWDLOAN_ADDRESS, - ICrowdloanABI, - wallet4, - ); - const refundBalanceBefore3 = await api.query.System.Account.getValue( - convertH160ToSS58(wallet3.address), - ); - const refundBalanceBefore4 = await api.query.System.Account.getValue( - convertH160ToSS58(wallet4.address), - ); - tx = await crowdloanContract3.contribute(nextId, contribution); - await tx.wait(); - tx = await crowdloanContract4.contribute(nextId, contribution); - await tx.wait(); - - await waitForFinalizedBlock(api, end); - - tx = await crowdloanContract.refund(nextId); - await tx.wait(); - - const refundBalanceAfter3 = await api.query.System.Account.getValue( - convertH160ToSS58(wallet3.address), - ); - const refundBalanceAfter4 = await api.query.System.Account.getValue( - convertH160ToSS58(wallet4.address), - ); - assert.ok( - Number(refundBalanceBefore3.data.free - refundBalanceAfter3.data.free) < - 1_000_000, - ); - assert.ok( - Number(refundBalanceBefore4.data.free - refundBalanceAfter4.data.free) < - 1_000_000, - ); - - tx = await crowdloanContract.dissolve(nextId); - await tx.wait(); - - const creatorBalanceAfterDissolve = await api.query.System.Account.getValue( - convertH160ToSS58(wallet1.address), - ); - assert.ok( - Number( - creatorBalanceBeforeCreate.data.free - - creatorBalanceAfterDissolve.data.free, - ) < 2_000_000, - ); - }); - - it("contributes and withdraws against a crowdloan created on substrate side", async () => { - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const deposit = BigInt(15_000_000_000); - const end = (await api.query.System.Number.getValue()) + 100; - - await api.tx.Crowdloan.create({ - deposit, - min_contribution: BigInt(1_000_000_000), - cap: BigInt(100_000_000_000), - end, - target_address: undefined, - call: api.tx.System.remark({ remark: Binary.fromText("foo") }) - .decodedCall, - }).signAndSubmit(alice); - - const balanceBefore = await api.query.System.Account.getValue( - convertH160ToSS58(wallet1.address), - ); - - const contribution = BigInt(5_000_000_000); - const tx = await crowdloanContract.contribute(nextId, contribution); - await tx.wait(); - - let balanceAfter = await api.query.System.Account.getValue( - convertH160ToSS58(wallet1.address), - ); - assert.ok( - Number(balanceBefore.data.free - balanceAfter.data.free) - - Number(contribution) < - 1_000_000, - ); - - const tx2 = await crowdloanContract.withdraw(nextId); - await tx2.wait(); - - balanceAfter = await api.query.System.Account.getValue( - convertH160ToSS58(wallet1.address), - ); - assert.ok( - Number(balanceBefore.data.free - balanceAfter.data.free) < 1_000_000, - ); - }); -}); diff --git a/contract-tests/test/eth.bridgeToken.deploy.test.ts b/contract-tests/test/eth.bridgeToken.deploy.test.ts deleted file mode 100644 index 94ebcd1260..0000000000 --- a/contract-tests/test/eth.bridgeToken.deploy.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as assert from "assert"; -import * as chai from "chai"; - -import { getDevnetApi } from "../src/substrate" -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; -import { ETH_LOCAL_URL } from "../src/config"; -import { devnet } from "@polkadot-api/descriptors" -import { PublicClient } from "viem"; -import { TypedApi } from "polkadot-api"; -import { BRIDGE_TOKEN_CONTRACT_ABI, BRIDGE_TOKEN_CONTRACT_BYTECODE } from "../src/contracts/bridgeToken"; -import { toViemAddress } from "../src/address-utils"; -import { forceSetBalanceToEthAddress, disableWhiteListCheck } from "../src/subtensor"; -import { ethers } from "ethers" -describe("bridge token contract deployment", () => { - // init eth part - const wallet = generateRandomEthersWallet(); - let publicClient: PublicClient; - - // init substrate part - let api: TypedApi - - before(async () => { - // init variables got from await and async - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - - await forceSetBalanceToEthAddress(api, wallet.address) - await disableWhiteListCheck(api, true) - }); - - it("Can deploy bridge token smart contract", async () => { - const contractFactory = new ethers.ContractFactory(BRIDGE_TOKEN_CONTRACT_ABI, BRIDGE_TOKEN_CONTRACT_BYTECODE, wallet) - const contract = await contractFactory.deploy("name", - "symbol", wallet.address) - await contract.waitForDeployment() - assert.notEqual(contract.target, undefined) - - const contractAddress = contract.target.toString() - - const code = await publicClient.getCode({ address: toViemAddress(contractAddress) }) - if (code === undefined) { - throw new Error("code not available") - } - assert.ok(code.length > 100) - assert.ok(code.includes("0x60806040523480156")) - }); - - it("Can deploy bridge token contract with gas limit", async () => { - const contractFactory = new ethers.ContractFactory(BRIDGE_TOKEN_CONTRACT_ABI, BRIDGE_TOKEN_CONTRACT_BYTECODE, wallet) - const successful_gas_limit = "12345678"; - const contract = await contractFactory.deploy("name", - "symbol", wallet.address, - { - gasLimit: successful_gas_limit, - } - ) - await contract.waitForDeployment() - assert.notEqual(contract.target, undefined) - - const contractAddress = contract.target.toString() - - const code = await publicClient.getCode({ address: toViemAddress(contractAddress) }) - if (code === undefined) { - throw new Error("code not available") - } - assert.ok(code.length > 100) - assert.ok(code.includes("0x60806040523480156")) - }); -}); \ No newline at end of file diff --git a/contract-tests/test/eth.chain-id.test.ts b/contract-tests/test/eth.chain-id.test.ts deleted file mode 100644 index 2e1c18d3d4..0000000000 --- a/contract-tests/test/eth.chain-id.test.ts +++ /dev/null @@ -1,74 +0,0 @@ - -import * as assert from "assert"; -import * as chai from "chai"; - -import { getDevnetApi, waitForTransactionWithRetry, getRandomSubstrateKeypair } from "../src/substrate" -import { generateRandomEthWallet, getPublicClient } from "../src/utils"; -import { convertPublicKeyToSs58 } from "../src/address-utils" -import { ETH_LOCAL_URL } from "../src/config"; -import { devnet } from "@polkadot-api/descriptors" -import { getPolkadotSigner } from "polkadot-api/signer"; -import { PublicClient } from "viem"; -import { TypedApi } from "polkadot-api"; -import { forceSetBalanceToSs58Address, forceSetChainID } from "../src/subtensor"; - -describe("Test the EVM chain ID", () => { - // init eth part - const wallet = generateRandomEthWallet(); - let ethClient: PublicClient; - - // init substrate part - const keyPair = getRandomSubstrateKeypair(); - let api: TypedApi; - - // init other variable - const initChainId = 42; - - before(async () => { - // init variables got from await and async - ethClient = await getPublicClient(ETH_LOCAL_URL); - api = await getDevnetApi() - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(keyPair.publicKey)) - - }); - - it("EVM chain id update is ok", async () => { - let chainId = await ethClient.getChainId(); - // init chain id should be 42 - assert.equal(chainId, initChainId); - - const newChainId = BigInt(100) - await forceSetChainID(api, newChainId) - - chainId = await ethClient.getChainId(); - assert.equal(chainId, newChainId); - - await forceSetChainID(api, BigInt(initChainId)) - - chainId = await ethClient.getChainId(); - // back to original value for other tests. and we can run it repeatedly - assert.equal(chainId, initChainId); - - }); - - it("EVM chain id is the same, only sudo can change it.", async () => { - let chainId = await ethClient.getChainId(); - // init chain id should be 42 - assert.equal(chainId, initChainId); - - // invalide signer for set chain ID - let signer = getPolkadotSigner( - keyPair.publicKey, - "Sr25519", - keyPair.sign, - ) - - let tx = api.tx.AdminUtils.sudo_set_evm_chain_id({ chain_id: BigInt(100) }) - await waitForTransactionWithRetry(api, tx, signer) - - // extrinsic should be failed and chain ID not updated. - chainId = await ethClient.getChainId(); - assert.equal(chainId, 42); - - }); -}); \ No newline at end of file diff --git a/contract-tests/test/eth.incremental.deploy.test.ts b/contract-tests/test/eth.incremental.deploy.test.ts deleted file mode 100644 index 49571b508a..0000000000 --- a/contract-tests/test/eth.incremental.deploy.test.ts +++ /dev/null @@ -1,61 +0,0 @@ - - -import * as assert from "assert"; -import * as chai from "chai"; - -import { getDevnetApi } from "../src/substrate" -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; -import { ETH_LOCAL_URL } from "../src/config"; -import { devnet } from "@polkadot-api/descriptors" -import { PublicClient } from "viem"; -import { TypedApi } from "polkadot-api"; -import { INCREMENTAL_CONTRACT_ABI, INCREMENTAL_CONTRACT_BYTECODE } from "../src/contracts/incremental"; -import { toViemAddress } from "../src/address-utils"; -import { ethers } from "ethers" -import { disableWhiteListCheck, forceSetBalanceToEthAddress } from "../src/subtensor"; - -describe("incremental smart contract deployment", () => { - // init eth part - const wallet = generateRandomEthersWallet(); - let publicClient: PublicClient; - - // init substrate part - let api: TypedApi - - before(async () => { - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - - await forceSetBalanceToEthAddress(api, wallet.address) - await disableWhiteListCheck(api, true) - }); - - it("Can deploy incremental smart contract", async () => { - const contractFactory = new ethers.ContractFactory(INCREMENTAL_CONTRACT_ABI, INCREMENTAL_CONTRACT_BYTECODE, wallet) - const contract = await contractFactory.deploy() - await contract.waitForDeployment() - - const value = await publicClient.readContract({ - abi: INCREMENTAL_CONTRACT_ABI, - address: toViemAddress(contract.target.toString()), - functionName: "retrieve", - args: [] - }) - assert.equal(value, 0) - - const newValue = 1234 - - const deployContract = new ethers.Contract(contract.target.toString(), INCREMENTAL_CONTRACT_ABI, wallet) - const storeTx = await deployContract.store(newValue) - await storeTx.wait() - - const newValueAfterStore = await publicClient.readContract({ - abi: INCREMENTAL_CONTRACT_ABI, - address: toViemAddress(contract.target.toString()), - functionName: "retrieve", - args: [] - }) - - assert.equal(newValue, newValueAfterStore) - }); -}); diff --git a/contract-tests/test/eth.substrate-transfer.test.ts b/contract-tests/test/eth.substrate-transfer.test.ts deleted file mode 100644 index fc8073585c..0000000000 --- a/contract-tests/test/eth.substrate-transfer.test.ts +++ /dev/null @@ -1,407 +0,0 @@ -import * as assert from "assert"; - -import { getDevnetApi, waitForTransactionCompletion, getRandomSubstrateSigner, waitForTransactionWithRetry } from "../src/substrate" -import { getPublicClient } from "../src/utils"; -import { ETH_LOCAL_URL, IBALANCETRANSFER_ADDRESS, IBalanceTransferABI } from "../src/config"; -import { devnet, MultiAddress } from "@polkadot-api/descriptors" -import { PublicClient } from "viem"; -import { TypedApi, Binary, FixedSizeBinary } from "polkadot-api"; -import { generateRandomEthersWallet } from "../src/utils"; -import { tao, raoToEth, bigintToRao, compareEthBalanceWithTxFee } from "../src/balance-math"; -import { toViemAddress, convertPublicKeyToSs58, convertH160ToSS58, ss58ToH160, ss58ToEthAddress, ethAddressToH160 } from "../src/address-utils" -import { ethers } from "ethers" -import { estimateTransactionCost, getContract } from "../src/eth" - -import { WITHDRAW_CONTRACT_ABI, WITHDRAW_CONTRACT_BYTECODE } from "../src/contracts/withdraw" - -import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address, disableWhiteListCheck } from "../src/subtensor"; - -describe("Balance transfers between substrate and EVM", () => { - const gwei = BigInt("1000000000"); - // init eth part - const wallet = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - let publicClient: PublicClient; - const provider = new ethers.JsonRpcProvider(ETH_LOCAL_URL); - // init substrate part - const signer = getRandomSubstrateSigner(); - let api: TypedApi - - before(async () => { - - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - - await forceSetBalanceToEthAddress(api, wallet.address) - await forceSetBalanceToEthAddress(api, wallet2.address) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(signer.publicKey)) - await disableWhiteListCheck(api, true) - }); - - it("Can transfer token from EVM to EVM", async () => { - const senderBalance = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - const receiverBalance = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - const transferBalance = raoToEth(tao(1)) - const tx = { - to: wallet2.address, - value: transferBalance.toString() - } - const txFee = await estimateTransactionCost(provider, tx) - - const txResponse = await wallet.sendTransaction(tx) - await txResponse.wait(); - - - const senderBalanceAfterTransfer = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - const receiverBalanceAfterTranser = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - - assert.equal(senderBalanceAfterTransfer, senderBalance - transferBalance - txFee) - assert.equal(receiverBalance, receiverBalanceAfterTranser - transferBalance) - }); - - it("Can transfer token from Substrate to EVM", async () => { - const ss58Address = convertH160ToSS58(wallet.address) - const senderBalance = (await api.query.System.Account.getValue(ss58Address)).data.free - const receiverBalance = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - const transferBalance = tao(1) - - const tx = api.tx.Balances.transfer_keep_alive({ value: transferBalance, dest: MultiAddress.Id(ss58Address) }) - await waitForTransactionWithRetry(api, tx, signer) - - const senderBalanceAfterTransfer = (await api.query.System.Account.getValue(ss58Address)).data.free - const receiverBalanceAfterTranser = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - - assert.equal(senderBalanceAfterTransfer, senderBalance + transferBalance) - assert.equal(receiverBalance, receiverBalanceAfterTranser - raoToEth(transferBalance)) - }); - - it("Can transfer token from EVM to Substrate", async () => { - const contract = getContract(IBALANCETRANSFER_ADDRESS, IBalanceTransferABI, wallet) - const senderBalance = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - const receiverBalance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(signer.publicKey))).data.free - const transferBalance = raoToEth(tao(1)) - - const tx = await contract.transfer(signer.publicKey, { value: transferBalance.toString() }) - await tx.wait() - - - const senderBalanceAfterTransfer = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - const receiverBalanceAfterTranser = (await api.query.System.Account.getValue(convertPublicKeyToSs58(signer.publicKey))).data.free - - compareEthBalanceWithTxFee(senderBalanceAfterTransfer, senderBalance - transferBalance) - assert.equal(receiverBalance, receiverBalanceAfterTranser - tao(1)) - }); - - it("Transfer from EVM to substrate using evm::withdraw", async () => { - const ss58Address = convertPublicKeyToSs58(signer.publicKey) - const senderBalance = (await api.query.System.Account.getValue(ss58Address)).data.free - const ethAddresss = ss58ToH160(ss58Address); - - // transfer token to mirror eth address - const ethTransfer = { - to: ss58ToEthAddress(ss58Address), - value: raoToEth(tao(2)).toString() - } - - const txResponse = await wallet.sendTransaction(ethTransfer) - await txResponse.wait(); - - const tx = api.tx.EVM.withdraw({ address: ethAddresss, value: tao(1) }) - const txFee = (await tx.getPaymentInfo(ss58Address)).partial_fee - - await waitForTransactionWithRetry(api, tx, signer) - - const senderBalanceAfterWithdraw = (await api.query.System.Account.getValue(ss58Address)).data.free - - assert.equal(senderBalance, senderBalanceAfterWithdraw - tao(1) + txFee) - }); - - it("Transfer from EVM to substrate using evm::call", async () => { - const ss58Address = convertPublicKeyToSs58(signer.publicKey) - const ethAddresss = ss58ToH160(ss58Address); - - // transfer token to mirror eth address - const ethTransfer = { - to: ss58ToEthAddress(ss58Address), - value: raoToEth(tao(2)).toString() - } - - const txResponse = await wallet.sendTransaction(ethTransfer) - await txResponse.wait(); - - const source: FixedSizeBinary<20> = ethAddresss; - const target = ethAddressToH160(wallet.address) - const receiverBalance = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - - // all these parameter value are tricky, any change could make the call failed - const tx = api.tx.EVM.call({ - source: source, - target: target, - // it is U256 in the extrinsic. - value: [raoToEth(tao(1)), tao(0), tao(0), tao(0)], - gas_limit: BigInt(1000000), - // it is U256 in the extrinsic. - max_fee_per_gas: [BigInt(10e9), BigInt(0), BigInt(0), BigInt(0)], - max_priority_fee_per_gas: undefined, - input: Binary.fromText(""), - nonce: undefined, - access_list: [], - authorization_list: [] - }) - // txFee not accurate - const txFee = (await tx.getPaymentInfo(ss58Address)).partial_fee - - await waitForTransactionWithRetry(api, tx, signer) - - const receiverBalanceAfterCall = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - assert.equal(receiverBalanceAfterCall, receiverBalance + raoToEth(tao(1))) - }); - - it("Forward value in smart contract", async () => { - - - const contractFactory = new ethers.ContractFactory(WITHDRAW_CONTRACT_ABI, WITHDRAW_CONTRACT_BYTECODE, wallet) - const contract = await contractFactory.deploy() - await contract.waitForDeployment() - - const code = await publicClient.getCode({ address: toViemAddress(contract.target.toString()) }) - if (code === undefined) { - throw new Error("code length is wrong for deployed contract") - } - assert.ok(code.length > 100) - - // transfer 2 TAO to contract - const ethTransfer = { - to: contract.target.toString(), - value: raoToEth(tao(2)).toString() - } - - const txResponse = await wallet.sendTransaction(ethTransfer) - await txResponse.wait(); - - const contractBalance = await publicClient.getBalance({ address: toViemAddress(contract.target.toString()) }) - const callerBalance = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - - const contractForCall = new ethers.Contract(contract.target.toString(), WITHDRAW_CONTRACT_ABI, wallet) - - const withdrawTx = await contractForCall.withdraw( - raoToEth(tao(1)).toString() - ); - - await withdrawTx.wait(); - - const contractBalanceAfterWithdraw = await publicClient.getBalance({ address: toViemAddress(contract.target.toString()) }) - const callerBalanceAfterWithdraw = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - - compareEthBalanceWithTxFee(callerBalanceAfterWithdraw, callerBalance + raoToEth(tao(1))) - assert.equal(contractBalance, contractBalanceAfterWithdraw + raoToEth(tao(1))) - }); - - it("Transfer full balance", async () => { - const ethBalance = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - const receiverBalance = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - const tx = { - to: wallet2.address, - value: ethBalance.toString(), - }; - const txPrice = await estimateTransactionCost(provider, tx); - const finalTx = { - to: wallet2.address, - value: (ethBalance - txPrice).toString(), - }; - try { - // transfer should be failed since substrate requires existial balance to keep account - const txResponse = await wallet.sendTransaction(finalTx) - await txResponse.wait(); - } catch (error) { - if (error instanceof Error) { - assert.equal((error as any).code, "INSUFFICIENT_FUNDS") - assert.equal(error.toString().includes("insufficient funds"), true) - } - } - - const receiverBalanceAfterTransfer = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - assert.equal(receiverBalance, receiverBalanceAfterTransfer) - }) - - it("Transfer more than owned balance should fail", async () => { - const ethBalance = await publicClient.getBalance({ address: toViemAddress(wallet.address) }) - const receiverBalance = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - const tx = { - to: wallet2.address, - value: (ethBalance + raoToEth(tao(1))).toString(), - }; - - try { - // transfer should be failed since substrate requires existial balance to keep account - const txResponse = await wallet.sendTransaction(tx) - await txResponse.wait(); - } catch (error) { - if (error instanceof Error) { - assert.equal((error as any).code, "INSUFFICIENT_FUNDS") - assert.equal(error.toString().includes("insufficient funds"), true) - } - } - - const receiverBalanceAfterTransfer = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - assert.equal(receiverBalance, receiverBalanceAfterTransfer) - }); - - it("Transfer more than u64::max in substrate equivalent should receive error response", async () => { - const receiverBalance = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - try { - const tx = { - to: wallet2.address, - value: raoToEth(BigInt(2) ** BigInt(64)).toString(), - }; - // transfer should be failed since substrate requires existial balance to keep account - const txResponse = await wallet.sendTransaction(tx) - await txResponse.wait(); - } catch (error) { - if (error instanceof Error) { - assert.equal((error as any).code, "INSUFFICIENT_FUNDS") - assert.equal(error.toString().includes("insufficient funds"), true) - } - } - - const contract = getContract(IBALANCETRANSFER_ADDRESS, IBalanceTransferABI, wallet) - try { - const tx = await contract.transfer(signer.publicKey, { value: raoToEth(BigInt(2) ** BigInt(64)).toString() }) - await tx.await() - } catch (error) { - if (error instanceof Error) { - console.log(error.toString()) - assert.equal(error.toString().includes("revert data"), true) - } - } - - try { - const dest = convertH160ToSS58(wallet2.address) - const tx = api.tx.Balances.transfer_keep_alive({ value: bigintToRao(BigInt(2) ** BigInt(64)), dest: MultiAddress.Id(dest) }) - await waitForTransactionCompletion(api, tx, signer) - .then(() => { }) - .catch((error) => { console.log(`transaction error ${error}`) }); - } catch (error) { - if (error instanceof Error) { - console.log(error.toString()) - assert.equal(error.toString().includes("Cannot convert"), true) - } - } - - try { - const dest = ethAddressToH160(wallet2.address) - const tx = api.tx.EVM.withdraw({ value: bigintToRao(BigInt(2) ** BigInt(64)), address: dest }) - await waitForTransactionCompletion(api, tx, signer) - .then(() => { }) - .catch((error) => { console.log(`transaction error ${error}`) }); - } catch (error) { - if (error instanceof Error) { - assert.equal(error.toString().includes("Cannot convert"), true) - } - } - - try { - const source = ethAddressToH160(wallet.address) - const target = ethAddressToH160(wallet2.address) - const tx = api.tx.EVM.call({ - source: source, - target: target, - // it is U256 in the extrinsic, the value is more than u64::MAX - value: [raoToEth(tao(1)), tao(0), tao(0), tao(1)], - gas_limit: BigInt(1000000), - // it is U256 in the extrinsic. - max_fee_per_gas: [BigInt(10e9), BigInt(0), BigInt(0), BigInt(0)], - max_priority_fee_per_gas: undefined, - input: Binary.fromText(""), - nonce: undefined, - access_list: [], - authorization_list: [] - }) - await waitForTransactionCompletion(api, tx, signer) - .then(() => { }) - .catch((error) => { console.log(`transaction error ${error}`) }); - } catch (error) { - if (error instanceof Error) { - console.log(error.toString()) - assert.equal((error as any).code, "INSUFFICIENT_FUNDS") - assert.equal(error.toString().includes("insufficient funds"), true) - } - } - - const receiverBalanceAfterTransfer = await publicClient.getBalance({ address: toViemAddress(wallet2.address) }) - assert.equal(receiverBalance, receiverBalanceAfterTransfer) - }); - - it("Gas price should be 10 GWei", async () => { - const feeData = await provider.getFeeData(); - assert.equal(feeData.gasPrice, BigInt(10000000000)); - }); - - - it("max_fee_per_gas and max_priority_fee_per_gas affect transaction fee properly", async () => { - - const testCases = [ - [10, 0, 21000 * 10 * 1e9], - [10, 10, 21000 * 10 * 1e9], - [11, 0, 21000 * 10 * 1e9], - // max_priority_fee_per_gas is disabled - // [11, 1, (21000 * 10 + 21000) * 1e9], - // [11, 2, (21000 * 10 + 21000) * 1e9], - ]; - - for (let i in testCases) { - const tc = testCases[i]; - const actualFee = await transferAndGetFee( - wallet, wallet2, publicClient, - gwei * BigInt(tc[0]), - gwei * BigInt(tc[1]) - ); - assert.equal(actualFee, BigInt(tc[2])) - } - }); - - it("Low max_fee_per_gas gets transaction rejected", async () => { - try { - await transferAndGetFee(wallet, wallet2, publicClient, gwei * BigInt(9), BigInt(0)) - } catch (error) { - if (error instanceof Error) { - console.log(error.toString()) - assert.equal(error.toString().includes("gas price less than block base fee"), true) - } - } - }); - - it("max_fee_per_gas lower than max_priority_fee_per_gas gets transaction rejected", async () => { - try { - await transferAndGetFee(wallet, wallet2, publicClient, gwei * BigInt(10), gwei * BigInt(11)) - } catch (error) { - if (error instanceof Error) { - assert.equal(error.toString().includes("priorityFee cannot be more than maxFee"), true) - } - } - }); -}); - -async function transferAndGetFee(wallet: ethers.Wallet, wallet2: ethers.Wallet, client: PublicClient, max_fee_per_gas: BigInt, max_priority_fee_per_gas: BigInt) { - - const ethBalanceBefore = await client.getBalance({ address: toViemAddress(wallet.address) }) - // Send TAO - const tx = { - to: wallet2.address, - value: raoToEth(tao(1)).toString(), - // EIP-1559 transaction parameters - maxPriorityFeePerGas: max_priority_fee_per_gas.toString(), - maxFeePerGas: max_fee_per_gas.toString(), - gasLimit: 21000, - }; - - // Send the transaction - const txResponse = await wallet.sendTransaction(tx); - await txResponse.wait() - - // Check balances - const ethBalanceAfter = await client.getBalance({ address: toViemAddress(wallet.address) }) - const fee = ethBalanceBefore - ethBalanceAfter - raoToEth(tao(1)) - - return fee; -} \ No newline at end of file diff --git a/contract-tests/test/leasing.precompile.test.ts b/contract-tests/test/leasing.precompile.test.ts deleted file mode 100644 index 7205a8ed4a..0000000000 --- a/contract-tests/test/leasing.precompile.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as assert from "assert"; - -import { ethers } from "ethers"; -import { TypedApi } from "polkadot-api"; -import { devnet } from "@polkadot-api/descriptors"; -import { ICROWDLOAN_ADDRESS, ICrowdloanABI } from "../src/contracts/crowdloan"; -import { ILEASING_ADDRESS, ILeasingABI } from "../src/contracts/leasing"; -import { INEURON_ADDRESS, INeuronABI } from "../src/contracts/neuron"; -import { - convertH160ToPublicKey, - convertH160ToSS58, -} from "../src/address-utils"; -import { generateRandomEthersWallet } from "../src/utils"; -import { getDevnetApi, waitForFinalizedBlock } from "../src/substrate"; -import { forceSetBalanceToEthAddress } from "../src/subtensor"; - -describe("Leasing precompile E2E smoke", () => { - let api: TypedApi; - let wallet1: ethers.Wallet; - let wallet2: ethers.Wallet; - let leaseContract: ethers.Contract; - let crowdloanContract: ethers.Contract; - let neuronContract: ethers.Contract; - - beforeEach(async () => { - api = await getDevnetApi(); - - wallet1 = generateRandomEthersWallet(); - wallet2 = generateRandomEthersWallet(); - leaseContract = new ethers.Contract(ILEASING_ADDRESS, ILeasingABI, wallet1); - crowdloanContract = new ethers.Contract( - ICROWDLOAN_ADDRESS, - ICrowdloanABI, - wallet1, - ); - neuronContract = new ethers.Contract(INEURON_ADDRESS, INeuronABI, wallet1); - - await forceSetBalanceToEthAddress(api, wallet1.address); - await forceSetBalanceToEthAddress(api, wallet2.address); - }); - - it("creates, reads, and terminates a lease through RPC", async () => { - const hotkey = generateRandomEthersWallet(); - let tx = await neuronContract.burnedRegister( - 1, - convertH160ToPublicKey(hotkey.address), - ); - await tx.wait(); - - const nextCrowdloanId = - await api.query.Crowdloan.NextCrowdloanId.getValue(); - const crowdloanDeposit = BigInt(100_000_000_000); - const crowdloanMinContribution = BigInt(1_000_000_000); - const crowdloanCap = - (await api.query.SubtensorModule.NetworkLastLockCost.getValue()) * - BigInt(2); - const crowdloanEnd = (await api.query.System.Number.getValue()) + 100; - const leasingEmissionsShare = 15; - const leasingEndBlock = (await api.query.System.Number.getValue()) + 200; - - tx = await leaseContract.createLeaseCrowdloan( - crowdloanDeposit, - crowdloanMinContribution, - crowdloanCap, - crowdloanEnd, - leasingEmissionsShare, - true, - leasingEndBlock, - ); - await tx.wait(); - - const crowdloanContract2 = new ethers.Contract( - ICROWDLOAN_ADDRESS, - ICrowdloanABI, - wallet2, - ); - tx = await crowdloanContract2.contribute( - nextCrowdloanId, - crowdloanCap - crowdloanDeposit, - ); - await tx.wait(); - - await waitForFinalizedBlock(api, crowdloanEnd); - - const nextLeaseId = - await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); - tx = await crowdloanContract.finalize(nextCrowdloanId); - await tx.wait(); - - const lease = - await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); - assert.ok(lease); - assert.equal(lease.beneficiary, convertH160ToSS58(wallet1.address)); - assert.equal(lease.emissions_share, leasingEmissionsShare); - assert.equal(lease.end_block, leasingEndBlock); - - const leaseInfo = await leaseContract.getLease(nextLeaseId); - assert.equal(leaseInfo[3], lease.emissions_share); - assert.equal(leaseInfo[4], true); - assert.equal(leaseInfo[5], lease.end_block); - assert.equal(leaseInfo[6], lease.netuid); - assert.equal(leaseInfo[7], lease.cost); - - const leaseId = await leaseContract.getLeaseIdForSubnet(lease.netuid); - assert.equal(leaseId, nextLeaseId); - - const beneficiaryShare = await leaseContract.getContributorShare( - nextLeaseId, - convertH160ToPublicKey(wallet1.address), - ); - assert.deepEqual(beneficiaryShare, [BigInt(0), BigInt(0)]); - - const contributorShare = await leaseContract.getContributorShare( - nextLeaseId, - convertH160ToPublicKey(wallet2.address), - ); - assert.notDeepEqual(contributorShare, [BigInt(0), BigInt(0)]); - - await waitForFinalizedBlock(api, leasingEndBlock); - - tx = await leaseContract.terminateLease( - nextLeaseId, - convertH160ToPublicKey(hotkey.address), - ); - await tx.wait(); - - const terminatedLease = - await api.query.SubtensorModule.SubnetLeases.getValue(nextLeaseId); - assert.equal(terminatedLease, undefined); - - const ownerColdkey = await api.query.SubtensorModule.SubnetOwner.getValue( - lease.netuid, - ); - const ownerHotkey = - await api.query.SubtensorModule.SubnetOwnerHotkey.getValue(lease.netuid); - assert.equal(ownerColdkey, convertH160ToSS58(wallet1.address)); - assert.equal(ownerHotkey, convertH160ToSS58(hotkey.address)); - }); -}); diff --git a/contract-tests/test/precompileGas.test.ts b/contract-tests/test/precompileGas.test.ts deleted file mode 100644 index 120d7fdd79..0000000000 --- a/contract-tests/test/precompileGas.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import * as assert from "assert"; -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; -import { ETH_LOCAL_URL } from "../src/config"; -import { getBalance, getDevnetApi } from "../src/substrate"; -import { forceSetBalanceToEthAddress } from "../src/subtensor"; -import { PrecompileGas_CONTRACT_ABI, PrecompileGas_CONTRACT_BYTECODE } from "../src/contracts/precompileGas"; -import { ethers } from "ethers"; -import { TypedApi } from "polkadot-api"; -import { devnet } from "@polkadot-api/descriptors"; -import { disableWhiteListCheck } from "../src/subtensor"; -import { convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils"; - -describe("SR25519 ED25519 Precompile Gas Test", () => { - const wallet = generateRandomEthersWallet(); - let api: TypedApi; - - // scope of precompile gas usage for sr25519 and ed25519 - const minPrecompileGas = BigInt(6000); - const maxPrecompileGas = BigInt(10000); - - before(async () => { - api = await getDevnetApi(); - await forceSetBalanceToEthAddress(api, wallet.address); - await disableWhiteListCheck(api, true); - }); - - it("Can deploy and call attackHardcoded", async () => { - const fee = await api.query.BaseFee.BaseFeePerGas.getValue() - assert.ok(fee[0] > 1000000000); - const baseFee = BigInt(fee[0]) / BigInt(1000000000); - console.log("Base fee per gas:", baseFee); - - const contractFactory = new ethers.ContractFactory(PrecompileGas_CONTRACT_ABI, PrecompileGas_CONTRACT_BYTECODE, wallet); - const contractDeploy = await contractFactory.deploy(); - - const result = await contractDeploy.waitForDeployment(); - console.log("Contract deployed to:", result.target); - - - let oneIterationGas = BigInt(0); - - for (const iter of [1, 11, 101]) { - const balanceBefore = await getBalance(api, convertH160ToSS58(wallet.address)); - const contract = new ethers.Contract(result.target, PrecompileGas_CONTRACT_ABI, wallet); - const iterations = iter; - const tx = await contract.callED25519(iterations) - await tx.wait() - - const balanceAfter = await getBalance(api, convertH160ToSS58(wallet.address)); - assert.ok(balanceAfter < balanceBefore); - - const usedGas = balanceBefore - balanceAfter; - if (iterations === 1) { - oneIterationGas = usedGas; - continue; - } - - assert.ok(usedGas >= oneIterationGas); - - const precompileUsedGas = BigInt(usedGas - oneIterationGas); - assert.ok(precompileUsedGas >= minPrecompileGas * BigInt(iterations - 1) * baseFee); - assert.ok(precompileUsedGas <= maxPrecompileGas * BigInt(iterations - 1) * baseFee); - } - - for (const iter of [1, 11, 101]) { - const balanceBefore = await getBalance(api, convertH160ToSS58(wallet.address)); - const contract = new ethers.Contract(result.target, PrecompileGas_CONTRACT_ABI, wallet); - const iterations = iter; - const tx = await contract.callSR25519(iterations) - await tx.wait() - - const balanceAfter = await getBalance(api, convertH160ToSS58(wallet.address)); - assert.ok(balanceAfter < balanceBefore); - - const usedGas = balanceBefore - balanceAfter; - if (iterations === 1) { - oneIterationGas = usedGas; - continue; - } - - assert.ok(usedGas >= oneIterationGas); - - const precompileUsedGas = BigInt(usedGas - oneIterationGas); - assert.ok(precompileUsedGas >= minPrecompileGas * BigInt(iterations - 1) * baseFee); - assert.ok(precompileUsedGas <= maxPrecompileGas * BigInt(iterations - 1) * baseFee); - } - }); -}); diff --git a/contract-tests/test/precompileWrapper.direct-call.test.ts b/contract-tests/test/precompileWrapper.direct-call.test.ts deleted file mode 100644 index fa1354f3ce..0000000000 --- a/contract-tests/test/precompileWrapper.direct-call.test.ts +++ /dev/null @@ -1,408 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair, getBalance, getSignerFromKeypair } from "../src/substrate"; -import { devnet } from "@polkadot-api/descriptors"; -import { TypedApi, Binary } from "polkadot-api"; -import { convertH160ToSS58, convertPublicKeyToSs58, convertH160ToPublicKey } from "../src/address-utils"; -import { tao, raoToEth } from "../src/balance-math"; -import { - forceSetBalanceToSs58Address, - addNewSubnetwork, - startCall, - disableWhiteListCheck, - forceSetBalanceToEthAddress, - getStake, - -} from "../src/subtensor"; -import { ethers } from "ethers"; -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; -import { PRECOMPILE_WRAPPER_ABI, PRECOMPILE_WRAPPER_BYTECODE } from "../src/contracts/precompileWrapper"; -import { ETH_LOCAL_URL } from "../src/config"; -import { PublicClient } from "viem"; -import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" - -describe("PrecompileWrapper - Direct Call Tests", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const wallet1 = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - - let api: TypedApi; - let publicClient: PublicClient; - let wrapperContract: ethers.Contract; - let wrapperAddress: string; - let netuid: number; - - before(async () => { - api = await getDevnetApi(); - publicClient = await getPublicClient(ETH_LOCAL_URL); - await disableWhiteListCheck(api, true); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)); - await forceSetBalanceToEthAddress(api, wallet1.address); - await forceSetBalanceToEthAddress(api, wallet2.address); - await addNewSubnetwork(api, hotkey, coldkey); - netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - await startCall(api, netuid, coldkey); - - const factory = new ethers.ContractFactory( - PRECOMPILE_WRAPPER_ABI, - PRECOMPILE_WRAPPER_BYTECODE, - wallet1 - ); - const deployContract = await factory.deploy(); - await deployContract.waitForDeployment(); - wrapperAddress = await deployContract.getAddress(); - await forceSetBalanceToEthAddress(api, wrapperAddress); - - console.log("Wrapper contract deployed at:", wrapperAddress); - console.log("Testing in subnet:", netuid); - - wrapperContract = new ethers.Contract(wrapperAddress, PRECOMPILE_WRAPPER_ABI, wallet1); - }); - - describe("Balance Transfer Precompile Direct Calls", () => { - it("Should transfer balance via wrapper", async () => { - const keypair = getRandomSubstrateKeypair(); - const transferAmount = raoToEth(tao(1)); - - // Transfer via wrapper - const transferTx = await wrapperContract.transfer(keypair.publicKey, { value: transferAmount.toString() }); - await transferTx.wait(); - - const balance = await getBalance(api, convertPublicKeyToSs58(keypair.publicKey)); - assert.ok(balance >= tao(1), "Balance should be transferred"); - }); - }); - - describe("Metagraph Precompile Direct Calls", () => { - it("Should get UID count via wrapper", async () => { - const uidCountViaWrapper = await wrapperContract.getUidCount(netuid); - assert.ok(uidCountViaWrapper !== undefined, "UID count should be not undefined"); - }); - }); - - describe("Subnet Precompile Direct Calls", () => { - it("Should get serving rate limit via wrapper", async () => { - const rateLimitViaWrapper = await wrapperContract.getServingRateLimit(netuid); - - assert.ok(rateLimitViaWrapper !== undefined, "Rate limit should be not undefined"); - }); - - it("Should register network with details via wrapper", async () => { - const newHotkey = getRandomSubstrateKeypair(); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(newHotkey.publicKey)); - - const totalNetworksBefore = await api.query.SubtensorModule.TotalNetworks.getValue(); - - const registerTx = await wrapperContract.registerNetworkWithDetails( - newHotkey.publicKey, - "Test Subnet", - "https://github.com/test/repo", - "test@example.com", - "https://test.example.com", - "test#1234", - "Test description", - "Additional info", - { value: raoToEth(tao(100)).toString() } - ); - await registerTx.wait(); - - const totalNetworksAfter = await api.query.SubtensorModule.TotalNetworks.getValue(); - const beforeValue = typeof totalNetworksBefore === 'bigint' ? totalNetworksBefore : BigInt(totalNetworksBefore); - assert.equal(totalNetworksAfter, beforeValue + BigInt(1), "Network should be registered"); - }); - }); - - describe("Neuron Precompile Direct Calls", () => { - it("Should register neuron via wrapper", async () => { - const newHotkey = getRandomSubstrateKeypair(); - const newColdkey = getRandomSubstrateKeypair(); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(newHotkey.publicKey)); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(newColdkey.publicKey)); - - // Use a reasonable burn amount (100 TAO) - const burnAmount = tao(100); - - const registerTx = await wrapperContract.burnedRegister( - netuid, - newHotkey.publicKey, - { value: raoToEth(burnAmount).toString() } - ); - await registerTx.wait(); - - const uid = await api.query.SubtensorModule.Uids.getValue(netuid, convertPublicKeyToSs58(newHotkey.publicKey)); - assert.ok(uid !== undefined, "Neuron should be registered"); - }); - }); - - describe("Staking Precompile Direct Calls", () => { - it("Should get total coldkey stake via wrapper", async () => { - const stakeViaWrapper = await wrapperContract.getTotalColdkeyStake(coldkey.publicKey); - assert.ok(stakeViaWrapper !== undefined, "Total coldkey stake should be not undefined"); - }); - - it("Should get total hotkey stake via wrapper", async () => { - const stakeViaWrapper = await wrapperContract.getTotalHotkeyStake(hotkey.publicKey); - assert.ok(stakeViaWrapper !== undefined, "Total hotkey stake should be not undefined"); - }); - - it("Should add stake via wrapper", async () => { - const stakeAmount = tao(2); - const stakeBefore = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - convertH160ToSS58(wrapperAddress), - netuid - ); - - const addStakeTx = await wrapperContract.addStake( - hotkey.publicKey, - stakeAmount.toString(), - netuid, - { value: raoToEth(stakeAmount).toString() } - ); - await addStakeTx.wait(); - - const stakeAfter = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - convertH160ToSS58(wrapperAddress), - netuid - ); - assert.ok(stakeAfter > stakeBefore, "Stake should be increased"); - }); - - it("Should remove stake via wrapper", async () => { - const removeAmount = tao(1); - const stakeBefore = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - convertH160ToSS58(wrapperAddress), - netuid - ); - - const removeStakeTx = await wrapperContract.removeStake( - hotkey.publicKey, - removeAmount.toString(), - netuid - ); - await removeStakeTx.wait(); - - const stakeAfter = await getStake( - api, - convertPublicKeyToSs58(hotkey.publicKey), - convertH160ToSS58(wrapperAddress), - netuid - ); - assert.ok(stakeAfter < stakeBefore, "Stake should be decreased"); - }); - }); - - describe("UID Lookup Precompile Direct Calls", () => { - it("Should lookup UID via wrapper", async () => { - const evmAddress = wallet1.address; - const limit = 10; - const lookupViaWrapper = await wrapperContract.uidLookup(netuid, evmAddress, limit); - - assert.ok(Array.isArray(lookupViaWrapper), "Lookup should return an array"); - }); - }); - - describe("Alpha Precompile Direct Calls", () => { - it("Should get alpha price via wrapper", async () => { - const priceViaWrapper = await wrapperContract.getAlphaPrice(netuid); - assert.ok(priceViaWrapper !== undefined, "Alpha price should be not undefined"); - }); - }); - - describe("Crowdloan Precompile Direct Calls", () => { - it("Should get crowdloan via wrapper", async () => { - // First create a crowdloan via substrate - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const end = await api.query.System.Number.getValue() + 100; - const deposit = BigInt(15_000_000_000); // 15 TAO - const minContribution = BigInt(1_000_000_000); // 1 TAO - const cap = BigInt(100_000_000_000); // 100 TAO - - const signer = getSignerFromKeypair(coldkey); - await api.tx.Crowdloan.create({ - deposit, - min_contribution: minContribution, - cap, - end, - target_address: undefined, - call: api.tx.System.remark({ remark: Binary.fromText("test") }).decodedCall - }).signAndSubmit(signer); - - // Wait a bit for the transaction to be included - await new Promise(resolve => setTimeout(resolve, 2000)); - - const crowdloanViaWrapper = await wrapperContract.getCrowdloan(nextId); - - assert.ok(crowdloanViaWrapper !== undefined, "Crowdloan should be not undefined"); - }); - - it("Should get contribution via wrapper", async () => { - const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const contributionViaWrapper = await wrapperContract.getContribution(nextId - 1, coldkey.publicKey); - - assert.ok(contributionViaWrapper !== undefined, "Contribution should be not undefined"); - }); - - it("Should create crowdloan via wrapper", async () => { - const deposit = BigInt(20_000_000_000); // 20 TAO - const minContribution = BigInt(2_000_000_000); // 2 TAO - const cap = BigInt(200_000_000_000); // 200 TAO - const end = Number(await api.query.System.Number.getValue()) + 100; - const targetAddress = wallet2.address; - - const nextIdBefore = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - const createTx = await wrapperContract.createCrowdloan( - deposit.toString(), - minContribution.toString(), - cap.toString(), - end, - targetAddress, - { value: raoToEth(deposit).toString() } - ); - await createTx.wait(); - - const nextIdAfter = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const beforeId = typeof nextIdBefore === 'bigint' ? nextIdBefore : BigInt(nextIdBefore); - assert.equal(nextIdAfter, beforeId + BigInt(1), "Crowdloan should be created"); - }); - }); - - - describe("Leasing Precompile Direct Calls", () => { - it("Should get contributor share via wrapper", async () => { - // First create a lease crowdloan - const nextCrowdloanId = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const crowdloanDeposit = BigInt(100_000_000_000); // 100 TAO - const networkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue(); - const lockCostValue = typeof networkLastLockCost === 'bigint' ? networkLastLockCost : BigInt(networkLastLockCost); - const crowdloanCap = lockCostValue * BigInt(2); - const currentBlock = await api.query.System.Number.getValue(); - const crowdloanEnd = currentBlock + 100; - const leasingEmissionsShare = 15; - const leasingEndBlock = currentBlock + 300; - - const signer = getSignerFromKeypair(coldkey); - await api.tx.Crowdloan.create({ - deposit: crowdloanDeposit, - min_contribution: BigInt(1_000_000_000), - cap: crowdloanCap, - end: crowdloanEnd, - target_address: undefined, - call: api.tx.SubtensorModule.register_leased_network({ - emissions_share: leasingEmissionsShare, - end_block: leasingEndBlock, - }).decodedCall - }).signAndSubmit(signer); - - await new Promise(resolve => setTimeout(resolve, 2000)); - - const nextLeaseId = await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); - - // Get contributor share - const shareViaWrapper = await wrapperContract.getContributorShare(nextLeaseId, coldkey.publicKey); - - assert.ok(shareViaWrapper !== undefined, "Share should be not undefined"); - - }); - - it("Should create lease crowdloan via wrapper", async () => { - const crowdloanDeposit = BigInt(100_000_000_000); // 100 TAO - const crowdloanMinContribution = BigInt(1_000_000_000); // 1 TAO - const networkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue(); - const lockCostValue = typeof networkLastLockCost === 'bigint' ? networkLastLockCost : BigInt(networkLastLockCost); - const crowdloanCap = lockCostValue * BigInt(2); - const currentBlock = await api.query.System.Number.getValue(); - const currentBlockValue = typeof currentBlock === 'bigint' ? Number(currentBlock) : currentBlock; - const crowdloanEnd = currentBlockValue + 100; - const leasingEmissionsShare = 15; - const hasLeasingEndBlock = true; - const leasingEndBlock = currentBlockValue + 300; - - const nextCrowdloanIdBefore = await api.query.Crowdloan.NextCrowdloanId.getValue(); - - const createTx = await wrapperContract.createLeaseCrowdloan( - crowdloanDeposit.toString(), - crowdloanMinContribution.toString(), - crowdloanCap.toString(), - crowdloanEnd, - leasingEmissionsShare, - hasLeasingEndBlock, - leasingEndBlock, - { value: raoToEth(crowdloanDeposit).toString() } - ); - await createTx.wait(); - - const nextCrowdloanIdAfter = await api.query.Crowdloan.NextCrowdloanId.getValue(); - const beforeId = typeof nextCrowdloanIdBefore === 'bigint' ? nextCrowdloanIdBefore : BigInt(nextCrowdloanIdBefore); - assert.equal(nextCrowdloanIdAfter, beforeId + BigInt(1), "Lease crowdloan should be created"); - }); - }); - - - describe("Proxy Precompile Direct Calls", () => { - it("Should get proxies via wrapper", async () => { - const accountKey = convertH160ToPublicKey(wallet1.address); - const proxiesViaWrapper = await wrapperContract.getProxies(accountKey); - - assert.ok(proxiesViaWrapper !== undefined, "Proxies should be not undefined"); - assert.ok(Array.isArray(proxiesViaWrapper), "Proxies should be an array"); - }); - it("Should add proxy via wrapper", async () => { - const delegate = getRandomSubstrateKeypair(); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(delegate.publicKey)); - const delegateKey = delegate.publicKey; - const proxyType = 0; - const delay = 0; - - const proxiesBefore = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(wrapperAddress)); - - const addProxyTx = await wrapperContract.addProxy(delegateKey, proxyType, delay); - await addProxyTx.wait(); - - const proxiesAfter = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(wrapperAddress)); - assert.ok(proxiesAfter[0].length > proxiesBefore[0].length, "Proxy should be added"); - }); - - it("Should proxy call via wrapper", async () => { - const proxyType = 0; - const delay = 0; - - const proxyContract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, wallet1); - const addProxyTx = await proxyContract.addProxy(convertH160ToPublicKey(wrapperAddress), proxyType, delay); - await addProxyTx.wait(); - - // Create a simple call (remark) - const remarkCall = api.tx.System.remark({ remark: Binary.fromText("") }); - - const callData = await remarkCall.getEncodedData(); - const data = callData.asBytes(); - - const proxyCallTx = await wrapperContract.proxyCall( - convertH160ToPublicKey(wallet1.address), - [proxyType], - [...data] - ); - await proxyCallTx.wait(); - - // Verify the call was executed (no error means success) - assert.ok(proxyCallTx, "Proxy call should succeed"); - }); - }); - - describe("Address Mapping Precompile Direct Calls", () => { - it("Should map address via wrapper", async () => { - const testAddress = wallet1.address; - const mappedViaWrapper = await wrapperContract.addressMapping(testAddress); - - assert.ok(mappedViaWrapper !== undefined, "Mapped address should be not undefined"); - assert.ok(mappedViaWrapper !== "0x0000000000000000000000000000000000000000000000000000000000000000", "Mapped address should not be zero"); - }); - }); -}); diff --git a/contract-tests/test/pure-proxy.precompile.test.ts b/contract-tests/test/pure-proxy.precompile.test.ts deleted file mode 100644 index f893b6d77a..0000000000 --- a/contract-tests/test/pure-proxy.precompile.test.ts +++ /dev/null @@ -1,210 +0,0 @@ -import * as assert from "assert"; - -import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { generateRandomEthersWallet } from "../src/utils"; -import { devnet, MultiAddress } from "@polkadot-api/descriptors" -import { PolkadotSigner, TypedApi } from "polkadot-api"; -import { convertH160ToPublicKey, convertH160ToSS58, convertPublicKeyToSs58 } from "../src/address-utils" -import { IProxyABI, IPROXY_ADDRESS } from "../src/contracts/proxy" -import { ethers } from 'ethers'; -import { forceSetBalanceToEthAddress, forceSetBalanceToSs58Address } from "../src/subtensor"; -import { KeyPair } from "@polkadot-labs/hdkd-helpers"; - -import { decodeAddress } from "@polkadot/util-crypto"; - -async function getTransferCallCode(api: TypedApi, receiver: KeyPair, transferAmount: number) { - - const unsignedTx = api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(convertPublicKeyToSs58(receiver.publicKey)), - value: BigInt(1000000000), - }); - const encodedCallDataBytes = await unsignedTx.getEncodedData(); - - // encoded call should be 0x050300d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d02286bee - // const transferCall = encodedCallDataBytes - - const data = encodedCallDataBytes.asBytes() - - return [...data] -} - -async function getProxies(api: TypedApi, address: string) { - const entries = await api.query.Proxy.Proxies.getEntries() - const result = [] - for (const entry of entries) { - const proxyAddress = entry.keyArgs[0] - const values = entry.value - const proxies = values[0] - for (const proxy of proxies) { - if (proxy.delegate === address) { - result.push(proxyAddress) - } - } - } - return result -} - -describe("Test pure proxy precompile", () => { - const evmWallet = generateRandomEthersWallet(); - // only used for edge case and normal proxy - const evmWallet2 = generateRandomEthersWallet(); - const evmWallet3 = generateRandomEthersWallet(); - const evmWallet4 = generateRandomEthersWallet(); - const receiver = getRandomSubstrateKeypair(); - - let api: TypedApi - - let alice: PolkadotSigner; - - before(async () => { - api = await getDevnetApi() - alice = await getAliceSigner(); - - await forceSetBalanceToEthAddress(api, evmWallet.address) - await forceSetBalanceToEthAddress(api, evmWallet2.address) - await forceSetBalanceToEthAddress(api, evmWallet3.address) - await forceSetBalanceToEthAddress(api, evmWallet4.address) - }) - - it("Call createPureProxy, then use proxy to call transfer", async () => { - const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) - console.log("evmWallet", evmWallet.address) - - const type = 0; - const delay = 0; - const index = 0; - const tx = await contract.createPureProxy(type, delay, index) - const response = await tx.wait() - console.log("response", response.blockNumber) - - const proxiesAfterAdd = await getProxies(api, convertH160ToSS58(evmWallet.address)) - - const length = proxiesAfterAdd.length - assert.equal(length, proxies.length + 1, "proxy should be set") - const proxy = proxiesAfterAdd[proxiesAfterAdd.length - 1] - - await forceSetBalanceToSs58Address(api, proxy) - const balance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free - - const amount = 1000000000; - - const callCode = await getTransferCallCode(api, receiver, amount) - const tx2 = await contract.proxyCall(decodeAddress(proxy), [type], callCode) - await tx2.wait() - - const balanceAfter = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free - assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") - }) - - it("Call createPureProxy, add multiple proxies", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet) - const type = 0; - const delay = 0; - const index = 0; - const proxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) - const length = proxies.length - for (let i = 0; i < 5; i++) { - const tx = await contract.createPureProxy(type, delay, index) - await tx.wait() - - await new Promise(resolve => setTimeout(resolve, 500)); - const currentProxies = await getProxies(api, convertH160ToSS58(evmWallet.address)) - assert.equal(currentProxies.length, length + i + 1, "proxy should be set") - } - }) - - it("Call createPureProxy, edge cases, call via wrong proxy", async () => { - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) - const amount = 1000000000; - const callCode = await getTransferCallCode(api, receiver, amount) - const type = 0; - - // call with wrong proxy - try { - const tx = await contract.proxyCall(receiver, [type], callCode) - await tx.wait() - } catch (error) { - assert.notEqual(error, undefined, "should fail if proxy not set") - } - }) - - it("Call createProxy, then use proxy to call transfer", async () => { - const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address)) - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2) - - const proxiesFromContract = await contract.getProxies(convertH160ToPublicKey(evmWallet2.address)) - assert.equal(proxiesFromContract.length, proxies[0].length, "proxies length should be equal") - - const type = 0; - const delay = 0; - - const tx = await contract.addProxy(convertH160ToPublicKey(evmWallet3.address), type, delay) - await tx.wait() - - const proxiesAfterAdd = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address)) - const proxiesList = proxiesAfterAdd[0].map(proxy => proxy.delegate) - - const proxiesFromContractAfterAdd = await contract.getProxies(convertH160ToPublicKey(evmWallet2.address)) - - assert.equal(proxiesFromContractAfterAdd.length, proxiesList.length, "proxy length should be equal") - - for (let index = 0; index < proxiesFromContractAfterAdd.length; index++) { - const proxyInfo = proxiesFromContractAfterAdd[index] - let proxySs58 = convertPublicKeyToSs58(proxyInfo[0]) - assert.ok(proxiesList.includes(proxySs58), "proxy should be set") - if (index === proxiesFromContractAfterAdd.length - 1) { - assert.equal(Number(proxyInfo[1]), type, "proxy_type should match") - assert.equal(Number(proxyInfo[2]), delay, "delay should match") - } - } - - assert.equal(proxiesList.length, proxies[0].length + 1, "proxy should be set") - const proxy = proxiesList[proxiesList.length - 1] - - assert.equal(proxy, convertH160ToSS58(evmWallet3.address), "proxy should be set") - const balance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free - const amount = 1000000000; - - const contract2 = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet3) - const callCode = await getTransferCallCode(api, receiver, amount) - const tx2 = await contract2.proxyCall(convertH160ToPublicKey(evmWallet2.address), [type], callCode) - await tx2.wait() - - const balanceAfter = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free - assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased") - }) - - it("Call addProxy many times, then check getProxies is correct", async () => { - const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet4.address)) - const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet4) - assert.equal(proxies[0].length, 0, "proxies length should be 0") - - const proxiesFromContract = await contract.getProxies(convertH160ToPublicKey(evmWallet4.address)) - assert.equal(proxiesFromContract.length, proxies[0].length, "proxies length should be equal") - - const type = 1; - const delay = 2; - - for (let i = 0; i < 5; i++) { - const evmWallet = generateRandomEthersWallet() - const tx = await contract.addProxy(convertH160ToPublicKey(evmWallet.address), type, delay) - await tx.wait() - } - - const proxiesAfterAdd = await await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet4.address)) - const proxiesList = proxiesAfterAdd[0].map(proxy => proxy.delegate) - - const proxiesFromContractAfterAdd = await contract.getProxies(convertH160ToPublicKey(evmWallet4.address)) - - assert.equal(proxiesFromContractAfterAdd.length, proxiesList.length, "proxy length should be equal") - - for (let index = 0; index < proxiesFromContractAfterAdd.length; index++) { - const proxyInfo = proxiesFromContractAfterAdd[index] - let proxySs58 = convertPublicKeyToSs58(proxyInfo[0]) - assert.ok(proxiesList.includes(proxySs58), "proxy should be set") - assert.equal(Number(proxyInfo[1]), type, "proxy_type should match") - assert.equal(Number(proxyInfo[2]), delay, "delay should match") - } - }) -}); diff --git a/contract-tests/test/runtime.call.precompile.test.ts b/contract-tests/test/runtime.call.precompile.test.ts deleted file mode 100644 index 40a05827f8..0000000000 --- a/contract-tests/test/runtime.call.precompile.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -import * as assert from "assert"; -import { getAliceSigner, getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { generateRandomEthersWallet, getPublicClient } from "../src/utils"; -import { IDISPATCH_ADDRESS, ISTORAGE_QUERY_ADDRESS, ETH_LOCAL_URL } from "../src/config"; -import { devnet, MultiAddress } from "@polkadot-api/descriptors" -import { PublicClient } from "viem"; -import { PolkadotSigner, TypedApi, getTypedCodecs } from "polkadot-api"; -import { convertPublicKeyToSs58 } from "../src/address-utils" -import { forceSetBalanceToEthAddress, setMaxChildkeyTake, burnedRegister, forceSetBalanceToSs58Address, addStake, setTxRateLimit, addNewSubnetwork, startCall, setTempo, disableAdminFreezeWindowAndOwnerHyperparamRateLimit } from "../src/subtensor"; -import { xxhashAsHex } from "@polkadot/util-crypto"; - -describe("Test the dispatch precompile", () => { - let publicClient: PublicClient; - const wallet1 = generateRandomEthersWallet(); - let api: TypedApi - let alice: PolkadotSigner; - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - let netuid: number; - - before(async () => { - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - alice = await getAliceSigner() - await forceSetBalanceToEthAddress(api, wallet1.address) - - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - - await disableAdminFreezeWindowAndOwnerHyperparamRateLimit(api) - - netuid = await addNewSubnetwork(api, hotkey, coldkey) - // set tempo big enough to avoid stake value updated with fast block feature - await setTempo(api, netuid, 10000) - await startCall(api, netuid, coldkey) - await setTxRateLimit(api, BigInt(0)) - - await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), coldkey) - await addStake(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), BigInt(1_000_000_000), coldkey) - }) - - it("Dispatch transfer call via precompile contract works correctly", async () => { - // call for transfer 1 token to alice - const transferAmount = BigInt(1000000000); - - const unsignedTx = api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(convertPublicKeyToSs58(alice.publicKey)), - value: transferAmount, - }); - const encodedCallDataBytes = await unsignedTx.getEncodedData(); - - // encoded call should be 0x050300d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d02286bee - const transferCall = encodedCallDataBytes.asHex() - - const aliceBalance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(alice.publicKey))).data.free - const txResponse = await wallet1.sendTransaction({ - to: IDISPATCH_ADDRESS, - data: transferCall, - }) - await txResponse.wait() - - const aliceBalanceAfterTransfer = (await api.query.System.Account.getValue(convertPublicKeyToSs58(alice.publicKey))).data.free - - assert.equal(aliceBalance + transferAmount, aliceBalanceAfterTransfer) - }) - - it("Storage query only allow some pallets prefixed storage", async () => { - const authorizedKeys = [ - await api.query.SubtensorModule.TotalNetworks.getKey(), - await api.query.Swap.FeeRate.getKey(), - await api.query.Balances.TotalIssuance.getKey(), - await api.query.Proxy.Announcements.getKey(), - await api.query.Scheduler.Agenda.getKey(), - await api.query.Drand.Pulses.getKey(), - await api.query.Crowdloan.Crowdloans.getKey(), - await api.query.Sudo.Key.getKey(), - await api.query.Multisig.Multisigs.getKey(), - await api.query.Timestamp.Now.getKey(), - ]; - - for (const key of authorizedKeys) { - await assert.doesNotReject( - publicClient.call({ - to: ISTORAGE_QUERY_ADDRESS, - data: key.toString() as `0x${string}`, - }) - ); - } - - const unauthorizedKeys = [ - await api.query.System.Events.getKey(), - await api.query.Grandpa.CurrentSetId.getKey(), - xxhashAsHex(":code", 128), - ]; - - for (const key of unauthorizedKeys) { - await assert.rejects( - publicClient.call({ - to: ISTORAGE_QUERY_ADDRESS, - data: key.toString() as `0x${string}`, - }) - ); - } - }) - - - it("Value type storage query call via precompile contract works correctly", async () => { - const key = await api.query.SubtensorModule.MaxChildkeyTake.getKey(); - - let maxChildkeyTake = 257; - await setMaxChildkeyTake(api, maxChildkeyTake) - - api.query.SubtensorModule.MaxChildkeyTake.getValue(); - const rawCallResponse = await publicClient.call({ - to: ISTORAGE_QUERY_ADDRESS, - data: key.toString() as `0x${string}`, - }) - const rawResultData = rawCallResponse.data ?? ""; - - const codec = await getTypedCodecs(devnet); - const maxChildkeyTakeCodec = codec.query.SubtensorModule.MaxChildkeyTake.value; - const maxChildkeyTakeFromContract = maxChildkeyTakeCodec.dec(rawResultData); - assert.equal(maxChildkeyTakeFromContract, maxChildkeyTake, "value should be 257") - }) - - it("Map type storage query call via precompile contract works correctly", async () => { - - const key = await api.query.SubtensorModule.Tempo.getKey(netuid); - - const tempoOnChain = await api.query.SubtensorModule.Tempo.getValue(netuid); - const rawCallResponse = await publicClient.call({ - to: ISTORAGE_QUERY_ADDRESS, - data: key.toString() as `0x${string}`, - }) - const rawResultData = rawCallResponse.data ?? ""; - - const codec = await getTypedCodecs(devnet); - const maxChildkeyTakeValueCodec = codec.query.SubtensorModule.Tempo.value; - const decodedValue = maxChildkeyTakeValueCodec.dec(rawResultData); - assert.equal(tempoOnChain, decodedValue, "value should be the same as on chain") - }) - - it("Double map type storage query call via precompile contract works correctly", async () => { - const key = await api.query.SubtensorModule.TotalHotkeyAlpha.getKey(convertPublicKeyToSs58(hotkey.publicKey), netuid); - const totalHotkeyAlphaOnChain = await api.query.SubtensorModule.TotalHotkeyAlpha.getValue(convertPublicKeyToSs58(hotkey.publicKey), netuid); - - const rawCallResponse = await publicClient.call({ - to: ISTORAGE_QUERY_ADDRESS, - data: key.toString() as `0x${string}`, - }) - const rawResultData = rawCallResponse.data ?? ""; - const codec = await getTypedCodecs(devnet); - const totalHotkeyAlphaValueCodec = codec.query.SubtensorModule.TotalHotkeyAlpha.value; - const decodedValue = totalHotkeyAlphaValueCodec.dec(rawResultData); - assert.equal(totalHotkeyAlphaOnChain, decodedValue, "value should be the same as on chain") - }) - - // Polkadot api can't decode the boolean type for now. - // it("Double map type storage query call via precompile contract works correctly", async () => { - // const key = await api.query.SubtensorModule.IsNetworkMember.getKey(convertPublicKeyToSs58(alice.publicKey), netuid); - - // const isNetworkMemberOnChain = await api.query.SubtensorModule.IsNetworkMember.getValue(convertPublicKeyToSs58(alice.publicKey), netuid); - // const rawCallResponse = await publicClient.call({ - // to: ISTORAGE_QUERY_ADDRESS, - // data: key.toString() as `0x${string}`, - // }) - - // const rawResultData = rawCallResponse.data ?? ""; - // const codec = await getTypedCodecs(devnet); - // const isNetworkMemberValueCodec = codec.query.SubtensorModule.IsNetworkMember.value; - // const decodedValue = isNetworkMemberValueCodec.dec(rawResultData); - // assert.equal(isNetworkMemberOnChain, decodedValue, "value should be the same as on chain") - // }) - -}); diff --git a/contract-tests/test/staking.precompile.reward.test.ts b/contract-tests/test/staking.precompile.reward.test.ts deleted file mode 100644 index 31e15c6225..0000000000 --- a/contract-tests/test/staking.precompile.reward.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate" -import { devnet } from "@polkadot-api/descriptors" -import { TypedApi } from "polkadot-api"; -import { convertPublicKeyToSs58 } from "../src/address-utils" -import { tao } from "../src/balance-math" -import { - forceSetBalanceToSs58Address, addNewSubnetwork, burnedRegister, - setTxRateLimit, setTempo, setWeightsSetRateLimit, setSubnetOwnerCut, - setMinDelegateTake, setActivityCutoff, addStake, setWeight, rootRegister, - startCall, - disableAdminFreezeWindowAndOwnerHyperparamRateLimit, - getStake -} from "../src/subtensor" - -describe("Test neuron precompile reward", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - - const validator = getRandomSubstrateKeypair(); - const miner = getRandomSubstrateKeypair(); - const nominator = getRandomSubstrateKeypair(); - - let api: TypedApi - - before(async () => { - const root_netuid = 0; - const root_tempo = 1; // neet root epoch to happen before subnet tempo - const subnet_tempo = 1; - api = await getDevnetApi() - - // await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(alice.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(validator.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(miner.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(nominator.publicKey)) - // await forceSetBalanceToEthAddress(api, wallet1.address) - // await forceSetBalanceToEthAddress(api, wallet2.address) - let netuid = await addNewSubnetwork(api, hotkey, coldkey) - await startCall(api, netuid, coldkey) - - console.log("test the case on subnet ", netuid) - await disableAdminFreezeWindowAndOwnerHyperparamRateLimit(api) - - await setTxRateLimit(api, BigInt(0)) - await setTempo(api, root_netuid, root_tempo) - await setTempo(api, netuid, subnet_tempo) - await setWeightsSetRateLimit(api, netuid, BigInt(0)) - - await burnedRegister(api, netuid, convertPublicKeyToSs58(validator.publicKey), coldkey) - await burnedRegister(api, netuid, convertPublicKeyToSs58(miner.publicKey), coldkey) - await burnedRegister(api, netuid, convertPublicKeyToSs58(nominator.publicKey), coldkey) - await setSubnetOwnerCut(api, 0) - await setActivityCutoff(api, netuid, 65535) - await setMinDelegateTake(api, 0) - }) - - it("Staker receives rewards", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1 - - await addStake(api, netuid, convertPublicKeyToSs58(miner.publicKey), tao(1), coldkey) - await addStake(api, netuid, convertPublicKeyToSs58(nominator.publicKey), tao(1), coldkey) - - await addStake(api, netuid, convertPublicKeyToSs58(validator.publicKey), tao(100), coldkey) - - const miner_alpha_before_emission = await getStake( - api, - convertPublicKeyToSs58(miner.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid - ) - - await setWeight(api, netuid, [0, 1], [0xffff, 0xffff], BigInt(0), validator) - await rootRegister(api, convertPublicKeyToSs58(validator.publicKey), coldkey) - - let index = 0; - while (index < 60) { - const pending = await api.query.SubtensorModule.PendingValidatorEmission.getValue(netuid); - if (pending > 0) { - console.log("pending amount is ", pending); - break; - } - - await new Promise((resolve) => setTimeout(resolve, 1000)); - console.log("wait for the ValidatorEmission update"); - index += 1; - } - - index = 0; - while (index < 60) { - let miner_current_alpha = await getStake( - api, - convertPublicKeyToSs58(miner.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid - ) - - if (miner_current_alpha > miner_alpha_before_emission) { - console.log("miner got reward"); - break; - } - - await new Promise((resolve) => setTimeout(resolve, 1000)); - console.log(" waiting for emission"); - index += 1; - } - }) -}) diff --git a/contract-tests/test/staking.precompile.wrap.test.ts b/contract-tests/test/staking.precompile.wrap.test.ts deleted file mode 100644 index e4d666adf1..0000000000 --- a/contract-tests/test/staking.precompile.wrap.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as assert from "assert"; -import { getDevnetApi, getRandomSubstrateKeypair } from "../src/substrate"; -import { devnet } from "@polkadot-api/descriptors"; -import { TypedApi } from "polkadot-api"; -import { - convertH160ToSS58, - convertPublicKeyToSs58, - ethAddressToH160, -} from "../src/address-utils"; -import { tao, raoToEth } from "../src/balance-math"; -import { - addNewSubnetwork, - addStake, - disableWhiteListCheck, - forceSetBalanceToEthAddress, - forceSetBalanceToSs58Address, - startCall, -} from "../src/subtensor"; -import { ethers } from "ethers"; -import { generateRandomEthersWallet } from "../src/utils"; - -import { abi, bytecode } from "../src/contracts/stakeWrap"; - -describe("Test staking precompile add from deployed contract", () => { - const hotkey = getRandomSubstrateKeypair(); - const coldkey = getRandomSubstrateKeypair(); - const wallet1 = generateRandomEthersWallet(); - - let api: TypedApi; - - before(async () => { - api = await getDevnetApi(); - await forceSetBalanceToSs58Address( - api, - convertPublicKeyToSs58(hotkey.publicKey), - ); - await forceSetBalanceToSs58Address( - api, - convertPublicKeyToSs58(coldkey.publicKey), - ); - await forceSetBalanceToEthAddress(api, wallet1.address); - await addNewSubnetwork(api, hotkey, coldkey); - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - await startCall(api, netuid, coldkey); - await disableWhiteListCheck(api, true) - console.log("will test in subnet: ", netuid); - }); - - it("Staker add and remove stake", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - - const contractFactory = new ethers.ContractFactory(abi, bytecode, wallet1) - const contract = await contractFactory.deploy() - await contract.waitForDeployment() - - // stake will remove the balance from contract, need transfer token to deployed contract - const ethTransfer = { - to: contract.target.toString(), - value: raoToEth(tao(10000)).toString() - } - - const txResponse = await wallet1.sendTransaction(ethTransfer) - await txResponse.wait(); - - const deployedContract = new ethers.Contract( - contract.target.toString(), - abi, - wallet1, - ); - - const tx = await deployedContract.stake( - hotkey.publicKey, - netuid, - tao(2), - ); - await tx.wait(); - - const tx2 = await deployedContract.removeStake( - hotkey.publicKey, - netuid, - tao(1), - ); - await tx2.wait(); - - }); - - it("Staker add stake limit", async () => { - let netuid = (await api.query.SubtensorModule.TotalNetworks.getValue()) - 1; - let ss58Address = convertH160ToSS58(wallet1.address); - - const contractFactory = new ethers.ContractFactory(abi, bytecode, wallet1) - const contract = await contractFactory.deploy() - await contract.waitForDeployment() - - - // stake will remove the balance from contract, need transfer token to deployed contract - const ethTransfer = { - to: contract.target.toString(), - value: raoToEth(tao(10000)).toString() - } - - const txResponse = await wallet1.sendTransaction(ethTransfer) - await txResponse.wait(); - - const balance = await api.query.System.Account.getValue(convertH160ToSS58(contract.target.toString())) - console.log(" == balance is ", balance.data.free) - - const deployedContract = new ethers.Contract( - contract.target.toString(), - abi, - wallet1, - ); - - const tx = await deployedContract.stakeLimit( - hotkey.publicKey, - netuid, - tao(2000), - tao(1000), - true, - ); - await tx.wait(); - - }); -}); diff --git a/contract-tests/test/transaction.replace.test.ts b/contract-tests/test/transaction.replace.test.ts deleted file mode 100644 index afb95ed9d5..0000000000 --- a/contract-tests/test/transaction.replace.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as assert from "assert"; - -import { getDevnetApi, getRandomSubstrateSigner, } from "../src/substrate" -import { getPublicClient } from "../src/utils"; -import { ETH_LOCAL_URL, IBALANCETRANSFER_ADDRESS, IBalanceTransferABI } from "../src/config"; -import { devnet } from "@polkadot-api/descriptors" -import { PublicClient } from "viem"; -import { TypedApi } from "polkadot-api"; -import { generateRandomEthersWallet } from "../src/utils"; -import { tao, raoToEth } from "../src/balance-math"; -import { toViemAddress, } from "../src/address-utils" -import { getContract } from "../src/eth" -import { forceSetBalanceToEthAddress, } from "../src/subtensor"; - -describe("Transaction replace tests", () => { - // init eth part - const wallet = generateRandomEthersWallet(); - const wallet2 = generateRandomEthersWallet(); - const signer = getRandomSubstrateSigner(); - let publicClient: PublicClient; - let api: TypedApi - - before(async () => { - - publicClient = await getPublicClient(ETH_LOCAL_URL) - api = await getDevnetApi() - await forceSetBalanceToEthAddress(api, wallet.address) - }); - - it("Can replace simple transfer transaction", async () => { - const transferBalance = raoToEth(tao(1)) - - const gasPrice = BigInt(10e9) - const gasLimit = BigInt(1000000) - const nonce = await publicClient.getTransactionCount({ address: toViemAddress(wallet.address) }) - - for (let i = 1; i < 10; i++) { - const transfer = { - to: wallet2.address, - value: transferBalance.toString(), - nonce: nonce, - gasPrice: gasPrice * BigInt(i), - gasLimit: gasLimit * BigInt(i) - } - - try { - await wallet.sendTransaction(transfer) - } catch (error) { - // ignore error, previous transaction could be mined. the nonce is wrong. - } - await new Promise(resolve => setTimeout(resolve, 10)) - } - - // check the node not crashed - await forceSetBalanceToEthAddress(api, wallet.address) - }) - - it("Can replace precompile call transaction", async () => { - const contract = getContract(IBALANCETRANSFER_ADDRESS, IBalanceTransferABI, wallet) - const transferBalance = raoToEth(tao(1)) - - const gasPrice = BigInt(10e9) - const gasLimit = BigInt(1000000) - const nonce = await publicClient.getTransactionCount({ address: toViemAddress(wallet.address) }) - - for (let i = 1; i < 10; i++) { - try { - await contract.transfer(signer.publicKey, { - value: transferBalance.toString(), - nonce: nonce, - gasPrice: gasPrice * BigInt(i), - gasLimit: gasLimit * BigInt(i) - }) - } catch (error) { - // ignore error, previous transaction could be mined. the nonce is wrong. - } - - await new Promise(resolve => setTimeout(resolve, 10)) - } - // check the node not crashed - await forceSetBalanceToEthAddress(api, wallet.address) - }) -}) \ No newline at end of file diff --git a/contract-tests/test/wasm.contract.test.ts b/contract-tests/test/wasm.contract.test.ts deleted file mode 100644 index 6ae8d82c08..0000000000 --- a/contract-tests/test/wasm.contract.test.ts +++ /dev/null @@ -1,976 +0,0 @@ -import { devnet, MultiAddress } from "@polkadot-api/descriptors"; -import { getInkClient, InkClient, } from "@polkadot-api/ink-contracts"; -import { KeyPair } from "@polkadot-labs/hdkd-helpers"; -import * as assert from "assert"; -import fs from "fs"; -import { Binary, TypedApi } from "polkadot-api"; -import { contracts } from "../.papi/descriptors"; -import { convertPublicKeyToSs58 } from "../src/address-utils"; -import { tao } from "../src/balance-math"; -import { getBalance, getDevnetApi, getRandomSubstrateKeypair, getSignerFromKeypair, waitForTransactionWithRetry } from "../src/substrate"; -import { addNewSubnetwork, burnedRegister, forceSetBalanceToSs58Address, sendWasmContractExtrinsic, setAdminFreezeWindow, setTargetRegistrationsPerInterval, startCall } from "../src/subtensor"; - -const bittensorWasmPath = "./bittensor/target/ink/bittensor.wasm" -const bittensorBytecode = fs.readFileSync(bittensorWasmPath) - -describe("Test wasm contract", () => { - - let api: TypedApi - let hotkey: KeyPair; - let coldkey: KeyPair; - - let hotkey2: KeyPair; - let coldkey2: KeyPair; - - // set initial netuid to 0 to avoid warning - let netuid: number = 0; - let contractAddress = ""; - let inkClient: InkClient; - - async function addStakeViaContract(addStakeToContract: boolean) { - if (contractAddress === "") { - return; - } - - const amount = tao(100) - let message - let dest - if (addStakeToContract) { - message = inkClient.message("add_stake") - dest = contractAddress; - } else { - message = inkClient.message("caller_add_stake") - dest = convertPublicKeyToSs58(coldkey.publicKey); - } - - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - amount: amount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - dest, - netuid, - ))?.stake - - assert.ok(stake !== undefined) - assert.ok(stake > BigInt(0)) - } - - async function getContractStake(): Promise { - const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid, - ))?.stake - - assert.ok(stake !== undefined) - return stake as bigint - } - - async function getContractStakeOnRoot(): Promise { - const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - 0, - ))?.stake - - assert.ok(stake !== undefined) - return stake as bigint - } - - async function initSecondColdAndHotkey() { - hotkey2 = getRandomSubstrateKeypair(); - coldkey2 = getRandomSubstrateKeypair(); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey2.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey2.publicKey)) - await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey2.publicKey), coldkey2) - } - - before(async () => { - // init variables got from await and async - api = await getDevnetApi() - await setAdminFreezeWindow(api); - - inkClient = getInkClient(contracts.bittensor) - hotkey = getRandomSubstrateKeypair(); - coldkey = getRandomSubstrateKeypair(); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - - netuid = await addNewSubnetwork(api, hotkey, coldkey) - await startCall(api, netuid, coldkey) - console.log("test the case on subnet ", netuid) - await addNewSubnetwork(api, hotkey, coldkey) - await startCall(api, netuid + 1, coldkey) - await setTargetRegistrationsPerInterval(api, netuid) - }) - - beforeEach(async () => { - hotkey = getRandomSubstrateKeypair(); - coldkey = getRandomSubstrateKeypair(); - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(coldkey.publicKey)) - await forceSetBalanceToSs58Address(api, convertPublicKeyToSs58(hotkey.publicKey)) - await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), coldkey) - - }); - - it("Can instantiate contract", async () => { - const signer = getSignerFromKeypair(coldkey); - const constructor = inkClient.constructor('new') - const data = constructor.encode() - const instantiate_with_code = await api.tx.Contracts.instantiate_with_code({ - code: Binary.fromBytes(bittensorBytecode), - storage_deposit_limit: BigInt(10000000), - value: BigInt(0), - gas_limit: { - ref_time: BigInt(1000000000), - proof_size: BigInt(1000000), - }, - data: Binary.fromBytes(data.asBytes()), - salt: Binary.fromHex("0x"), - }).signAndSubmit(signer) - - let codeStoredEvents = await api.event.Contracts.Instantiated.filter(instantiate_with_code.events) - if (codeStoredEvents.length === 0) { - throw new Error("No events found after instantiating contract call") - } - contractAddress = codeStoredEvents[0].contract - - // transfer 10 Tao to contract then we can stake - const transfer = await api.tx.Balances.transfer_keep_alive({ - dest: MultiAddress.Id(contractAddress), - value: tao(2000), - }) - await waitForTransactionWithRetry(api, transfer, signer) - }) - - - it("Can query stake info from contract", async () => { - - const queryMessage = inkClient.message("get_stake_info_for_hotkey_coldkey_netuid") - - const data = queryMessage.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - coldkey: Binary.fromBytes(coldkey.publicKey), - netuid: netuid, - }) - - const response = await api.apis.ContractsApi.call( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - BigInt(0), - undefined, - undefined, - Binary.fromBytes(data.asBytes()), - ) - - assert.ok(response.result.success) - const result = queryMessage.decode(response.result.value).value.value - - if (typeof result === "object" && "hotkey" in result && "coldkey" in result && "netuid" in result && "stake" in result && "locked" in result && "emission" in result && "tao_emission" in result && "drain" in result && "is_registered" in result) { - assert.equal(result.hotkey, convertPublicKeyToSs58(hotkey.publicKey)) - assert.equal(result.coldkey, convertPublicKeyToSs58(coldkey.publicKey)) - assert.equal(result.netuid, netuid) - assert.equal(result.is_registered, true) - } else { - throw new Error("result is not an object") - } - - }) - - it("Can add stake to contract", async () => { - await addStakeViaContract(true) - }) - - it("Can remove stake to contract", async () => { - await addStakeViaContract(true) - const stake = await getContractStake() - - let amount = stake / BigInt(2) - const message = inkClient.message("remove_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - amount: amount, - }) - - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfterAddStake = await getContractStake() - - assert.ok(stakeAfterAddStake < stake) - }) - - it("Can unstake all from contract", async () => { - await addStakeViaContract(true) - // Get stake before unstake_all - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - // Call unstake_all - const unstakeMessage = inkClient.message("unstake_all") - const unstakeData = unstakeMessage.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, unstakeData) - - // Verify stake is now zero - const stakeAfter = await getContractStake() - - assert.equal(stakeAfter, BigInt(0)) - }) - - it("Can unstake all alpha from contract", async () => { - await addStakeViaContract(true) - // Get stake before unstake_all_alpha - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - // Call unstake_all_alpha - const message = inkClient.message("unstake_all_alpha") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stake is now zero - const stakeAfter = await getContractStake() - - assert.equal(stakeAfter, BigInt(0)) - }) - - it("Can move stake between hotkeys", async () => { - await addStakeViaContract(true) - await initSecondColdAndHotkey() - // Get initial stakes - const originStakeBefore = await getContractStake() - - const destStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - contractAddress, - netuid, - ))?.stake || BigInt(0) - - assert.ok(originStakeBefore > BigInt(0)) - - // Move stake - const moveAmount = originStakeBefore / BigInt(2) - const message = inkClient.message("move_stake") - const data = message.encode({ - origin_hotkey: Binary.fromBytes(hotkey.publicKey), - destination_hotkey: Binary.fromBytes(hotkey2.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: moveAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stakes changed - const originStakeAfter = await getContractStake() - - const destStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - contractAddress, - netuid, - ))?.stake - - assert.ok(destStakeAfter !== undefined) - assert.ok(originStakeAfter < originStakeBefore) - assert.ok(destStakeAfter > destStakeBefore) - }) - - it("Can transfer stake between coldkeys", async () => { - await addStakeViaContract(true) - await initSecondColdAndHotkey() - // Get initial stake - const stakeBeforeOrigin = await getContractStake() - - const stakeBeforeDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - - assert.ok(stakeBeforeOrigin > BigInt(0)) - assert.ok(stakeBeforeDest !== undefined) - - // Transfer stake - const transferAmount = stakeBeforeOrigin / BigInt(2) - const message = inkClient.message("transfer_stake") - const data = message.encode({ - destination_coldkey: Binary.fromBytes(coldkey2.publicKey), - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: transferAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stake transferred - const stakeAfterOrigin = await getContractStake() - - const stakeAfterDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - - assert.ok(stakeAfterDest !== undefined) - assert.ok(stakeAfterOrigin < stakeBeforeOrigin) - assert.ok(stakeAfterDest > stakeBeforeDest!) - }) - - it("Can swap stake between networks", async () => { - await addStakeViaContract(true) - // Get initial stakes - const stakeBefore = await getContractStake() - - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake || BigInt(0) - - assert.ok(stakeBefore > BigInt(0)) - - // Swap stake - const swapAmount = stakeBefore / BigInt(2) - const message = inkClient.message("swap_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: swapAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stakes swapped - const stakeAfter = await getContractStake() - - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake - - assert.ok(stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2) - }) - - it("Can add stake with limit", async () => { - const stakeBefore = await getContractStake() - - const message = inkClient.message("add_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - amount: tao(200), - limit_price: tao(100), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - // Verify stake was added - const stakeAfter = await getContractStake() - - assert.ok(stakeAfter > stakeBefore) - }) - - it("Can remove stake with limit", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - const message = inkClient.message("remove_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - assert.ok(stakeAfter < stakeBefore) - }) - - it("Can swap stake with limit", async () => { - await addStakeViaContract(true) - - const stakeBefore = await getContractStake() - - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake - - assert.ok(stakeBefore > BigInt(0)) - assert.ok(stakeBefore2 !== undefined) - - const message = inkClient.message("swap_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - netuid + 1, - ))?.stake - - assert.ok(stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2) - }) - - it("Can remove stake full limit", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - - assert.ok(stakeBefore > BigInt(0)) - - const message = inkClient.message("remove_stake_full_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: netuid, - limit_price: undefined, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - assert.ok(stakeAfter < stakeBefore) - }) - - it("Can set coldkey auto stake hotkey", async () => { - const message = inkClient.message("set_coldkey_auto_stake_hotkey") - const data = message.encode({ - netuid: netuid, - hotkey: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - let autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( - contractAddress, - netuid, - ) - - assert.ok(autoStakeHotkey !== undefined) - assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey.publicKey)) - }) - - it("Can add and remove proxy", async () => { - const message = inkClient.message("add_proxy") - const data = message.encode({ - delegate: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - let proxies = await api.query.Proxy.Proxies.getValue( - contractAddress, - ) - assert.ok(proxies !== undefined) - assert.ok(proxies.length > 0 && proxies[0].length > 0) - assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey.publicKey)) - - - const removeMessage = inkClient.message("remove_proxy") - const removeData = removeMessage.encode({ - delegate: Binary.fromBytes(hotkey.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) - - let proxiesAfterRemove = await api.query.Proxy.Proxies.getValue( - contractAddress, - ) - assert.ok(proxiesAfterRemove !== undefined) - assert.ok(proxiesAfterRemove[0].length === 0) - }) - - it("Can get alpha price", async () => { - const message = inkClient.message("get_alpha_price") - const data = message.encode({ - netuid: netuid, - }) - - const response = await api.apis.ContractsApi.call( - convertPublicKeyToSs58(hotkey.publicKey), - contractAddress, - BigInt(0), - undefined, - undefined, - Binary.fromBytes(data.asBytes()), - ) - - assert.ok(response.result.success) - const result = message.decode(response.result.value).value.value - - assert.ok(result !== undefined) - }) - - it("Can recycle alpha from contract stake", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - const message = inkClient.message("recycle_alpha") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: stakeBefore / BigInt(2), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - assert.ok(stakeAfter < stakeBefore) - assert.ok(alphaOutAfter < alphaOutBefore) - }) - - it("Can burn alpha from contract stake", async () => { - await addStakeViaContract(true) - const stakeBefore = await getContractStake() - const alphaBurnedBefore = await api.query.AlphaAssets.AlphaBurned.getValue(netuid) - - const message = inkClient.message("burn_alpha") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: stakeBefore / BigInt(2), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - const alphaBurnedAfter = await api.query.AlphaAssets.AlphaBurned.getValue(netuid) - - assert.ok(stakeAfter < stakeBefore) - assert.ok(alphaBurnedBefore < alphaBurnedAfter) - }) - - it("Can add stake and recycle resulting alpha", async () => { - const stakeBefore = await getContractStake() - - const message = inkClient.message("add_stake_recycle") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: tao(100), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - - assert.equal(stakeAfter, stakeBefore) - }) - - it("Can add stake and burn resulting alpha", async () => { - const stakeBefore = await getContractStake() - const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - const message = inkClient.message("add_stake_burn") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: tao(100), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStake() - const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - assert.equal(stakeAfter, stakeBefore) - assert.ok(alphaOutAfter > alphaOutBefore) - }) - - it("Can caller add stake (fn 20)", async () => { - await addStakeViaContract(false) - }) - - it("Can caller remove stake (fn 21)", async () => { - await addStakeViaContract(false) - const stake = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stake !== undefined) - const amount = stake / BigInt(2) - const message = inkClient.message("caller_remove_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter < stake!) - }) - - it("Can caller unstake_all (fn 22)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_unstake_all") - const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined) - assert.ok(stakeAfter < stakeBefore!) - }) - - it("Can caller unstake_all_alpha (fn 23)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_unstake_all_alpha") - const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined) - assert.ok(stakeAfter < stakeBefore!) - }) - - it("Can caller move_stake (fn 24)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const originStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const destStakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake || BigInt(0) - assert.ok(originStakeBefore !== undefined && originStakeBefore > BigInt(0)) - const moveAmount = originStakeBefore / BigInt(2) - const message = inkClient.message("caller_move_stake") - const data = message.encode({ - origin_hotkey: Binary.fromBytes(hotkey.publicKey), - destination_hotkey: Binary.fromBytes(hotkey2.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: moveAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const originStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const destStakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey2.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(originStakeAfter !== undefined && destStakeAfter !== undefined) - assert.ok(originStakeAfter < originStakeBefore!) - assert.ok(destStakeAfter > destStakeBefore) - }) - - it("Can caller transfer_stake (fn 25)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const stakeBeforeOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeBeforeDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - assert.ok(stakeBeforeOrigin !== undefined && stakeBeforeOrigin > BigInt(0)) - assert.ok(stakeBeforeDest !== undefined) - const transferAmount = stakeBeforeOrigin / BigInt(2) - const message = inkClient.message("caller_transfer_stake") - const data = message.encode({ - destination_coldkey: Binary.fromBytes(coldkey2.publicKey), - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid, - amount: transferAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfterOrigin = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeAfterDest = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey2.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfterOrigin !== undefined && stakeAfterDest !== undefined) - assert.ok(stakeAfterOrigin < stakeBeforeOrigin!) - assert.ok(stakeAfterDest > stakeBeforeDest!) - }) - - it("Can caller swap_stake (fn 26)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake || BigInt(0) - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const swapAmount = stakeBefore / BigInt(2) - const message = inkClient.message("caller_swap_stake") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: swapAmount, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2) - }) - - it("Can caller add_stake_limit (fn 27)", async () => { - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined) - const message = inkClient.message("caller_add_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: tao(200), - limit_price: tao(100), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter > stakeBefore!) - }) - - it("Can caller remove_stake_limit (fn 28)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_remove_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) - }) - - it("Can caller swap_stake_limit (fn 29)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeBefore2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - assert.ok(stakeBefore2 !== undefined) - const message = inkClient.message("caller_swap_stake_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - origin_netuid: netuid, - destination_netuid: netuid + 1, - amount: stakeBefore / BigInt(2), - limit_price: tao(1), - allow_partial: false, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - const stakeAfter2 = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid + 1, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter2 !== undefined) - assert.ok(stakeAfter < stakeBefore) - assert.ok(stakeAfter2 > stakeBefore2!) - }) - - it("Can caller remove_stake_full_limit (fn 30)", async () => { - await addStakeViaContract(false) - const stakeBefore = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeBefore !== undefined && stakeBefore > BigInt(0)) - const message = inkClient.message("caller_remove_stake_full_limit") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid, - limit_price: undefined, - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const stakeAfter = (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( - convertPublicKeyToSs58(hotkey.publicKey), - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ))?.stake - assert.ok(stakeAfter !== undefined && stakeAfter < stakeBefore!) - }) - - it("Can caller set_coldkey_auto_stake_hotkey (fn 31)", async () => { - await addStakeViaContract(false) - await initSecondColdAndHotkey() - const message = inkClient.message("caller_set_coldkey_auto_stake_hotkey") - const data = message.encode({ - netuid, - hotkey: Binary.fromBytes(hotkey2.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - const autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( - convertPublicKeyToSs58(coldkey.publicKey), - netuid, - ) - assert.ok(autoStakeHotkey === convertPublicKeyToSs58(hotkey2.publicKey)) - }) - - it("Can caller add_proxy and remove_proxy (fn 32-33)", async () => { - const addMessage = inkClient.message("caller_add_proxy") - const addData = addMessage.encode({ - delegate: Binary.fromBytes(hotkey2.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, addData) - let proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) - assert.ok(proxies !== undefined && proxies[0].length > 0) - assert.ok(proxies[0][0].delegate === convertPublicKeyToSs58(hotkey2.publicKey)) - - const removeMessage = inkClient.message("caller_remove_proxy") - const removeData = removeMessage.encode({ - delegate: Binary.fromBytes(hotkey2.publicKey), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData) - proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)) - assert.ok(proxies !== undefined && proxies[0].length === 0) - }) - - it("Check add_stake_recycle is atomic operation", async () => { - const stakeBefore = await getContractStakeOnRoot() - const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - - // recycle alpha on root subnet is not allowed, the extrinsic should be failed. - const message = inkClient.message("add_stake_recycle") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: 0, - amount: tao(100), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStakeOnRoot() - const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - - assert.ok(balanceBefore - balanceAfter < 10_000_000) - assert.equal(stakeAfter, stakeBefore) - }) - - it("Check add_stake_burn is atomic operation", async () => { - const stakeBefore = await getContractStakeOnRoot() - const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - - const message = inkClient.message("add_stake_burn") - const data = message.encode({ - hotkey: Binary.fromBytes(hotkey.publicKey), - netuid: 0, - amount: tao(100), - }) - await sendWasmContractExtrinsic(api, coldkey, contractAddress, data) - - const stakeAfter = await getContractStakeOnRoot() - const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid) - const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)) - - assert.ok(balanceBefore - balanceAfter < 10_000_000) - assert.equal(stakeAfter, stakeBefore) - assert.ok(alphaOutAfter > alphaOutBefore) - }) -}); \ No newline at end of file diff --git a/contract-tests/tsconfig.json b/contract-tests/tsconfig.json deleted file mode 100644 index c9c555d96f..0000000000 --- a/contract-tests/tsconfig.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/contract-tests/yarn.lock b/contract-tests/yarn.lock deleted file mode 100644 index 037e0a53ce..0000000000 --- a/contract-tests/yarn.lock +++ /dev/null @@ -1,3027 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@adraffy/ens-normalize@^1.10.1", "@adraffy/ens-normalize@^1.11.0": - version "1.11.1" - resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz" - integrity sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ== - -"@adraffy/ens-normalize@1.10.1": - version "1.10.1" - resolved "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz" - integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== - -"@babel/code-frame@^7.26.2": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== - dependencies: - "@babel/helper-validator-identifier" "^7.27.1" - js-tokens "^4.0.0" - picocolors "^1.1.1" - -"@babel/helper-validator-identifier@^7.27.1": - version "7.28.5" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" - integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== - -"@commander-js/extra-typings@^14.0.0": - version "14.0.0" - resolved "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz" - integrity sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg== - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" - -"@esbuild/linux-x64@0.25.12": - version "0.25.12" - resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz" - integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== - -"@ethereumjs/rlp@^10.0.0": - version "10.1.0" - resolved "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-10.1.0.tgz" - integrity sha512-r67BJbwilammAqYI4B5okA66cNdTlFzeWxPNJOolKV52ZS/flo0tUBf4x4gxWXBgh48OgsdFV1Qp5pRoSe8IhQ== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.13" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" - integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": - version "1.5.5" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" - integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== - -"@jridgewell/trace-mapping@^0.3.24": - version "0.3.31" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" - integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@noble/ciphers@^1.3.0": - version "1.3.0" - resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz" - integrity sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw== - -"@noble/curves@^1.3.0", "@noble/curves@^1.6.0", "@noble/curves@~1.9.0", "@noble/curves@~1.9.2": - version "1.9.7" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz" - integrity sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw== - dependencies: - "@noble/hashes" "1.8.0" - -"@noble/curves@^2.0.0": - version "2.0.1" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" - integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== - dependencies: - "@noble/hashes" "2.0.1" - -"@noble/curves@^2.0.1": - version "2.0.1" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" - integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== - dependencies: - "@noble/hashes" "2.0.1" - -"@noble/curves@~1.8.1": - version "1.8.2" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz" - integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== - dependencies: - "@noble/hashes" "1.7.2" - -"@noble/curves@~2.0.0": - version "2.0.1" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-2.0.1.tgz" - integrity sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw== - dependencies: - "@noble/hashes" "2.0.1" - -"@noble/curves@1.2.0": - version "1.2.0" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz" - integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== - dependencies: - "@noble/hashes" "1.3.2" - -"@noble/curves@1.8.1": - version "1.8.1" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz" - integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ== - dependencies: - "@noble/hashes" "1.7.1" - -"@noble/curves@1.9.1": - version "1.9.1" - resolved "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz" - integrity sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA== - dependencies: - "@noble/hashes" "1.8.0" - -"@noble/hashes@^1.3.1": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@^1.3.3", "@noble/hashes@~1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@^1.5.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@^1.8.0", "@noble/hashes@1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@^2.0.0", "@noble/hashes@^2.0.1", "@noble/hashes@~2.0.0", "@noble/hashes@2.0.1": - version "2.0.1" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-2.0.1.tgz" - integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== - -"@noble/hashes@~1.7.1", "@noble/hashes@1.7.1": - version "1.7.1" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" - integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== - -"@noble/hashes@~1.8.0": - version "1.8.0" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - -"@noble/hashes@1.3.2": - version "1.3.2" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== - -"@noble/hashes@1.7.2": - version "1.7.2" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz" - integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@polkadot-api/cli@0.16.3": - version "0.16.3" - resolved "https://registry.npmjs.org/@polkadot-api/cli/-/cli-0.16.3.tgz" - integrity sha512-s+p3dFw1vOeyMMqhUbt1RFyqPZdR7vg6joS0v9wBvK3qX5xU+QfOOaMxXJ8fl0mJEbwoJnJsvVl4MzjsABaKCg== - dependencies: - "@commander-js/extra-typings" "^14.0.0" - "@polkadot-api/codegen" "0.20.0" - "@polkadot-api/ink-contracts" "0.4.3" - "@polkadot-api/json-rpc-provider" "0.0.4" - "@polkadot-api/known-chains" "0.9.15" - "@polkadot-api/legacy-provider" "0.3.6" - "@polkadot-api/metadata-compatibility" "0.4.1" - "@polkadot-api/observable-client" "0.17.0" - "@polkadot-api/polkadot-sdk-compat" "2.3.3" - "@polkadot-api/sm-provider" "0.1.14" - "@polkadot-api/smoldot" "0.3.14" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/substrate-client" "0.4.7" - "@polkadot-api/utils" "0.2.0" - "@polkadot-api/wasm-executor" "^0.2.2" - "@polkadot-api/ws-provider" "0.7.4" - "@types/node" "^24.10.1" - commander "^14.0.2" - execa "^9.6.0" - fs.promises.exists "^1.1.4" - ora "^9.0.0" - read-pkg "^10.0.0" - rxjs "^7.8.2" - tsc-prog "^2.3.0" - tsup "8.5.0" - typescript "^5.9.3" - write-package "^7.2.0" - -"@polkadot-api/codegen@0.20.0": - version "0.20.0" - resolved "https://registry.npmjs.org/@polkadot-api/codegen/-/codegen-0.20.0.tgz" - integrity sha512-akwPArm35UZcebUFtTKcEkdBLCjYyKweGw3/tT04p/EtM4OsQ1FxhRdXZ51ScBC3JVGCFQTUO2hNsd1E6YXvlw== - dependencies: - "@polkadot-api/ink-contracts" "0.4.3" - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/metadata-compatibility" "0.4.1" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/common-sdk-utils@0.1.0": - version "0.1.0" - resolved "https://registry.npmjs.org/@polkadot-api/common-sdk-utils/-/common-sdk-utils-0.1.0.tgz" - integrity sha512-cgA9fh8dfBai9b46XaaQmj9vwzyHStQjc/xrAvQksgF6SqvZ0yAfxVqLvGrsz/Xi3dsAdKLg09PybC7MUAMv9w== - -"@polkadot-api/descriptors@file:.papi/descriptors": - version "0.1.0-autogenerated.9947536328969970535" - resolved "file:.papi/descriptors" - -"@polkadot-api/ink-contracts@^0.4.1", "@polkadot-api/ink-contracts@>=0.4.0", "@polkadot-api/ink-contracts@0.4.3": - version "0.4.3" - resolved "https://registry.npmjs.org/@polkadot-api/ink-contracts/-/ink-contracts-0.4.3.tgz" - integrity sha512-Wl+4Dxjt0GAl+rADZEgrrqEesqX/xygTpX18TmzmspcKhb9QIZf9FJI8A5Sgtq0TKAOwsd1d/hbHVX3LgbXFXg== - dependencies: - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/json-rpc-provider-proxy@^0.1.0": - version "0.1.0" - resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz" - integrity sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg== - -"@polkadot-api/json-rpc-provider-proxy@0.2.7": - version "0.2.7" - resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.2.7.tgz" - integrity sha512-+HM4JQXzO2GPUD2++4GOLsmFL6LO8RoLvig0HgCLuypDgfdZMlwd8KnyGHjRnVEHA5X+kvXbk84TDcAXVxTazQ== - -"@polkadot-api/json-rpc-provider@^0.0.1", "@polkadot-api/json-rpc-provider@0.0.1": - version "0.0.1" - resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz" - integrity sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA== - -"@polkadot-api/json-rpc-provider@0.0.4": - version "0.0.4" - resolved "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.4.tgz" - integrity sha512-9cDijLIxzHOBuq6yHqpqjJ9jBmXrctjc1OFqU+tQrS96adQze3mTIH6DTgfb/0LMrqxzxffz1HQGrIlEH00WrA== - -"@polkadot-api/known-chains@0.9.15": - version "0.9.15" - resolved "https://registry.npmjs.org/@polkadot-api/known-chains/-/known-chains-0.9.15.tgz" - integrity sha512-VQGu2Anvnx0y0Ltd6sQB3aYzQFGsaQwf2znh+w4Oflaxln5lsjO/+trpXz/rdrdgyi0iafkhpeho/p/EGBwJ+A== - -"@polkadot-api/legacy-provider@0.3.6": - version "0.3.6" - resolved "https://registry.npmjs.org/@polkadot-api/legacy-provider/-/legacy-provider-0.3.6.tgz" - integrity sha512-JZQg0HVtBowFKxNrZdnMBKXmeSBD4yFlz6egEpvE97RXRvjaBzTaVuFFhBchngq9YmgFQewuWSoX5XSUW6hcEg== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.4" - "@polkadot-api/raw-client" "0.1.1" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/logs-provider@0.0.6": - version "0.0.6" - resolved "https://registry.npmjs.org/@polkadot-api/logs-provider/-/logs-provider-0.0.6.tgz" - integrity sha512-4WgHlvy+xee1ADaaVf6+MlK/+jGMtsMgAzvbQOJZnP4PfQuagoTqaeayk8HYKxXGphogLlPbD06tANxcb+nvAg== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.4" - -"@polkadot-api/merkleize-metadata@1.1.27": - version "1.1.27" - resolved "https://registry.npmjs.org/@polkadot-api/merkleize-metadata/-/merkleize-metadata-1.1.27.tgz" - integrity sha512-OdKwOzzrLL0Ju3pQA9LjeQEquMcD+KtLybUAO3fVxwjxD5cyI0RwillGoAIBJvfMaZpNxnxJnD+WzNjRcr7FiQ== - dependencies: - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/metadata-builders@0.13.7": - version "0.13.7" - resolved "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.13.7.tgz" - integrity sha512-xwggY8F/gtX7qGzz+jzP3DZvWgBWIIFQhk+r2MJ431CR+tNKeTtzGdwNocVrb9NYTK2naC9ckJS14nrNM6LWLw== - dependencies: - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/metadata-builders@0.3.2": - version "0.3.2" - resolved "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz" - integrity sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg== - dependencies: - "@polkadot-api/substrate-bindings" "0.6.0" - "@polkadot-api/utils" "0.1.0" - -"@polkadot-api/metadata-compatibility@0.4.1": - version "0.4.1" - resolved "https://registry.npmjs.org/@polkadot-api/metadata-compatibility/-/metadata-compatibility-0.4.1.tgz" - integrity sha512-mZt4Af6oPXEHAprrckJiSZkWRVf0mqwF+Bm+703rPsezLptQid9AjSzh1hkgIkOrPbg6IhWbmMhbuJVjx9VeQA== - dependencies: - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/substrate-bindings" "0.16.5" - -"@polkadot-api/observable-client@^0.3.0": - version "0.3.2" - resolved "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz" - integrity sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug== - dependencies: - "@polkadot-api/metadata-builders" "0.3.2" - "@polkadot-api/substrate-bindings" "0.6.0" - "@polkadot-api/utils" "0.1.0" - -"@polkadot-api/observable-client@0.17.0": - version "0.17.0" - resolved "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.17.0.tgz" - integrity sha512-hilb12Fg1JrlM/0nucMT85//EQltB53fmoh7YNBsZMiNpavn/3qGTO4s0JMlC/LBbddYg0nxA+DMkSVlapo7cQ== - dependencies: - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/substrate-client" "0.4.7" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/pjs-signer@0.6.17": - version "0.6.17" - resolved "https://registry.npmjs.org/@polkadot-api/pjs-signer/-/pjs-signer-0.6.17.tgz" - integrity sha512-bxFtyiNOchV0osh6m+1CaN4tkWF7Mo4IT9XPLZBwSybpHZgwmu2wbhgqBkVL98QMyGzud7NHfrJsTCgFU6jHGg== - dependencies: - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/polkadot-signer" "0.1.6" - "@polkadot-api/signers-common" "0.1.18" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/polkadot-sdk-compat@2.3.3": - version "2.3.3" - resolved "https://registry.npmjs.org/@polkadot-api/polkadot-sdk-compat/-/polkadot-sdk-compat-2.3.3.tgz" - integrity sha512-p30po+iv4trniSJ7UZiIt/rFInvtA9Tzg65EzuRkCaQAnh54a3MPp9w/q+x+SNLEcfzVLvf8LyPnMPOIpKuj5w== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.4" - -"@polkadot-api/polkadot-signer@0.1.6": - version "0.1.6" - resolved "https://registry.npmjs.org/@polkadot-api/polkadot-signer/-/polkadot-signer-0.1.6.tgz" - integrity sha512-X7ghAa4r7doETtjAPTb50IpfGtrBmy3BJM5WCfNKa1saK04VFY9w+vDn+hwEcM4p0PcDHt66Ts74hzvHq54d9A== - -"@polkadot-api/raw-client@0.1.1": - version "0.1.1" - resolved "https://registry.npmjs.org/@polkadot-api/raw-client/-/raw-client-0.1.1.tgz" - integrity sha512-HxalpNEo8JCYXfxKM5p3TrK8sEasTGMkGjBNLzD4TLye9IK2smdb5oTvp2yfkU1iuVBdmjr69uif4NaukOYo2g== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.4" - -"@polkadot-api/sdk-ink@^0.5.1": - version "0.5.1" - resolved "https://registry.npmjs.org/@polkadot-api/sdk-ink/-/sdk-ink-0.5.1.tgz" - integrity sha512-9pRnghjigivvgq7375hzkoazstvPDbc0YB01Jzw1/MYKcX+YJn1p/H8SAQTWbKlz2ohFgi1nwU52a0bsmKqb/Q== - dependencies: - "@ethereumjs/rlp" "^10.0.0" - "@polkadot-api/common-sdk-utils" "0.1.0" - "@polkadot-api/substrate-bindings" "^0.16.3" - abitype "^1.1.1" - viem "^2.37.9" - -"@polkadot-api/signer@0.2.11": - version "0.2.11" - resolved "https://registry.npmjs.org/@polkadot-api/signer/-/signer-0.2.11.tgz" - integrity sha512-32tqbJo6JDfc/lHg+nTveeunFRULonWoTQX9xbs70arr/tAyyZfljupdECRK8CVRx1777es/CQO3QVj8EpWtYg== - dependencies: - "@noble/hashes" "^2.0.1" - "@polkadot-api/merkleize-metadata" "1.1.27" - "@polkadot-api/polkadot-signer" "0.1.6" - "@polkadot-api/signers-common" "0.1.18" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/signers-common@0.1.18": - version "0.1.18" - resolved "https://registry.npmjs.org/@polkadot-api/signers-common/-/signers-common-0.1.18.tgz" - integrity sha512-UQXuRZoQ+jMolEpIPF0mVXcoqQ/382fHrSOgfK5sIvjeH0HPf4P+s3IwcnwyAdpHY2gdHXYlHd/SAw7Q1gJ4EA== - dependencies: - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/polkadot-signer" "0.1.6" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/utils" "0.2.0" - -"@polkadot-api/sm-provider@0.1.14": - version "0.1.14" - resolved "https://registry.npmjs.org/@polkadot-api/sm-provider/-/sm-provider-0.1.14.tgz" - integrity sha512-QQvoeBSIwnEm8IUhGA6sBU6LNh2v7SOuVOnF77ZD7P5ELTrdmQH2Tcn0W15qGTmTG45b3Z52XsKpuQbIJ7c7XA== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.4" - "@polkadot-api/json-rpc-provider-proxy" "0.2.7" - -"@polkadot-api/smoldot@>=0.3", "@polkadot-api/smoldot@0.3.14": - version "0.3.14" - resolved "https://registry.npmjs.org/@polkadot-api/smoldot/-/smoldot-0.3.14.tgz" - integrity sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ== - dependencies: - "@types/node" "^24.5.2" - smoldot "2.0.39" - -"@polkadot-api/substrate-bindings@^0.16.3", "@polkadot-api/substrate-bindings@0.16.5": - version "0.16.5" - resolved "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.16.5.tgz" - integrity sha512-QFgNlBmtLtiUGTCTurxcE6UZrbI2DaQ5/gyIiC2FYfEhStL8tl20b09FRYHcSjY+lxN42Rcf9HVX+MCFWLYlpQ== - 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": - version "0.6.0" - resolved "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz" - integrity sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw== - dependencies: - "@noble/hashes" "^1.3.1" - "@polkadot-api/utils" "0.1.0" - "@scure/base" "^1.1.1" - scale-ts "^1.6.0" - -"@polkadot-api/substrate-client@^0.1.2", "@polkadot-api/substrate-client@0.1.4": - version "0.1.4" - resolved "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz" - integrity sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.1" - "@polkadot-api/utils" "0.1.0" - -"@polkadot-api/substrate-client@0.4.7": - version "0.4.7" - resolved "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.4.7.tgz" - integrity sha512-Mmx9VKincVqfVQmq89gzDk4DN3uKwf8CxoqYvq+EiPUZ1QmMUc7X4QMwG1MXIlYdnm5LSXzn+2Jn8ik8xMgL+w== - 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": - version "0.1.0" - resolved "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz" - integrity sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA== - -"@polkadot-api/utils@0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.2.0.tgz" - integrity sha512-nY3i5fQJoAxU4n3bD7Fs208/KR2J95SGfVc58kDjbRYN5a84kWaGEqzjBNtP9oqht49POM8Bm9mbIrkvC1Bzuw== - -"@polkadot-api/wasm-executor@^0.2.2": - version "0.2.3" - resolved "https://registry.npmjs.org/@polkadot-api/wasm-executor/-/wasm-executor-0.2.3.tgz" - integrity sha512-B2h1o+Qlo9idpASaHvMSoViB2I5ko5OAfwfhYF8LQDkTADK0B+SeStzNj1Qn+FG34wqTuv7HzBCdjaUgzYINJQ== - -"@polkadot-api/ws-provider@0.7.4": - version "0.7.4" - resolved "https://registry.npmjs.org/@polkadot-api/ws-provider/-/ws-provider-0.7.4.tgz" - integrity sha512-mkk2p8wPht+ljU1xULCPMsLpNF7NHuGaufuDCIZZgopALaZpfVFJxc3qa9s6Xv8X3hM+TRoC5WknuD1ykRY99A== - dependencies: - "@polkadot-api/json-rpc-provider" "0.0.4" - "@polkadot-api/json-rpc-provider-proxy" "0.2.7" - "@types/ws" "^8.18.1" - ws "^8.18.3" - -"@polkadot-labs/hdkd-helpers@^0.0.25": - version "0.0.25" - resolved "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.25.tgz" - integrity sha512-GwHayBuyHKfzvGD0vG47NbjFeiK6rRQHQAn1syut9nt0mhXMg4yb3tJ//IyM317qWuDU3HbD2OIp5jKDEQz2/A== - dependencies: - "@noble/curves" "^2.0.0" - "@noble/hashes" "^2.0.0" - "@scure/base" "^2.0.0" - "@scure/sr25519" "^0.3.0" - scale-ts "^1.6.1" - -"@polkadot-labs/hdkd-helpers@~0.0.26": - version "0.0.26" - resolved "https://registry.npmjs.org/@polkadot-labs/hdkd-helpers/-/hdkd-helpers-0.0.26.tgz" - integrity sha512-mp3GCSiOQeh4aPt+DYBQq6UnX/tKgYUH5F75knjW3ATSA90ifEEWWjRan0Bddt4QKYKamaDGadK9GbVREgzQFw== - 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@^0.0.25": - version "0.0.25" - resolved "https://registry.npmjs.org/@polkadot-labs/hdkd/-/hdkd-0.0.25.tgz" - integrity sha512-+yZJC1TE4ZKdfoILw8nGxu3H/klrYXm9GdVB0kcyQDecq320ThUmM1M4l8d1F/3QD0Nez9NwHi9t5B++OgJU5A== - dependencies: - "@polkadot-labs/hdkd-helpers" "~0.0.26" - -"@polkadot/api-augment@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-16.5.3.tgz" - integrity sha512-9+8YKSS66x9qpWS+ZQ/FSm9P4mgE+icD53oAmeIykriPW2gcSTAiNufLwAjmAJAkOLcqbTD7LPjFW6xFlmtYsA== - dependencies: - "@polkadot/api-base" "16.5.3" - "@polkadot/rpc-augment" "16.5.3" - "@polkadot/types" "16.5.3" - "@polkadot/types-augment" "16.5.3" - "@polkadot/types-codec" "16.5.3" - "@polkadot/util" "^13.5.9" - tslib "^2.8.1" - -"@polkadot/api-base@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/api-base/-/api-base-16.5.3.tgz" - integrity sha512-M1+pY6OFQ1uOB73VQMt2JAGq/UVISVQJISqyfjiUllUc0qIzaDMkcZxRqE34Lwaib3fD3RuIpG6dXqCL9rdzJQ== - dependencies: - "@polkadot/rpc-core" "16.5.3" - "@polkadot/types" "16.5.3" - "@polkadot/util" "^13.5.9" - rxjs "^7.8.1" - tslib "^2.8.1" - -"@polkadot/api-derive@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-16.5.3.tgz" - integrity sha512-nMsnSC/N1SK1kNhgh2FhrrR1S8bTVH+3WsuBHFRzl+txKHq232IeIn9LpebSvgZdd77PaKaYBxbhYcNaA8Ypew== - dependencies: - "@polkadot/api" "16.5.3" - "@polkadot/api-augment" "16.5.3" - "@polkadot/api-base" "16.5.3" - "@polkadot/rpc-core" "16.5.3" - "@polkadot/types" "16.5.3" - "@polkadot/types-codec" "16.5.3" - "@polkadot/util" "^13.5.9" - "@polkadot/util-crypto" "^13.5.9" - rxjs "^7.8.1" - tslib "^2.8.1" - -"@polkadot/api@^16.4.6", "@polkadot/api@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/api/-/api-16.5.3.tgz" - integrity sha512-Ptwo0f5Qonmus7KIklsbFcGTdHtNjbTAwl5GGI8Mp0dmBc7Y/ISJpIJX49UrG6FhW6COMa0ItsU87XIWMRwI/Q== - dependencies: - "@polkadot/api-augment" "16.5.3" - "@polkadot/api-base" "16.5.3" - "@polkadot/api-derive" "16.5.3" - "@polkadot/keyring" "^13.5.9" - "@polkadot/rpc-augment" "16.5.3" - "@polkadot/rpc-core" "16.5.3" - "@polkadot/rpc-provider" "16.5.3" - "@polkadot/types" "16.5.3" - "@polkadot/types-augment" "16.5.3" - "@polkadot/types-codec" "16.5.3" - "@polkadot/types-create" "16.5.3" - "@polkadot/types-known" "16.5.3" - "@polkadot/util" "^13.5.9" - "@polkadot/util-crypto" "^13.5.9" - eventemitter3 "^5.0.1" - rxjs "^7.8.1" - tslib "^2.8.1" - -"@polkadot/keyring@^13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.9.tgz" - integrity sha512-bMCpHDN7U8ytxawjBZ89/he5s3AmEZuOdkM/ABcorh/flXNPfyghjFK27Gy4OKoFxX52yJ2sTHR4NxM87GuFXQ== - dependencies: - "@polkadot/util" "13.5.9" - "@polkadot/util-crypto" "13.5.9" - tslib "^2.8.0" - -"@polkadot/networks@^13.5.9", "@polkadot/networks@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz" - integrity sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA== - dependencies: - "@polkadot/util" "13.5.9" - "@substrate/ss58-registry" "^1.51.0" - tslib "^2.8.0" - -"@polkadot/networks@14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/networks/-/networks-14.0.1.tgz" - integrity sha512-wGlBtXDkusRAj4P7uxfPz80gLO1+j99MLBaQi3bEym2xrFrFhgIWVHOZlBit/1PfaBjhX2Z8XjRxaM2w1p7w2w== - dependencies: - "@polkadot/util" "14.0.1" - "@substrate/ss58-registry" "^1.51.0" - tslib "^2.8.0" - -"@polkadot/rpc-augment@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-16.5.3.tgz" - integrity sha512-q3Y+b0FSwbYe8Qopd4In+9KCL3eH5QmGVvimX7Z8+cvQ9+h+JUA6TP1bfpWBmYJRKlolaljsBQPBWoubchmxSw== - dependencies: - "@polkadot/rpc-core" "16.5.3" - "@polkadot/types" "16.5.3" - "@polkadot/types-codec" "16.5.3" - "@polkadot/util" "^13.5.9" - tslib "^2.8.1" - -"@polkadot/rpc-core@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-16.5.3.tgz" - integrity sha512-UYEIRhO/1uTz/rpWLwUN9Re3c4fuTs0I9RR8dHKpKsH3jZTs1M3CtqME3NNzpGqApY1xb9tZemU/0GfHjCpeBQ== - dependencies: - "@polkadot/rpc-augment" "16.5.3" - "@polkadot/rpc-provider" "16.5.3" - "@polkadot/types" "16.5.3" - "@polkadot/util" "^13.5.9" - rxjs "^7.8.1" - tslib "^2.8.1" - -"@polkadot/rpc-provider@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-16.5.3.tgz" - integrity sha512-O7hD82HwjT4XJ4i/G58B52RSDM7arHXSpzahZKz4/wtb4x6d6b4JVdfZoskInadARFi5RwIWCrftwPtpRH81Fw== - dependencies: - "@polkadot/keyring" "^13.5.9" - "@polkadot/types" "16.5.3" - "@polkadot/types-support" "16.5.3" - "@polkadot/util" "^13.5.9" - "@polkadot/util-crypto" "^13.5.9" - "@polkadot/x-fetch" "^13.5.9" - "@polkadot/x-global" "^13.5.9" - "@polkadot/x-ws" "^13.5.9" - eventemitter3 "^5.0.1" - mock-socket "^9.3.1" - nock "^13.5.5" - tslib "^2.8.1" - optionalDependencies: - "@substrate/connect" "0.8.11" - -"@polkadot/types-augment@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-16.5.3.tgz" - integrity sha512-SfS4arJUxW6BeCEhLMVPrZwWOLte69k5+/lvEKOKHQA8Mz0MEkD4uqGZGibDjgBgdnu8N+3b+rs+Fn3YfZu4yA== - dependencies: - "@polkadot/types" "16.5.3" - "@polkadot/types-codec" "16.5.3" - "@polkadot/util" "^13.5.9" - tslib "^2.8.1" - -"@polkadot/types-codec@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-16.5.3.tgz" - integrity sha512-b+oKMrIZrsFH4pPwvGQ6lMS8oFrYAGMy9QSbytA+KDmXAgTCtShz5XGvdQabvsGCjJ45EKgkKpKynVcYh3gk8g== - dependencies: - "@polkadot/util" "^13.5.9" - "@polkadot/x-bigint" "^13.5.9" - tslib "^2.8.1" - -"@polkadot/types-create@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/types-create/-/types-create-16.5.3.tgz" - integrity sha512-XGnBLNamPh7eQGcHNGFghA/prH7z2BsQ+9EVSbHCvw9ENr/Ow24mmmkZyMG5WM/5I6/4HRdfwFJucYt1GL/p9g== - dependencies: - "@polkadot/types-codec" "16.5.3" - "@polkadot/util" "^13.5.9" - tslib "^2.8.1" - -"@polkadot/types-known@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/types-known/-/types-known-16.5.3.tgz" - integrity sha512-ZLAZI24bQD0C9CJWYHxrLG8QSmzRzfWa51rlSNwZ9Atsc3R+GeX1YZGc9IljpQxYJCHrCqd6X8TXpAmEJdnbKw== - dependencies: - "@polkadot/networks" "^13.5.9" - "@polkadot/types" "16.5.3" - "@polkadot/types-codec" "16.5.3" - "@polkadot/types-create" "16.5.3" - "@polkadot/util" "^13.5.9" - tslib "^2.8.1" - -"@polkadot/types-support@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/types-support/-/types-support-16.5.3.tgz" - integrity sha512-ggyIRV+4Kn+aG1PiVT0PE00pAqMveyS3CuFsW9gJnKxeev4VrGfr08R4vw/61D7uIfpilkQdkXNgXAbeN09Mxg== - dependencies: - "@polkadot/util" "^13.5.9" - tslib "^2.8.1" - -"@polkadot/types@16.5.3": - version "16.5.3" - resolved "https://registry.npmjs.org/@polkadot/types/-/types-16.5.3.tgz" - integrity sha512-xy9uv/X4iT7uJ7TNCoqbcMkR8ePHwNW6DgpOU+1y1zc/KSu9ZC5i+haFOL68BpmR/QXk99YfuHoKwXvteDmykw== - dependencies: - "@polkadot/keyring" "^13.5.9" - "@polkadot/types-augment" "16.5.3" - "@polkadot/types-codec" "16.5.3" - "@polkadot/types-create" "16.5.3" - "@polkadot/util" "^13.5.9" - "@polkadot/util-crypto" "^13.5.9" - rxjs "^7.8.1" - tslib "^2.8.1" - -"@polkadot/util-crypto@^13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz" - integrity sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg== - dependencies: - "@noble/curves" "^1.3.0" - "@noble/hashes" "^1.3.3" - "@polkadot/networks" "13.5.9" - "@polkadot/util" "13.5.9" - "@polkadot/wasm-crypto" "^7.5.3" - "@polkadot/wasm-util" "^7.5.3" - "@polkadot/x-bigint" "13.5.9" - "@polkadot/x-randomvalues" "13.5.9" - "@scure/base" "^1.1.7" - tslib "^2.8.0" - -"@polkadot/util-crypto@^14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-14.0.1.tgz" - integrity sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A== - dependencies: - "@noble/curves" "^1.3.0" - "@noble/hashes" "^1.3.3" - "@polkadot/networks" "14.0.1" - "@polkadot/util" "14.0.1" - "@polkadot/wasm-crypto" "^7.5.3" - "@polkadot/wasm-util" "^7.5.3" - "@polkadot/x-bigint" "14.0.1" - "@polkadot/x-randomvalues" "14.0.1" - "@scure/base" "^1.1.7" - "@scure/sr25519" "^0.2.0" - tslib "^2.8.0" - -"@polkadot/util-crypto@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz" - integrity sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg== - dependencies: - "@noble/curves" "^1.3.0" - "@noble/hashes" "^1.3.3" - "@polkadot/networks" "13.5.9" - "@polkadot/util" "13.5.9" - "@polkadot/wasm-crypto" "^7.5.3" - "@polkadot/wasm-util" "^7.5.3" - "@polkadot/x-bigint" "13.5.9" - "@polkadot/x-randomvalues" "13.5.9" - "@scure/base" "^1.1.7" - tslib "^2.8.0" - -"@polkadot/util@*", "@polkadot/util@^13.5.9", "@polkadot/util@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz" - integrity sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw== - 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.1.6" - bn.js "^5.2.1" - tslib "^2.8.0" - -"@polkadot/util@14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/util/-/util-14.0.1.tgz" - integrity sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg== - 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.1.6" - bn.js "^5.2.1" - tslib "^2.8.0" - -"@polkadot/wasm-bridge@7.5.3": - version "7.5.3" - resolved "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.3.tgz" - integrity sha512-mUvwwNH+uP1wqpMuHjmEwHxRIaVc5csmb+ukycWQGhzwhpXe/0fvBEU2TQ8kwgqO2MU0FS3hN/QcIWKfPRJgxQ== - dependencies: - "@polkadot/wasm-util" "7.5.3" - tslib "^2.7.0" - -"@polkadot/wasm-crypto-asmjs@7.5.3": - version "7.5.3" - resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.3.tgz" - integrity sha512-fSbbjI+4p0U3PQ8nOz/3p7euHriSdh+2CSywNuXHa8fMaYlMqCKt9K7+HI8CQ4RZNvZWDq+Py1nEDEkM4rZrvw== - dependencies: - tslib "^2.7.0" - -"@polkadot/wasm-crypto-init@7.5.3": - version "7.5.3" - resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.3.tgz" - integrity sha512-KvUpxqvW70XhuDiw/N6rM8fQ7zRjIFblw+vdJ0/wwyagwg9jrYNA9TMei5ksQd9sxGCGXN/xJmwHJXuUjkocmg== - dependencies: - "@polkadot/wasm-bridge" "7.5.3" - "@polkadot/wasm-crypto-asmjs" "7.5.3" - "@polkadot/wasm-crypto-wasm" "7.5.3" - "@polkadot/wasm-util" "7.5.3" - tslib "^2.7.0" - -"@polkadot/wasm-crypto-wasm@7.5.3": - version "7.5.3" - resolved "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.3.tgz" - integrity sha512-fc88+HyVxebB/40GVgGUOLBqyO3C571DXWPTFmtt5EX9H8gw7Jg0Bkitz7hgSVP2x4FjXpqS9UNTJ8trVH0x1A== - dependencies: - "@polkadot/wasm-util" "7.5.3" - tslib "^2.7.0" - -"@polkadot/wasm-crypto@^7.5.3": - version "7.5.3" - resolved "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.5.3.tgz" - integrity sha512-dmKUM9vw1wrnCHGuIeOtQo1pwuSF7fkyF4TYimTn3tAa0+3cDctYBErtGxgUeqP0Bo4Q0Of4/vnHlSk5Rbt9Uw== - dependencies: - "@polkadot/wasm-bridge" "7.5.3" - "@polkadot/wasm-crypto-asmjs" "7.5.3" - "@polkadot/wasm-crypto-init" "7.5.3" - "@polkadot/wasm-crypto-wasm" "7.5.3" - "@polkadot/wasm-util" "7.5.3" - tslib "^2.7.0" - -"@polkadot/wasm-util@*", "@polkadot/wasm-util@^7.5.3", "@polkadot/wasm-util@7.5.3": - version "7.5.3" - resolved "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.3.tgz" - integrity sha512-hBr9bbjS+Yr7DrDUSkIIuvlTSoAlI8WXuo9YEB4C76j130u/cl+zyq6Iy/WnaTE6QH+8i9DhM8QTety6TqYnUQ== - dependencies: - tslib "^2.7.0" - -"@polkadot/x-bigint@^13.5.9", "@polkadot/x-bigint@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz" - integrity sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g== - dependencies: - "@polkadot/x-global" "13.5.9" - tslib "^2.8.0" - -"@polkadot/x-bigint@14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-14.0.1.tgz" - integrity sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ== - dependencies: - "@polkadot/x-global" "14.0.1" - tslib "^2.8.0" - -"@polkadot/x-fetch@^13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.5.9.tgz" - integrity sha512-urwXQZtT4yYROiRdJS6zHu18J/jCoAGpbgPIAjwdqjT11t9XIq4SjuPMxD19xBRhbYe9ocWV8i1KHuoMbZgKbA== - dependencies: - "@polkadot/x-global" "13.5.9" - node-fetch "^3.3.2" - tslib "^2.8.0" - -"@polkadot/x-global@^13.5.9", "@polkadot/x-global@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.9.tgz" - integrity sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA== - dependencies: - tslib "^2.8.0" - -"@polkadot/x-global@14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/x-global/-/x-global-14.0.1.tgz" - integrity sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA== - dependencies: - tslib "^2.8.0" - -"@polkadot/x-randomvalues@*", "@polkadot/x-randomvalues@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz" - integrity sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw== - dependencies: - "@polkadot/x-global" "13.5.9" - tslib "^2.8.0" - -"@polkadot/x-randomvalues@14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-14.0.1.tgz" - integrity sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg== - dependencies: - "@polkadot/x-global" "14.0.1" - tslib "^2.8.0" - -"@polkadot/x-textdecoder@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.9.tgz" - integrity sha512-W2HhVNUbC/tuFdzNMbnXAWsIHSg9SC9QWDNmFD3nXdSzlXNgL8NmuiwN2fkYvCQBtp/XSoy0gDLx0C+Fo19cfw== - dependencies: - "@polkadot/x-global" "13.5.9" - tslib "^2.8.0" - -"@polkadot/x-textdecoder@14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-14.0.1.tgz" - integrity sha512-CcWiPCuPVJsNk4Vq43lgFHqLRBQHb4r9RD7ZIYgmwoebES8TNm4g2ew9ToCzakFKSpzKu6I07Ne9wv/dt5zLuw== - dependencies: - "@polkadot/x-global" "14.0.1" - tslib "^2.8.0" - -"@polkadot/x-textencoder@13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.9.tgz" - integrity sha512-SG0MHnLUgn1ZxFdm0KzMdTHJ47SfqFhdIPMcGA0Mg/jt2rwrfrP3jtEIJMsHfQpHvfsNPfv55XOMmoPWuQnP/Q== - dependencies: - "@polkadot/x-global" "13.5.9" - tslib "^2.8.0" - -"@polkadot/x-textencoder@14.0.1": - version "14.0.1" - resolved "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-14.0.1.tgz" - integrity sha512-VY51SpQmF1ccmAGLfxhYnAe95Spfz049WZ/+kK4NfsGF9WejxVdU53Im5C80l45r8qHuYQsCWU3+t0FNunh2Kg== - dependencies: - "@polkadot/x-global" "14.0.1" - tslib "^2.8.0" - -"@polkadot/x-ws@^13.5.9": - version "13.5.9" - resolved "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.5.9.tgz" - integrity sha512-NKVgvACTIvKT8CjaQu9d0dERkZsWIZngX/4NVSjc01WHmln4F4y/zyBdYn/Z2V0Zw28cISx+lB4qxRmqTe7gbg== - dependencies: - "@polkadot/x-global" "13.5.9" - tslib "^2.8.0" - ws "^8.18.0" - -"@rollup/rollup-linux-x64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz" - integrity sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w== - -"@rollup/rollup-linux-x64-musl@4.53.3": - version "4.53.3" - resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz" - integrity sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q== - -"@rx-state/core@^0.1.4": - version "0.1.4" - resolved "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz" - integrity sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ== - -"@scure/base@^1.1.1", "@scure/base@^1.1.7", "@scure/base@~1.2.2", "@scure/base@~1.2.4", "@scure/base@~1.2.5": - version "1.2.6" - resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz" - integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== - -"@scure/base@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@scure/base/-/base-2.0.0.tgz" - integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== - -"@scure/bip32@^1.5.0", "@scure/bip32@^1.7.0", "@scure/bip32@1.7.0": - version "1.7.0" - resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz" - integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== - dependencies: - "@noble/curves" "~1.9.0" - "@noble/hashes" "~1.8.0" - "@scure/base" "~1.2.5" - -"@scure/bip32@1.6.2": - version "1.6.2" - resolved "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz" - integrity sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw== - dependencies: - "@noble/curves" "~1.8.1" - "@noble/hashes" "~1.7.1" - "@scure/base" "~1.2.2" - -"@scure/bip39@^1.4.0", "@scure/bip39@^1.6.0", "@scure/bip39@1.6.0": - version "1.6.0" - resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz" - integrity sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A== - dependencies: - "@noble/hashes" "~1.8.0" - "@scure/base" "~1.2.5" - -"@scure/bip39@1.5.4": - version "1.5.4" - resolved "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz" - integrity sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA== - dependencies: - "@noble/hashes" "~1.7.1" - "@scure/base" "~1.2.4" - -"@scure/sr25519@^0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.2.0.tgz" - integrity sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg== - dependencies: - "@noble/curves" "~1.9.2" - "@noble/hashes" "~1.8.0" - -"@scure/sr25519@^0.3.0": - version "0.3.0" - resolved "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.3.0.tgz" - integrity sha512-SKsinX2sImunfcsH3seGrwH/OayBwwaJqVN8J1cJBNRCfbBq5q0jyTKGa9PcW1HWv9vXT6Yuq41JsxFLvF59ew== - dependencies: - "@noble/curves" "~2.0.0" - "@noble/hashes" "~2.0.0" - -"@sec-ant/readable-stream@^0.4.1": - version "0.4.1" - resolved "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz" - integrity sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg== - -"@sindresorhus/merge-streams@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz" - integrity sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ== - -"@substrate/connect-extension-protocol@^2.0.0": - version "2.2.2" - resolved "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz" - integrity sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA== - -"@substrate/connect-known-chains@^1.1.5": - version "1.10.3" - resolved "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz" - integrity sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w== - -"@substrate/connect@0.8.11": - version "0.8.11" - resolved "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz" - integrity sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw== - dependencies: - "@substrate/connect-extension-protocol" "^2.0.0" - "@substrate/connect-known-chains" "^1.1.5" - "@substrate/light-client-extension-helpers" "^1.0.0" - smoldot "2.0.26" - -"@substrate/light-client-extension-helpers@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz" - integrity sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg== - dependencies: - "@polkadot-api/json-rpc-provider" "^0.0.1" - "@polkadot-api/json-rpc-provider-proxy" "^0.1.0" - "@polkadot-api/observable-client" "^0.3.0" - "@polkadot-api/substrate-client" "^0.1.2" - "@substrate/connect-extension-protocol" "^2.0.0" - "@substrate/connect-known-chains" "^1.1.5" - rxjs "^7.8.1" - -"@substrate/ss58-registry@^1.51.0": - version "1.51.0" - resolved "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz" - integrity sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ== - -"@tsconfig/node10@^1.0.7": - version "1.0.12" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz" - integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.4" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" - integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== - -"@types/bn.js@^5.1.6": - version "5.2.0" - resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz" - integrity sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q== - dependencies: - "@types/node" "*" - -"@types/chai@^5.0.1": - version "5.2.3" - resolved "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz" - integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== - dependencies: - "@types/deep-eql" "*" - assertion-error "^2.0.1" - -"@types/deep-eql@*": - version "4.0.2" - resolved "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz" - integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== - -"@types/estree@1.0.8": - version "1.0.8" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" - integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== - -"@types/mocha@^10.0.10": - version "10.0.10" - resolved "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz" - integrity sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q== - -"@types/node@*", "@types/node@^22.18.0": - version "22.19.1" - resolved "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz" - integrity sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ== - dependencies: - undici-types "~6.21.0" - -"@types/node@^24.10.1": - version "24.10.1" - resolved "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz" - integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== - dependencies: - undici-types "~7.16.0" - -"@types/node@^24.5.2": - version "24.10.1" - resolved "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz" - integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== - dependencies: - undici-types "~7.16.0" - -"@types/node@22.7.5": - version "22.7.5" - resolved "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz" - integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== - dependencies: - undici-types "~6.19.2" - -"@types/normalize-package-data@^2.4.3", "@types/normalize-package-data@^2.4.4": - version "2.4.4" - resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz" - integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== - -"@types/ws@^8.18.1": - version "8.18.1" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz" - integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== - dependencies: - "@types/node" "*" - -abitype@^1.0.6, abitype@^1.0.9, abitype@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/abitype/-/abitype-1.2.0.tgz" - integrity sha512-fD3ROjckUrWsybaSor2AdWxzA0e/DSyV2dA4aYd7bd8orHsoJjl09fOgKfUkTDfk0BsDGBf4NBgu/c7JoS2Npw== - -abitype@1.0.8: - version "1.0.8" - resolved "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz" - integrity sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg== - -abitype@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz" - integrity sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A== - -acorn-walk@^8.1.1: - version "8.3.4" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" - integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== - dependencies: - acorn "^8.11.0" - -acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: - version "8.15.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== - -aes-js@4.0.0-beta.5: - version "4.0.0-beta.5" - resolved "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz" - integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.2.2" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" - integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.3" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" - integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== - -any-promise@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" - integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -assert@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz" - integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== - dependencies: - call-bind "^1.0.2" - is-nan "^1.3.2" - object-is "^1.1.5" - object.assign "^4.1.4" - util "^0.12.5" - -assertion-error@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz" - integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== - -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -bn.js@^5.2.1: - version "5.2.2" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz" - integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== - -brace-expansion@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== - dependencies: - balanced-match "^1.0.0" - -browser-stdout@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -bundle-require@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz" - integrity sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA== - dependencies: - load-tsconfig "^0.2.3" - -cac@^6.7.14: - version "6.7.14" - resolved "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz" - integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== - -call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" - integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.7, call-bind@^1.0.8: - version "1.0.8" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz" - integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== - dependencies: - call-bind-apply-helpers "^1.0.0" - es-define-property "^1.0.0" - get-intrinsic "^1.2.4" - set-function-length "^1.2.2" - -call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" - integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== - dependencies: - call-bind-apply-helpers "^1.0.2" - get-intrinsic "^1.3.0" - -camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -chai@^6.0.1: - version "6.2.1" - resolved "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz" - integrity sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg== - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^5.6.2: - version "5.6.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz" - integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== - -chokidar@^4.0.1, chokidar@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz" - integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== - dependencies: - readdirp "^4.0.1" - -cli-cursor@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" - integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== - dependencies: - restore-cursor "^5.0.0" - -cli-spinners@^3.2.0: - version "3.3.0" - resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz" - integrity sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ== - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -commander@^14.0.2, commander@~14.0.0: - version "14.0.2" - resolved "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz" - integrity sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ== - -commander@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - -confbox@^0.1.8: - version "0.1.8" - resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz" - integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== - -consola@^3.4.0: - version "3.4.2" - resolved "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz" - integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== - -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - -cross-spawn@^7.0.6: - version "7.0.6" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" - integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -data-uri-to-buffer@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz" - integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== - -debug@^4.1.0, debug@^4.3.5, debug@^4.4.0: - version "4.4.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -deepmerge-ts@^7.1.0: - version "7.1.5" - resolved "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz" - integrity sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw== - -define-data-property@^1.0.1, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -define-properties@^1.1.3, define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -detect-indent@^7.0.1: - version "7.0.2" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz" - integrity sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A== - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -diff@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz" - integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== - -dotenv@17.2.1: - version "17.2.1" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz" - integrity sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ== - -dunder-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" - integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== - dependencies: - call-bind-apply-helpers "^1.0.1" - es-errors "^1.3.0" - gopd "^1.2.0" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -es-define-property@^1.0.0, es-define-property@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" - integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== - dependencies: - es-errors "^1.3.0" - -esbuild@^0.25.0, esbuild@>=0.18: - version "0.25.12" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz" - integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== - 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" - -escalade@^3.1.1: - version "3.2.0" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" - integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -ethers@^6.13.5: - version "6.16.0" - resolved "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz" - integrity sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A== - 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" - -eventemitter3@^5.0.1, eventemitter3@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" - integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== - -execa@^9.6.0: - version "9.6.1" - resolved "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz" - integrity sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA== - dependencies: - "@sindresorhus/merge-streams" "^4.0.0" - cross-spawn "^7.0.6" - figures "^6.1.0" - get-stream "^9.0.0" - 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.2.0" - signal-exit "^4.1.0" - strip-final-newline "^4.0.0" - yoctocolors "^2.1.1" - -fdir@^6.5.0: - version "6.5.0" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" - integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== - -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - -figures@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz" - integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg== - dependencies: - is-unicode-supported "^2.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -fix-dts-default-cjs-exports@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz" - integrity sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg== - dependencies: - magic-string "^0.30.17" - mlly "^1.7.4" - rollup "^4.34.8" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -for-each@^0.3.5: - version "0.3.5" - resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" - integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== - dependencies: - is-callable "^1.2.7" - -foreground-child@^3.1.0: - version "3.3.1" - resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz" - integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== - dependencies: - cross-spawn "^7.0.6" - signal-exit "^4.0.1" - -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - -fs.promises.exists@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/fs.promises.exists/-/fs.promises.exists-1.1.4.tgz" - integrity sha512-lJzUGWbZn8vhGWBedA+RYjB/BeJ+3458ljUfmplqhIeb6ewzTFWNPCR1HCiYCkXV9zxcHz9zXkJzMsEgDLzh3Q== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -generator-function@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz" - integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-east-asian-width@^1.3.0: - version "1.4.0" - resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz" - integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== - -get-intrinsic@^1.2.4, get-intrinsic@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" - integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== - dependencies: - call-bind-apply-helpers "^1.0.2" - es-define-property "^1.0.1" - es-errors "^1.3.0" - es-object-atoms "^1.1.1" - function-bind "^1.1.2" - get-proto "^1.0.1" - gopd "^1.2.0" - has-symbols "^1.1.0" - hasown "^2.0.2" - math-intrinsics "^1.1.0" - -get-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" - integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== - dependencies: - dunder-proto "^1.0.1" - es-object-atoms "^1.0.0" - -get-stream@^9.0.0: - version "9.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz" - integrity sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA== - dependencies: - "@sec-ant/readable-stream" "^0.4.1" - is-stream "^4.0.1" - -glob@^10.4.5: - version "10.5.0" - resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" - integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -gopd@^1.0.1, gopd@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" - integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-symbols@^1.0.3, has-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" - integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== - -has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -he@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hosted-git-info@^7.0.0: - version "7.0.2" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz" - integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== - dependencies: - lru-cache "^10.0.1" - -hosted-git-info@^9.0.0: - version "9.0.2" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz" - integrity sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg== - dependencies: - lru-cache "^11.1.0" - -human-signals@^8.0.1: - version "8.0.1" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz" - integrity sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ== - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -index-to-position@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz" - integrity sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw== - -inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-arguments@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz" - integrity sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA== - dependencies: - call-bound "^1.0.2" - has-tostringtag "^1.0.2" - -is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-function@^1.0.7: - version "1.1.2" - resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz" - integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== - dependencies: - call-bound "^1.0.4" - generator-function "^2.0.0" - get-proto "^1.0.1" - has-tostringtag "^1.0.2" - safe-regex-test "^1.1.0" - -is-interactive@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz" - integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== - -is-nan@^1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz" - integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-plain-obj@^4.0.0, is-plain-obj@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - -is-regex@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz" - integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== - dependencies: - call-bound "^1.0.2" - gopd "^1.2.0" - has-tostringtag "^1.0.2" - hasown "^2.0.2" - -is-stream@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz" - integrity sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A== - -is-typed-array@^1.1.3: - version "1.1.15" - resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz" - integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== - dependencies: - which-typed-array "^1.1.16" - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-unicode-supported@^2.0.0, is-unicode-supported@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz" - integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isows@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz" - integrity sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw== - -isows@1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz" - integrity sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg== - -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - -joycon@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" - integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz" - integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== - dependencies: - argparse "^2.0.1" - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -lilconfig@^3.1.1: - version "3.1.3" - resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz" - integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -load-tsconfig@^0.2.3: - version "0.2.5" - resolved "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz" - integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" - integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== - -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-symbols@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz" - integrity sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg== - dependencies: - is-unicode-supported "^2.0.0" - yoctocolors "^2.1.1" - -lru-cache@^10.0.1, lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - -lru-cache@^11.1.0: - version "11.2.4" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz" - integrity sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg== - -magic-string@^0.30.17: - version "0.30.21" - resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz" - integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.5" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -math-intrinsics@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" - integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== - -mimic-function@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" - integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== - -minimatch@^9.0.4, minimatch@^9.0.5: - version "9.0.5" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== - dependencies: - brace-expansion "^2.0.1" - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - -mlly@^1.7.4: - version "1.8.0" - resolved "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz" - integrity sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== - dependencies: - acorn "^8.15.0" - pathe "^2.0.3" - pkg-types "^1.3.1" - ufo "^1.6.1" - -mocha@^11.1.0: - version "11.7.5" - resolved "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz" - integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== - dependencies: - browser-stdout "^1.3.1" - chokidar "^4.0.1" - debug "^4.3.5" - diff "^7.0.0" - escape-string-regexp "^4.0.0" - find-up "^5.0.0" - glob "^10.4.5" - he "^1.2.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - log-symbols "^4.1.0" - minimatch "^9.0.5" - ms "^2.1.3" - picocolors "^1.1.1" - serialize-javascript "^6.0.2" - strip-json-comments "^3.1.1" - supports-color "^8.1.1" - workerpool "^9.2.0" - yargs "^17.7.2" - yargs-parser "^21.1.1" - yargs-unparser "^2.0.0" - -mock-socket@^9.3.1: - version "9.3.1" - resolved "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz" - integrity sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw== - -ms@^2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -mz@^2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -nock@^13.5.5: - version "13.5.6" - resolved "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz" - integrity sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - propagate "^2.0.0" - -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - -node-fetch@^3.3.2: - version "3.3.2" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz" - integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - -normalize-package-data@^6.0.0: - version "6.0.2" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz" - integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== - dependencies: - hosted-git-info "^7.0.0" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - -normalize-package-data@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-8.0.0.tgz" - integrity sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ== - dependencies: - hosted-git-info "^9.0.0" - semver "^7.3.5" - validate-npm-package-license "^3.0.4" - -npm-run-path@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz" - integrity sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA== - dependencies: - path-key "^4.0.0" - unicorn-magic "^0.3.0" - -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4: - version "4.1.7" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz" - integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== - dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" - es-object-atoms "^1.0.0" - has-symbols "^1.1.0" - object-keys "^1.1.1" - -onetime@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" - integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== - dependencies: - mimic-function "^5.0.0" - -ora@^9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz" - integrity sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A== - dependencies: - chalk "^5.6.2" - cli-cursor "^5.0.0" - cli-spinners "^3.2.0" - is-interactive "^2.0.0" - is-unicode-supported "^2.1.0" - log-symbols "^7.0.1" - stdin-discarder "^0.2.2" - string-width "^8.1.0" - strip-ansi "^7.1.2" - -ox@0.6.7: - version "0.6.7" - resolved "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz" - integrity sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA== - dependencies: - "@adraffy/ens-normalize" "^1.10.1" - "@noble/curves" "^1.6.0" - "@noble/hashes" "^1.5.0" - "@scure/bip32" "^1.5.0" - "@scure/bip39" "^1.4.0" - abitype "^1.0.6" - eventemitter3 "5.0.1" - -ox@0.9.6: - version "0.9.6" - resolved "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz" - integrity sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg== - dependencies: - "@adraffy/ens-normalize" "^1.11.0" - "@noble/ciphers" "^1.3.0" - "@noble/curves" "1.9.1" - "@noble/hashes" "^1.8.0" - "@scure/bip32" "^1.7.0" - "@scure/bip39" "^1.6.0" - abitype "^1.0.9" - eventemitter3 "5.0.1" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - -parse-json@^8.0.0, parse-json@^8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz" - integrity sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ== - dependencies: - "@babel/code-frame" "^7.26.2" - index-to-position "^1.1.0" - type-fest "^4.39.1" - -parse-ms@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz" - integrity sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== - -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -pathe@^2.0.1, pathe@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" - integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== - -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - -"picomatch@^3 || ^4", picomatch@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" - integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== - -pirates@^4.0.1: - version "4.0.7" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz" - integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== - -pkg-types@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz" - integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== - dependencies: - confbox "^0.1.8" - mlly "^1.7.4" - pathe "^2.0.1" - -polkadot-api@^1.22.0, polkadot-api@^1.8.1, polkadot-api@>=1.19.0, polkadot-api@>=1.21.0: - version "1.22.0" - resolved "https://registry.npmjs.org/polkadot-api/-/polkadot-api-1.22.0.tgz" - integrity sha512-uREBLroPbnJxBBQ+qSkKLF493qukX4PAg32iThlELrZdxfNNgro6nvWRdVmBv73tFHvf+nyWWHKTx1c57nbixg== - dependencies: - "@polkadot-api/cli" "0.16.3" - "@polkadot-api/ink-contracts" "0.4.3" - "@polkadot-api/json-rpc-provider" "0.0.4" - "@polkadot-api/known-chains" "0.9.15" - "@polkadot-api/logs-provider" "0.0.6" - "@polkadot-api/metadata-builders" "0.13.7" - "@polkadot-api/metadata-compatibility" "0.4.1" - "@polkadot-api/observable-client" "0.17.0" - "@polkadot-api/pjs-signer" "0.6.17" - "@polkadot-api/polkadot-sdk-compat" "2.3.3" - "@polkadot-api/polkadot-signer" "0.1.6" - "@polkadot-api/signer" "0.2.11" - "@polkadot-api/sm-provider" "0.1.14" - "@polkadot-api/smoldot" "0.3.14" - "@polkadot-api/substrate-bindings" "0.16.5" - "@polkadot-api/substrate-client" "0.4.7" - "@polkadot-api/utils" "0.2.0" - "@polkadot-api/ws-provider" "0.7.4" - "@rx-state/core" "^0.1.4" - -possible-typed-array-names@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" - integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== - -postcss-load-config@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz" - integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g== - dependencies: - lilconfig "^3.1.1" - -prettier@^3.3.3: - version "3.7.4" - resolved "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz" - integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== - -pretty-ms@^9.2.0: - version "9.3.0" - resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz" - integrity sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ== - dependencies: - parse-ms "^4.0.0" - -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -read-pkg@^10.0.0: - version "10.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-10.0.0.tgz" - integrity sha512-A70UlgfNdKI5NSvTTfHzLQj7NJRpJ4mT5tGafkllJ4wh71oYuGm/pzphHcmW4s35iox56KSK721AihodoXSc/A== - dependencies: - "@types/normalize-package-data" "^2.4.4" - normalize-package-data "^8.0.0" - parse-json "^8.3.0" - type-fest "^5.2.0" - unicorn-magic "^0.3.0" - -read-pkg@^9.0.1: - version "9.0.1" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz" - integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== - dependencies: - "@types/normalize-package-data" "^2.4.3" - normalize-package-data "^6.0.0" - parse-json "^8.0.0" - type-fest "^4.6.0" - unicorn-magic "^0.1.0" - -readdirp@^4.0.1: - version "4.1.2" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz" - integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -restore-cursor@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" - integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== - dependencies: - onetime "^7.0.0" - signal-exit "^4.1.0" - -rollup@^4.34.8: - version "4.53.3" - resolved "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz" - integrity sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA== - dependencies: - "@types/estree" "1.0.8" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.53.3" - "@rollup/rollup-android-arm64" "4.53.3" - "@rollup/rollup-darwin-arm64" "4.53.3" - "@rollup/rollup-darwin-x64" "4.53.3" - "@rollup/rollup-freebsd-arm64" "4.53.3" - "@rollup/rollup-freebsd-x64" "4.53.3" - "@rollup/rollup-linux-arm-gnueabihf" "4.53.3" - "@rollup/rollup-linux-arm-musleabihf" "4.53.3" - "@rollup/rollup-linux-arm64-gnu" "4.53.3" - "@rollup/rollup-linux-arm64-musl" "4.53.3" - "@rollup/rollup-linux-loong64-gnu" "4.53.3" - "@rollup/rollup-linux-ppc64-gnu" "4.53.3" - "@rollup/rollup-linux-riscv64-gnu" "4.53.3" - "@rollup/rollup-linux-riscv64-musl" "4.53.3" - "@rollup/rollup-linux-s390x-gnu" "4.53.3" - "@rollup/rollup-linux-x64-gnu" "4.53.3" - "@rollup/rollup-linux-x64-musl" "4.53.3" - "@rollup/rollup-openharmony-arm64" "4.53.3" - "@rollup/rollup-win32-arm64-msvc" "4.53.3" - "@rollup/rollup-win32-ia32-msvc" "4.53.3" - "@rollup/rollup-win32-x64-gnu" "4.53.3" - "@rollup/rollup-win32-x64-msvc" "4.53.3" - fsevents "~2.3.2" - -rxjs@^7.8.1, rxjs@^7.8.2, rxjs@>=7, rxjs@>=7.8.0, rxjs@>=7.8.1: - version "7.8.2" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz" - integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== - dependencies: - tslib "^2.1.0" - -safe-buffer@^5.1.0: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex-test@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz" - integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== - dependencies: - call-bound "^1.0.2" - es-errors "^1.3.0" - is-regex "^1.2.1" - -scale-ts@^1.6.0, scale-ts@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz" - integrity sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g== - -semver@^7.3.5: - version "7.7.3" - resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz" - integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== - -serialize-javascript@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - -set-function-length@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -signal-exit@^4.0.1, signal-exit@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -smoldot@2.0.26, smoldot@2.x: - version "2.0.26" - resolved "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz" - integrity sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig== - dependencies: - ws "^8.8.1" - -smoldot@2.0.39: - version "2.0.39" - resolved "https://registry.npmjs.org/smoldot/-/smoldot-2.0.39.tgz" - integrity sha512-yFMSzI6nkqWFTNao99lBA/TguUFU+bR3A5UGTDd/QqqB12jqzvZnmW/No6l2rKmagt8Qx/KybMNowV/E28znhA== - dependencies: - ws "^8.8.1" - -sort-keys@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz" - integrity sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ== - dependencies: - is-plain-obj "^4.0.0" - -source-map@0.8.0-beta.0: - version "0.8.0-beta.0" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz" - integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== - dependencies: - whatwg-url "^7.0.0" - -spdx-correct@^3.0.0: - version "3.2.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" - integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.5.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz" - integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.22" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz" - integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== - -stdin-discarder@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz" - integrity sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ== - -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string-width@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz" - integrity sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg== - dependencies: - get-east-asian-width "^1.3.0" - strip-ansi "^7.1.0" - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1: - version "7.1.2" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" - integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== - dependencies: - ansi-regex "^6.0.1" - -strip-ansi@^7.1.0, strip-ansi@^7.1.2: - version "7.1.2" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" - integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== - dependencies: - ansi-regex "^6.0.1" - -strip-final-newline@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz" - integrity sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw== - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -sucrase@^3.35.0: - version "3.35.1" - resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz" - integrity sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.2" - commander "^4.0.0" - lines-and-columns "^1.1.6" - mz "^2.7.0" - pirates "^4.0.1" - tinyglobby "^0.2.11" - ts-interface-checker "^0.1.9" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -tagged-tag@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz" - integrity sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng== - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" - integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.1" - resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" - integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== - dependencies: - any-promise "^1.0.0" - -tinyexec@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz" - integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== - -tinyglobby@^0.2.11: - version "0.2.15" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz" - integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== - dependencies: - fdir "^6.5.0" - picomatch "^4.0.3" - -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" - integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== - dependencies: - punycode "^2.1.0" - -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -ts-interface-checker@^0.1.9: - version "0.1.13" - resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" - integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== - -ts-node@^10.9.2: - version "10.9.2" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" - integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -tsc-prog@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/tsc-prog/-/tsc-prog-2.3.0.tgz" - integrity sha512-ycET2d75EgcX7y8EmG4KiZkLAwUzbY4xRhA6NU0uVbHkY4ZjrAAuzTMxXI85kOwATqPnBI5C/7y7rlpY0xdqHA== - -tslib@^2.1.0, tslib@^2.7.0, tslib@^2.8.0, tslib@^2.8.1: - version "2.8.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" - integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== - -tslib@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz" - integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== - -tsup@8.5.0: - version "8.5.0" - resolved "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz" - integrity sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ== - dependencies: - bundle-require "^5.1.0" - cac "^6.7.14" - chokidar "^4.0.3" - consola "^3.4.0" - debug "^4.4.0" - esbuild "^0.25.0" - fix-dts-default-cjs-exports "^1.0.0" - joycon "^3.1.1" - picocolors "^1.1.1" - postcss-load-config "^6.0.1" - resolve-from "^5.0.0" - rollup "^4.34.8" - source-map "0.8.0-beta.0" - sucrase "^3.35.0" - tinyexec "^0.3.2" - tinyglobby "^0.2.11" - tree-kill "^1.2.2" - -type-fest@^4.23.0, type-fest@^4.39.1, type-fest@^4.6.0: - version "4.41.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz" - integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== - -type-fest@^5.2.0: - version "5.3.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-5.3.0.tgz" - integrity sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g== - dependencies: - tagged-tag "^1.0.0" - -typescript@^5.7.2, typescript@^5.9.3, typescript@>=2.7, typescript@>=4, typescript@>=4.5.0, typescript@>=5.0.4, typescript@>=5.4.0: - version "5.9.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" - integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== - -ufo@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz" - integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== - -undici-types@~6.19.2: - version "6.19.8" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz" - integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== - -undici-types@~6.21.0: - version "6.21.0" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz" - integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== - -undici-types@~7.16.0: - version "7.16.0" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz" - integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== - -unicorn-magic@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz" - integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== - -unicorn-magic@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz" - integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== - -util@^0.12.5: - version "0.12.5" - resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -validate-npm-package-license@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -viem@^2.37.9: - version "2.41.2" - resolved "https://registry.npmjs.org/viem/-/viem-2.41.2.tgz" - integrity sha512-LYliajglBe1FU6+EH9mSWozp+gRA/QcHfxeD9Odf83AdH5fwUS7DroH4gHvlv6Sshqi1uXrYFA2B/EOczxd15g== - 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" - isows "1.0.7" - ox "0.9.6" - ws "8.18.3" - -viem@2.23.4: - version "2.23.4" - resolved "https://registry.npmjs.org/viem/-/viem-2.23.4.tgz" - integrity sha512-UQquuolKlS1w5H5e0Fd1KKoUlIPJryIEBzY5AUhGyV1ka+9O6+3uYVhUzj6RbvGK0PtsMKn2ddwPZFwjNDVU/A== - dependencies: - "@noble/curves" "1.8.1" - "@noble/hashes" "1.7.1" - "@scure/bip32" "1.6.2" - "@scure/bip39" "1.5.4" - abitype "1.0.8" - isows "1.0.6" - ox "0.6.7" - ws "8.18.0" - -web-streams-polyfill@^3.0.3: - version "3.3.3" - resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz" - integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== - -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - -which-typed-array@^1.1.16, which-typed-array@^1.1.2: - version "1.1.19" - resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== - 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.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -workerpool@^9.2.0: - version "9.3.4" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz" - integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -write-file-atomic@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz" - integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^4.0.1" - -write-json-file@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/write-json-file/-/write-json-file-6.0.0.tgz" - integrity sha512-MNHcU3f9WxnNyR6MxsYSj64Jz0+dwIpisWKWq9gqLj/GwmA9INg3BZ3vt70/HB3GEwrnDQWr4RPrywnhNzmUFA== - dependencies: - detect-indent "^7.0.1" - is-plain-obj "^4.1.0" - sort-keys "^5.0.0" - write-file-atomic "^5.0.1" - -write-package@^7.2.0: - version "7.2.0" - resolved "https://registry.npmjs.org/write-package/-/write-package-7.2.0.tgz" - integrity sha512-uMQTubF/vcu+Wd0b5BGtDmiXePd/+44hUWQz2nZPbs92/BnxRo74tqs+hqDo12RLiEd+CXFKUwxvvIZvtt34Jw== - dependencies: - deepmerge-ts "^7.1.0" - read-pkg "^9.0.1" - sort-keys "^5.0.0" - type-fest "^4.23.0" - write-json-file "^6.0.0" - -ws@*, ws@^8.18.0, ws@^8.18.2, ws@^8.18.3, ws@^8.8.1, ws@8.18.3: - version "8.18.3" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== - -ws@8.17.1: - version "8.17.1" - resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== - -ws@8.18.0: - version "8.18.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs-unparser@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -yoctocolors@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz" - integrity sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug== diff --git a/docs/wasm-contracts.md b/docs/wasm-contracts.md index f787ea4375..2e9db982d5 100644 --- a/docs/wasm-contracts.md +++ b/docs/wasm-contracts.md @@ -48,6 +48,9 @@ Subtensor provides a custom chain extension that allows smart contracts to inter | 17 | `burn_alpha` | Burn alpha stake without reducing SubnetAlphaOut (supply neutral) | `(AccountId, NetUid, AlphaBalance)` | `u64` (actual amount burned) | | 18 | `add_stake_recycle` | Atomically add stake then recycle the resulting alpha | `(AccountId, NetUid, TaoBalance)` | `u64` (alpha amount recycled) | | 19 | `add_stake_burn` | Atomically add stake then burn the resulting alpha | `(AccountId, NetUid, TaoBalance)` | `u64` (alpha amount burned) | +| 34 | `get_subnet_registration_state` | Query whether a subnet exists and which registration generation currently owns the netuid | `(NetUid)` | `SubnetRegistrationState { netuid, exists, registered_subnet_counter }` | +| 35 | `get_coldkey_lock` | Query the current rolled-forward lock state for a coldkey on a subnet | `(AccountId, NetUid)` | `Option` | +| 36 | `get_stake_availability` | Query total, locked, and currently available alpha for a coldkey on a subnet | `(AccountId, NetUid)` | `StakeAvailability { netuid: NetUid, total: AlphaBalance, locked: AlphaBalance, available: AlphaBalance }` | > [!NOTE] > Functions **16** and **17** use the decoded argument order **`(hotkey, netuid, amount)`**, matching [`SubtensorChainExtension`](../chain-extensions/src/lib.rs). If your ink! contract encoded **`(hotkey, amount, netuid)`** for those functions, **recompile and redeploy**; the runtime will decode the older layout incorrectly. diff --git a/eco-tests/Cargo.toml b/eco-tests/Cargo.toml index bf29e738fa..673b7d2f29 100644 --- a/eco-tests/Cargo.toml +++ b/eco-tests/Cargo.toml @@ -36,13 +36,17 @@ pallet-scheduler = { git = "https://github.com/opentensor/polkadot-sdk.git", rev pallet-preimage = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "7cc54bf2d50ae3921d718736dfeb0de9468539c7", 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-subtensor-swap-runtime-api = { path = "../pallets/swap/runtime-api", default-features = false, features = ["std"] } +subtensor-custom-rpc-runtime-api = { path = "../pallets/subtensor/runtime-api", default-features = false, features = ["std"] } +sp-api = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "7cc54bf2d50ae3921d718736dfeb0de9468539c7", 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"] } +subtensor-swap-interface = { path = "../primitives/swap-interface", default-features = false, features = ["std"] } share-pool = { path = "../primitives/share-pool", default-features = false, features = ["std"] } +substrate-fixed = { git = "https://github.com/encointer/substrate-fixed.git", tag = "v0.6.0", 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" diff --git a/eco-tests/src/helpers.rs b/eco-tests/src/helpers.rs index c6fa0ec72d..1696e33634 100644 --- a/eco-tests/src/helpers.rs +++ b/eco-tests/src/helpers.rs @@ -87,9 +87,9 @@ pub fn next_block_no_epoch(netuid: NetUid) -> u64 { let high_tempo: u16 = u16::MAX - 1; let old_tempo: u16 = SubtensorModule::get_tempo(netuid); - SubtensorModule::set_tempo(netuid, high_tempo); + SubtensorModule::set_tempo_unchecked(netuid, high_tempo); let new_block = next_block(); - SubtensorModule::set_tempo(netuid, old_tempo); + SubtensorModule::set_tempo_unchecked(netuid, old_tempo); new_block } @@ -99,14 +99,14 @@ pub fn run_to_block_no_epoch(netuid: NetUid, n: u64) { let high_tempo: u16 = u16::MAX - 1; let old_tempo: u16 = SubtensorModule::get_tempo(netuid); - SubtensorModule::set_tempo(netuid, high_tempo); + SubtensorModule::set_tempo_unchecked(netuid, high_tempo); run_to_block(n); - SubtensorModule::set_tempo(netuid, old_tempo); + SubtensorModule::set_tempo_unchecked(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( + let blocks_to_next_epoch = SubtensorModule::blocks_until_next_auto_epoch( netuid, SubtensorModule::get_tempo(netuid), SubtensorModule::get_current_block_as_u64(), @@ -322,10 +322,6 @@ pub fn increase_stake_on_hotkey_account(hotkey: &U256, increment: TaoBalance, ne ); } -pub fn remove_stake_rate_limit_for_tests(hotkey: &U256, coldkey: &U256, netuid: NetUid) { - StakingOperationRateLimiter::::remove((hotkey, coldkey, netuid)); -} - pub fn setup_reserves(netuid: NetUid, tao: TaoBalance, alpha: AlphaBalance) { SubnetTAO::::set(netuid, tao); SubnetAlphaIn::::set(netuid, alpha); diff --git a/eco-tests/src/lib.rs b/eco-tests/src/lib.rs index d7d60aca2b..980de0f3da 100644 --- a/eco-tests/src/lib.rs +++ b/eco-tests/src/lib.rs @@ -4,3 +4,6 @@ mod helpers; mod mock; #[cfg(test)] mod tests; + +#[cfg(test)] +mod tests_taocom_indexer; diff --git a/eco-tests/src/mock.rs b/eco-tests/src/mock.rs index aba98da9b5..5ce8bc9b6b 100644 --- a/eco-tests/src/mock.rs +++ b/eco-tests/src/mock.rs @@ -8,13 +8,13 @@ use core::num::NonZeroU64; use frame_support::dispatch::DispatchResult; -use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth, InstanceFilter}; +use frame_support::traits::{Contains, Everything, 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 frame_system::{EnsureRoot, limits}; use pallet_subtensor::*; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_utility as pallet_utility; @@ -28,7 +28,8 @@ 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; +pub type Block = frame_system::mocking::MockBlock; +pub use api_mocks::MockApi; // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( @@ -141,7 +142,14 @@ impl system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; - type DispatchExtension = pallet_subtensor::CheckColdkeySwap; + type DispatchExtension = ( + pallet_subtensor::CheckColdkeySwap, + pallet_subtensor::CheckWeights, + pallet_subtensor::CheckRateLimits, + pallet_subtensor::CheckDelegateTake, + pallet_subtensor::CheckServingEndpoints, + pallet_subtensor::CheckEvmKeyAssociation, + ); } parameter_types! { @@ -198,6 +206,12 @@ parameter_types! { 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 MinTempo: u16 = pallet_subtensor::MIN_TEMPO; + pub const MaxTempo: u16 = pallet_subtensor::MAX_TEMPO; + pub const MinActivityCutoffFactorMilli: u32 = + pallet_subtensor::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const MaxActivityCutoffFactorMilli: u32 = + pallet_subtensor::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const InitialValidatorPruneLen: u64 = 0; pub const InitialScalingLawPower: u16 = 50; pub const InitialMaxAllowedValidators: u16 = 100; @@ -237,6 +251,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = 10; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const MaxEpochsPerBlock: u8 = 32; } impl pallet_subtensor::Config for Test { @@ -285,6 +300,10 @@ impl pallet_subtensor::Config for Test { type InitialMinStake = InitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = MinTempo; + type MaxTempo = MaxTempo; + type MinActivityCutoffFactorMilli = MinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = MaxActivityCutoffFactorMilli; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; @@ -315,6 +334,7 @@ impl pallet_subtensor::Config for Test { type AuthorshipProvider = MockAuthorshipProvider; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = MaxEpochsPerBlock; type WeightInfo = (); type AlphaAssets = AlphaAssets; } @@ -323,7 +343,6 @@ impl pallet_subtensor::Config for Test { 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(); } @@ -335,7 +354,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = TaoBalanceReserve; type AlphaReserve = AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); @@ -548,28 +566,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), ())) + UncheckedExtrinsic::new_bare(call) } } @@ -606,3 +608,81 @@ pub fn add_balance_to_coldkey_account(coldkey: &U256, tao: TaoBalance) { let credit = SubtensorModule::mint_tao(tao); let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); } + +mod api_mocks { + use codec::Compact; + use pallet_subtensor::rpc_info::delegate_info::DelegateInfo; + use pallet_subtensor::rpc_info::stake_info::StakeInfo; + use pallet_subtensor_swap_runtime_api::{SimSwapResult, SubnetPrice, SwapRuntimeApi}; + use sp_runtime::AccountId32; + use subtensor_custom_rpc_runtime_api::{DelegateInfoRuntimeApi, StakeInfoRuntimeApi}; + use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance}; + + use super::Block; + + pub struct MockApi; + + sp_api::mock_impl_runtime_apis! { + impl DelegateInfoRuntimeApi for MockApi { + fn get_delegates() -> Vec> { Vec::new() } + fn get_delegate(_delegate_account: AccountId32) -> Option> { None } + fn get_delegated( + _delegatee_account: AccountId32, + ) -> Vec<(DelegateInfo, (Compact, Compact))> { + Vec::new() + } + } + + impl StakeInfoRuntimeApi for MockApi { + fn get_stake_info_for_coldkey(_coldkey_account: AccountId32) -> Vec> { + Vec::new() + } + fn get_stake_info_for_coldkeys( + _coldkey_accounts: Vec, + ) -> Vec<(AccountId32, Vec>)> { + Vec::new() + } + fn get_stake_info_for_hotkey_coldkey_netuid( + _hotkey_account: AccountId32, + _coldkey_account: AccountId32, + _netuid: NetUid, + ) -> Option> { + None + } + fn get_stake_fee( + _origin: Option<(AccountId32, NetUid)>, + _origin_coldkey_account: AccountId32, + _destination: Option<(AccountId32, NetUid)>, + _destination_coldkey_account: AccountId32, + _amount: u64, + ) -> u64 { + 0 + } + } + + impl SwapRuntimeApi for MockApi { + fn current_alpha_price(_netuid: NetUid) -> u64 { 0 } + fn current_alpha_price_all() -> Vec { Vec::new() } + fn sim_swap_tao_for_alpha(_netuid: NetUid, _tao: TaoBalance) -> SimSwapResult { + SimSwapResult { + tao_amount: 0u64.into(), + alpha_amount: 0u64.into(), + tao_fee: 0u64.into(), + alpha_fee: 0u64.into(), + tao_slippage: 0u64.into(), + alpha_slippage: 0u64.into(), + } + } + fn sim_swap_alpha_for_tao(_netuid: NetUid, _alpha: AlphaBalance) -> SimSwapResult { + SimSwapResult { + tao_amount: 0u64.into(), + alpha_amount: 0u64.into(), + tao_fee: 0u64.into(), + alpha_fee: 0u64.into(), + tao_slippage: 0u64.into(), + alpha_slippage: 0u64.into(), + } + } + } + } +} diff --git a/eco-tests/src/tests_taocom_indexer.rs b/eco-tests/src/tests_taocom_indexer.rs new file mode 100644 index 0000000000..d79e05f9b2 --- /dev/null +++ b/eco-tests/src/tests_taocom_indexer.rs @@ -0,0 +1,191 @@ +//! Indexer-contract tests for the TAO.com / ecosystem indexer. +//! Any modification in these tests will notify the member responsible +//! for the communication between protocol and the indexer team. + +#![allow(clippy::unwrap_used)] +#![allow(clippy::arithmetic_side_effects)] + +use pallet_subtensor::rpc_info::delegate_info::DelegateInfo; +use pallet_subtensor::rpc_info::stake_info::StakeInfo; +use pallet_subtensor::*; +use pallet_subtensor_swap_runtime_api::SwapRuntimeApi; +use share_pool::SafeFloat; +use sp_core::U256; +use sp_runtime::AccountId32; +use sp_runtime::traits::Block as BlockT; +use substrate_fixed::types::{I96F32, U64F64}; +use subtensor_custom_rpc_runtime_api::{DelegateInfoRuntimeApi, StakeInfoRuntimeApi}; +use subtensor_runtime_common::{AlphaBalance, MechId, NetUid, NetUidStorageIndex, TaoBalance}; + +use super::helpers::*; +use super::mock::*; + +#[test] +fn indexer_neuron_per_subnet_vectors() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + let netuid_idx = NetUidStorageIndex::from(netuid); + + let _: Vec = Active::::get(netuid); + let _: Vec = Consensus::::get(netuid); + let _: Vec = Dividends::::get(netuid); + let _: Vec = Incentive::::get(netuid_idx); + let _: Vec = LastUpdate::::get(netuid_idx); + let _: Vec = ValidatorPermit::::get(netuid); + let _: Vec = ValidatorTrust::::get(netuid); + let _: Vec = Emission::::get(netuid); + }); +} + +#[test] +fn indexer_neuron_uid_maps() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + let netuid_idx = NetUidStorageIndex::from(netuid); + let hotkey = U256::from(1); + let uid: u16 = 0; + + let _: Option = Uids::::get(netuid, hotkey); + let _: U256 = Keys::::get(netuid, uid); + let _: Vec<(u16, u16)> = Weights::::get(netuid_idx, uid); + let _: Vec<(u16, u16)> = Bonds::::get(netuid_idx, uid); + let _: Option = Axons::::get(netuid, hotkey); + }); +} + +#[test] +fn indexer_ownership_and_childkey_graph() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + let key = U256::from(42); + + let _: U256 = Owner::::get(key); + let _: U256 = SubnetOwner::::get(netuid); + let _: U256 = SubnetOwnerHotkey::::get(netuid); + let _: Vec<(u64, U256)> = ChildKeys::::get(key, netuid); + let _: Vec<(u64, U256)> = ParentKeys::::get(key, netuid); + }); +} + +#[test] +fn indexer_stake_and_alpha_shares() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + + let _: AlphaBalance = TotalHotkeyAlpha::::get(hotkey, netuid); + let _: U64F64 = TotalHotkeyShares::::get(hotkey, netuid); + let _: SafeFloat = TotalHotkeySharesV2::::get(hotkey, netuid); + let _: U64F64 = Alpha::::get((hotkey, coldkey, netuid)); + let _: SafeFloat = AlphaV2::::get((hotkey, coldkey, netuid)); + }); +} + +#[test] +fn indexer_subnet_metadata() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + let coldkey = U256::from(7); + + let _: u16 = TotalNetworks::::get(); + let _: Vec = TokenSymbol::::get(netuid); + let _: Option = IdentitiesV2::::get(coldkey); + let _: Option = SubnetIdentitiesV3::::get(netuid); + let _: MechId = MechanismCountCurrent::::get(netuid); + let _: Option = FirstEmissionBlockNumber::::get(netuid); + }); +} + +#[test] +fn indexer_subnet_pool_and_emissions() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + + let _: I96F32 = SubnetMovingPrice::::get(netuid); + let _: u128 = SubnetVolume::::get(netuid); + let _: TaoBalance = SubnetTAO::::get(netuid); + let _: AlphaBalance = SubnetAlphaIn::::get(netuid); + let _: AlphaBalance = SubnetAlphaOut::::get(netuid); + let _: TaoBalance = SubnetTaoInEmission::::get(netuid); + let _: AlphaBalance = SubnetAlphaInEmission::::get(netuid); + let _: AlphaBalance = SubnetAlphaOutEmission::::get(netuid); + let _: AlphaBalance = PendingValidatorEmission::::get(netuid); + let _: AlphaBalance = PendingServerEmission::::get(netuid); + }); +} + +#[test] +fn indexer_subnet_hyperparams() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + + let _: u16 = Rho::::get(netuid); + let _: u16 = Kappa::::get(netuid); + let _: u16 = ImmunityPeriod::::get(netuid); + let _: u16 = MinAllowedWeights::::get(netuid); + let _: u16 = MaxWeightsLimit::::get(netuid); + let _: u16 = Tempo::::get(netuid); + let _: u64 = MinDifficulty::::get(netuid); + let _: u64 = MaxDifficulty::::get(netuid); + let _: u64 = WeightsVersionKey::::get(netuid); + let _: u64 = WeightsSetRateLimit::::get(netuid); + let _: u16 = AdjustmentInterval::::get(netuid); + let _: u16 = ActivityCutoff::::get(netuid); + let _: bool = NetworkRegistrationAllowed::::get(netuid); + let _: u16 = TargetRegistrationsPerInterval::::get(netuid); + let _: TaoBalance = MinBurn::::get(netuid); + let _: TaoBalance = MaxBurn::::get(netuid); + let _: u64 = BondsMovingAverage::::get(netuid); + let _: u16 = MaxRegistrationsPerBlock::::get(netuid); + let _: u64 = ServingRateLimit::::get(netuid); + let _: u16 = MaxAllowedValidators::::get(netuid); + let _: u64 = Difficulty::::get(netuid); + let _: u64 = AdjustmentAlpha::::get(netuid); + let _: u64 = RevealPeriodEpochs::::get(netuid); + let _: bool = CommitRevealWeightsEnabled::::get(netuid); + let _: bool = LiquidAlphaOn::::get(netuid); + let _: i16 = AlphaSigmoidSteepness::::get(netuid); + let _: bool = Yuma3On::::get(netuid); + let _: bool = BondsResetOn::::get(netuid); + let _: (u16, u16) = AlphaValues::::get(netuid); + let _: RecycleOrBurnEnum = RecycleOrBurn::::get(netuid); + }); +} + +#[test] +fn indexer_step_and_toggles() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + + let _: u64 = BlocksSinceLastStep::::get(netuid); + let _: u64 = LastMechansimStepBlock::::get(netuid); + let _: Option<(RateLimitKey, u64)> = LastRateLimitedBlock::::iter().next(); + let _: bool = TransferToggle::::get(netuid); + }); +} + +#[test] +fn indexer_network_economics() { + new_test_ext(1).execute_with(|| { + let _: TaoBalance = NetworkMinLockCost::::get(); + let _: TaoBalance = NetworkLastLockCost::::get(); + let _: u64 = NetworkLockReductionInterval::::get(); + let _: TaoBalance = TotalIssuance::::get(); + }); +} + +#[test] +fn indexer_runtime_api_signatures() { + let at = ::Hash::default(); + let netuid = NetUid::from(1u16); + let acct = AccountId32::new([0u8; 32]); + + let _: Option> = + DelegateInfoRuntimeApi::get_delegate(&MockApi, at, acct.clone()).unwrap(); + + let _: Vec<(AccountId32, Vec>)> = + StakeInfoRuntimeApi::get_stake_info_for_coldkeys(&MockApi, at, vec![acct.clone()]).unwrap(); + + let _: u64 = SwapRuntimeApi::current_alpha_price(&MockApi, at, netuid).unwrap(); +} diff --git a/contract-tests/bittensor/.gitignore b/ink-contract/.gitignore similarity index 100% rename from contract-tests/bittensor/.gitignore rename to ink-contract/.gitignore diff --git a/contract-tests/bittensor/Cargo.toml b/ink-contract/Cargo.toml similarity index 100% rename from contract-tests/bittensor/Cargo.toml rename to ink-contract/Cargo.toml diff --git a/contract-tests/bittensor/lib.rs b/ink-contract/lib.rs similarity index 91% rename from contract-tests/bittensor/lib.rs rename to ink-contract/lib.rs index bff61d3b08..85adf61083 100755 --- a/contract-tests/bittensor/lib.rs +++ b/ink-contract/lib.rs @@ -40,6 +40,9 @@ pub enum FunctionId { CallerSetColdkeyAutoStakeHotkeyV1 = 31, CallerAddProxyV1 = 32, CallerRemoveProxyV1 = 33, + GetSubnetRegistrationStateV1 = 34, + GetColdkeyLockV1 = 35, + GetStakeAvailabilityV1 = 36, } #[ink::chain_extension(extension = 0x1000)] @@ -269,6 +272,21 @@ pub trait RuntimeReadWrite { #[ink(function = 33)] fn caller_remove_proxy(delegate: ::AccountId); + + #[ink(function = 34)] + fn get_subnet_registration_state(netuid: u16) -> SubnetRegistrationState; + + #[ink(function = 35)] + fn get_coldkey_lock( + coldkey: ::AccountId, + netuid: u16, + ) -> Option; + + #[ink(function = 36)] + fn get_stake_availability( + coldkey: ::AccountId, + netuid: u16, + ) -> StakeAvailability; } #[ink::scale_derive(Encode, Decode, TypeInfo)] @@ -313,6 +331,28 @@ pub struct StakeInfo { is_registered: bool, } +#[ink::scale_derive(Encode, Decode, TypeInfo)] +pub struct SubnetRegistrationState { + netuid: u16, + exists: bool, + registered_subnet_counter: u64, +} + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +pub struct ColdkeyLock { + locked_mass: u64, + conviction_bits: u128, + last_update: u64, +} + +#[ink::scale_derive(Encode, Decode, TypeInfo)] +pub struct StakeAvailability { + netuid: u16, + total: u64, + locked: u64, + available: u64, +} + #[ink::contract(env = crate::CustomEnvironment)] mod bittensor { use super::*; @@ -801,5 +841,40 @@ mod bittensor { .caller_remove_proxy(delegate.into()) .map_err(|_e| ReadWriteErrorCode::WriteFailed) } + + #[ink(message)] + pub fn get_subnet_registration_state( + &self, + netuid: u16, + ) -> Result { + self.env() + .extension() + .get_subnet_registration_state(netuid) + .map_err(|_e| ReadWriteErrorCode::ReadFailed) + } + + #[ink(message)] + pub fn get_coldkey_lock( + &self, + coldkey: [u8; 32], + netuid: u16, + ) -> Result, ReadWriteErrorCode> { + self.env() + .extension() + .get_coldkey_lock(coldkey.into(), netuid) + .map_err(|_e| ReadWriteErrorCode::ReadFailed) + } + + #[ink(message)] + pub fn get_stake_availability( + &self, + coldkey: [u8; 32], + netuid: u16, + ) -> Result { + self.env() + .extension() + .get_stake_availability(coldkey.into(), netuid) + .map_err(|_e| ReadWriteErrorCode::ReadFailed) + } } } diff --git a/node/src/dev_keystore.rs b/node/src/dev_keystore.rs new file mode 100644 index 0000000000..6011021bff --- /dev/null +++ b/node/src/dev_keystore.rs @@ -0,0 +1,56 @@ +use stc_shield::MemoryShieldKeystore; +use stp_shield::{Result as TraitResult, ShieldKeystore}; + +/// A fixed (non-rotating) shield keystore for single-validator dev/manual-seal nodes. +/// +/// Uses the same ML-KEM-768 keypair for both `next_enc_key()` and `current_dec_key()`, +/// bypassing the multi-validator key-rotation timing assumption. In a real multi-validator +/// AURA chain, each validator builds every Kth block (K≥3), so the keystore rolls at the +/// same cadence as the on-chain PendingKey pipeline (2-block delay). In single-validator +/// manual-seal mode the keystore would roll on every block, drifting 2 pairs ahead of +/// PendingKey. This keystore avoids that by keeping both keys from the same generated pair. +/// +/// Construction: capture `next_enc_key()` from a fresh `MemoryShieldKeystore`, roll once +/// so that key becomes current, then freeze. `current_dec_key()` delegates to the inner +/// store (which now holds the matching pair), and `roll_for_next_slot()` is a no-op. +pub struct DevShieldKeystore { + enc_key_bytes: Vec, + inner: MemoryShieldKeystore, +} + +impl DevShieldKeystore { + #[allow(clippy::expect_used)] + pub fn new() -> Self { + let inner = MemoryShieldKeystore::new(); + let enc_key_bytes = inner + .next_enc_key() + .expect("MemoryShieldKeystore always has a next key"); + inner + .roll_for_next_slot() + .expect("initial roll should not fail"); + Self { + enc_key_bytes, + inner, + } + } +} + +impl Default for DevShieldKeystore { + fn default() -> Self { + Self::new() + } +} + +impl ShieldKeystore for DevShieldKeystore { + fn roll_for_next_slot(&self) -> TraitResult<()> { + Ok(()) + } + + fn next_enc_key(&self) -> TraitResult> { + Ok(self.enc_key_bytes.clone()) + } + + fn current_dec_key(&self) -> TraitResult> { + self.inner.current_dec_key() + } +} diff --git a/node/src/lib.rs b/node/src/lib.rs index 4740155f5e..d269fe583d 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -4,6 +4,7 @@ pub mod client; pub mod clone_spec; pub mod conditional_evm_block_import; pub mod consensus; +pub mod dev_keystore; pub mod ethereum; pub mod rpc; pub mod service; diff --git a/node/src/main.rs b/node/src/main.rs index 2766b93054..a6aa15038f 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -10,6 +10,7 @@ mod clone_spec; mod command; mod conditional_evm_block_import; mod consensus; +mod dev_keystore; mod ethereum; mod rpc; mod service; diff --git a/node/src/service.rs b/node/src/service.rs index d07671f81f..624f63b968 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -544,10 +544,14 @@ where .await; if role.is_authority() { - let shield_keystore = Arc::new(MemoryShieldKeystore::new()); - - // manual-seal authorship + // manual-seal authorship — use a fixed keystore so the single-validator dev + // node doesn't drift: MemoryShieldKeystore rolls on every own-block import + // (every block in single-validator mode), advancing current_dec_key() 2 pairs + // ahead of PendingKey on-chain. DevShieldKeystore avoids this by keeping the + // same keypair for both next_enc_key() and current_dec_key(). if let Some(sealing) = sealing { + let dev_shield_keystore: stp_shield::ShieldKeystorePtr = + Arc::new(crate::dev_keystore::DevShieldKeystore::new()); run_manual_seal_authorship( sealing, client, @@ -558,12 +562,14 @@ where prometheus_registry.as_ref(), telemetry.as_ref(), commands_stream, - shield_keystore.clone(), + dev_shield_keystore, )?; log::info!("Manual Seal Ready"); return Ok(task_manager); } + let shield_keystore = Arc::new(MemoryShieldKeystore::new()); + stc_shield::spawn_key_rotation_on_own_import( &task_manager.spawn_handle(), client.clone(), @@ -749,7 +755,7 @@ fn run_manual_seal_authorship( transaction_pool.clone(), prometheus_registry, telemetry.as_ref().map(|x| x.handle()), - shield_keystore, + shield_keystore.clone(), ); thread_local!(static TIMESTAMP: RefCell = const { RefCell::new(0) }); @@ -781,8 +787,15 @@ fn run_manual_seal_authorship( } } - let create_inherent_data_providers = - move |_, ()| async move { Ok(MockTimestampInherentDataProvider) }; + let create_inherent_data_providers = move |_, ()| { + let keystore = shield_keystore.clone(); + async move { + Ok(( + MockTimestampInherentDataProvider, + stc_shield::InherentDataProvider::new(keystore), + )) + } + }; let aura_data_provider = sc_consensus_manual_seal::consensus::aura::AuraConsensusDataProvider::new(client.clone()); diff --git a/pallets/admin-utils/Cargo.toml b/pallets/admin-utils/Cargo.toml index 85236a425a..ae374b4938 100644 --- a/pallets/admin-utils/Cargo.toml +++ b/pallets/admin-utils/Cargo.toml @@ -92,6 +92,7 @@ runtime-benchmarks = [ "pallet-subtensor-swap/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "subtensor-runtime-common/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 0f4928237c..d1a373d791 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -689,5 +689,16 @@ mod benchmarks { ); /* sudo_set_min_non_immune_uids() */ } + #[benchmark] + fn sudo_set_max_epochs_per_block() { + #[extrinsic_call] + _(RawOrigin::Root, 8u8); + + assert_eq!( + pallet_subtensor::Pallet::::get_max_epochs_per_block(), + 8u8 + ); + } + impl_benchmark_test_suite!(AdminUtils, mock::new_test_ext(), mock::Test); } diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index f972facca6..0e63a1f05a 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -611,6 +611,9 @@ pub mod pallet { /// The extrinsic sets the activity cutoff for a subnet. /// It is only callable by the root account or subnet owner. /// The extrinsic will call the Subtensor pallet to set the activity cutoff. + // #[deprecated( + // note = "Please use set_activity_cutoff_factor instead. This extrinsic will be removed soon." + // )] #[pallet::call_index(18)] #[pallet::weight(::WeightInfo::sudo_set_activity_cutoff())] pub fn sudo_set_activity_cutoff( @@ -983,7 +986,7 @@ pub mod pallet { pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - pallet_subtensor::Pallet::::set_tempo(netuid, tempo); + pallet_subtensor::Pallet::::apply_tempo_with_cycle_reset(netuid, tempo); log::debug!("TempoSet( netuid: {netuid:?} tempo: {tempo:?} ) "); Ok(()) } @@ -2276,6 +2279,20 @@ pub mod pallet { Ok(()) } + + /// Sets the per-block cap on subnet epochs (dynamic tempo throttle). + #[pallet::call_index(96)] + #[pallet::weight(::WeightInfo::sudo_set_max_epochs_per_block())] + pub fn sudo_set_max_epochs_per_block( + origin: OriginFor, + max_epochs_per_block: u8, + ) -> DispatchResult { + ensure_root(origin)?; + ensure!(max_epochs_per_block >= 1, Error::::ValueNotInBounds); + pallet_subtensor::Pallet::::set_max_epochs_per_block(max_epochs_per_block); + + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 37b4e06aa5..e3b658a0a8 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -4,9 +4,9 @@ use core::num::NonZeroU64; use frame_support::{ PalletId, assert_ok, derive_impl, parameter_types, - traits::{Everything, Hooks, InherentBuilder, PrivilegeCmp}, + traits::{Everything, Hooks, PrivilegeCmp}, }; -use frame_system::{self as system, offchain::CreateTransactionBase}; +use frame_system::{self as system}; use frame_system::{EnsureRoot, limits}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityList as GrandpaAuthorityList; @@ -123,6 +123,10 @@ parameter_types! { pub const InitialMaxBurn: TaoBalance = TaoBalance::new(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 MinTempo: u16 = pallet_subtensor::MIN_TEMPO; + pub const MaxTempo: u16 = pallet_subtensor::MAX_TEMPO; + pub const MinActivityCutoffFactorMilli: u32 = pallet_subtensor::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const MaxActivityCutoffFactorMilli: u32 = pallet_subtensor::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const InitialValidatorPruneLen: u64 = 0; pub const InitialScalingLawPower: u16 = 50; pub const InitialMaxAllowedValidators: u16 = 100; @@ -161,6 +165,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = 0; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const MaxEpochsPerBlock: u8 = 32; } impl pallet_subtensor::Config for Test { @@ -209,6 +214,10 @@ impl pallet_subtensor::Config for Test { type InitialMinStake = InitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = MinTempo; + type MaxTempo = MaxTempo; + type MinActivityCutoffFactorMilli = MinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = MaxActivityCutoffFactorMilli; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; @@ -240,6 +249,7 @@ impl pallet_subtensor::Config for Test { type AuthorshipProvider = MockAuthorshipProvider; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = MaxEpochsPerBlock; type WeightInfo = (); } @@ -343,7 +353,6 @@ impl pallet_alpha_assets::Config for Test {} 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(1_000_000).unwrap(); } @@ -355,7 +364,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = pallet_subtensor::TaoBalanceReserve; type AlphaReserve = pallet_subtensor::AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); @@ -471,28 +479,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - Some(UncheckedExtrinsic::new_signed(call, nonce, (), ())) + UncheckedExtrinsic::new_bare(call) } } diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 7e05acf54b..735e784ad8 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1498,6 +1498,79 @@ fn test_sudo_set_coldkey_swap_reannouncement_delay() { }); } +#[test] +fn test_sudo_set_max_epochs_per_block() { + new_test_ext().execute_with(|| { + let root = RuntimeOrigin::root(); + let non_root = RuntimeOrigin::signed(U256::from(1)); + let init_value = SubtensorModule::get_max_epochs_per_block(); + let to_be_set: u8 = init_value.saturating_add(3); + + // Non-root is rejected and leaves the value untouched. + assert_noop!( + AdminUtils::sudo_set_max_epochs_per_block(non_root, to_be_set), + DispatchError::BadOrigin + ); + assert_eq!(SubtensorModule::get_max_epochs_per_block(), init_value); + + // Zero is rejected by the `>= 1` guard (a zero cap would halt all subnet epochs). + assert_noop!( + AdminUtils::sudo_set_max_epochs_per_block(root.clone(), 0u8), + Error::::ValueNotInBounds + ); + assert_eq!(SubtensorModule::get_max_epochs_per_block(), init_value); + + // Root succeeds: storage is updated and the event is emitted. + assert_ok!(AdminUtils::sudo_set_max_epochs_per_block(root, to_be_set)); + assert_eq!(SubtensorModule::get_max_epochs_per_block(), to_be_set); + System::assert_last_event(Event::MaxEpochsPerBlockSet(to_be_set).into()); + }); +} + +#[test] +fn test_sudo_set_max_epochs_per_block_changes_deferrals() { + new_test_ext().execute_with(|| { + let root = RuntimeOrigin::root(); + + // Create several subnets and force each to be "due this block". + let created: u16 = 4; + for i in 0..created { + let netuid = NetUid::from(i + 1); + add_network(netuid, 100 /*tempo*/); + pallet_subtensor::PendingEpochAt::::insert(netuid, 1); + } + + let block = SubtensorModule::get_current_block_as_u64(); + let subnets: Vec = SubtensorModule::get_all_subnet_netuids() + .into_iter() + .filter(|x| *x != NetUid::ROOT) + .collect(); + let due = subnets + .iter() + .filter(|n| SubtensorModule::should_run_epoch(**n, block)) + .count(); + assert!(due >= created as usize); + + // Tight cap (1): every due subnet beyond the first is deferred. + assert_ok!(AdminUtils::sudo_set_max_epochs_per_block(root.clone(), 1u8)); + let deferred_tight = SubtensorModule::epochs_deferred_this_block(&subnets, block).len(); + assert_eq!(deferred_tight, due.saturating_sub(1)); + + // Raising the cap above the due count clears all deferrals — proving the + // admin-set cap directly drives which epochs are deferred. + assert_ok!(AdminUtils::sudo_set_max_epochs_per_block( + root, + (due as u8).saturating_add(2) + )); + let deferred_loose = SubtensorModule::epochs_deferred_this_block(&subnets, block).len(); + assert_eq!(deferred_loose, 0); + assert!( + deferred_loose < deferred_tight, + "raising MaxEpochsPerBlock must defer fewer epochs" + ); + }); +} + #[test] fn test_sudo_set_dissolve_network_schedule_duration() { new_test_ext().execute_with(|| { @@ -2042,7 +2115,7 @@ fn test_sudo_set_admin_freeze_window_and_rate() { fn test_freeze_window_blocks_root_and_owner() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); - let tempo = 10; + let tempo: u16 = 10; // Create subnet with tempo 10 add_network(netuid, tempo); // Set freeze window to 3 blocks @@ -2050,8 +2123,12 @@ fn test_freeze_window_blocks_root_and_owner() { <::RuntimeOrigin>::root(), 3 )); - // Advance to a block where remaining < 3 - run_to_block((tempo - 2).into()); + // Pin the state-based scheduler so the next auto-epoch lands at + // `LastEpochBlock + tempo`. Freeze window covers blocks (next_auto - 3, next_auto]. + pallet_subtensor::LastEpochBlock::::insert(netuid, 0); + let next_auto = tempo as u64; + // Advance to a block inside the freeze window (remaining < 3). + run_to_block(next_auto - 2); // Root should be blocked during freeze window assert_noop!( @@ -2147,7 +2224,7 @@ fn test_owner_hyperparam_update_rate_limit_enforced() { SubnetOwner::::insert(netuid, owner); // Set tempo to 1 so owner hyperparam RL = 2 tempos = 2 blocks - SubtensorModule::set_tempo(netuid, 1); + SubtensorModule::set_tempo_unchecked(netuid, 1); // Disable admin freeze window to avoid blocking on small tempo assert_ok!(AdminUtils::sudo_set_admin_freeze_window( <::RuntimeOrigin>::root(), @@ -2202,7 +2279,7 @@ fn test_hyperparam_rate_limit_enforced_by_tempo() { SubnetOwner::::insert(netuid, owner); // Set tempo to 1 so RL = 2 blocks - SubtensorModule::set_tempo(netuid, 1); + SubtensorModule::set_tempo_unchecked(netuid, 1); // Disable admin freeze window to avoid blocking on small tempo assert_ok!(AdminUtils::sudo_set_admin_freeze_window( <::RuntimeOrigin>::root(), @@ -2250,7 +2327,7 @@ fn test_owner_hyperparam_rate_limit_independent_per_param() { SubnetOwner::::insert(netuid, owner); // Use small tempo to make RL short and deterministic (2 blocks when tempo=1) - SubtensorModule::set_tempo(netuid, 1); + SubtensorModule::set_tempo_unchecked(netuid, 1); // Disable admin freeze window so it doesn't interfere with small tempo assert_ok!(AdminUtils::sudo_set_admin_freeze_window( <::RuntimeOrigin>::root(), diff --git a/pallets/admin-utils/src/weights.rs b/pallets/admin-utils/src/weights.rs index 8320052fe1..26b4a52fe0 100644 --- a/pallets/admin-utils/src/weights.rs +++ b/pallets/admin-utils/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_admin_utils` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-05-25, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-06-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervmg397c`, CPU: `AMD EPYC 7763 64-Core Processor` +//! HOSTNAME: `runnervm7b5n9`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.JEdED8lJZP +// --output=/tmp/tmp.oS4msGVv9F // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -93,6 +93,7 @@ pub trait WeightInfo { fn sudo_set_owner_immune_neuron_limit() -> Weight; fn sudo_trim_to_max_allowed_uids() -> Weight; fn sudo_set_min_non_immune_uids() -> Weight; + fn sudo_set_max_epochs_per_block() -> Weight; } /// Weights for `pallet_admin_utils` using the Substrate node and recommended hardware. @@ -105,10 +106,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_037_000 picoseconds. - Weight::from_parts(4_538_772, 0) - // Standard Error: 736 - .saturating_add(Weight::from_parts(25_351, 0).saturating_mul(a.into())) + // Minimum execution time: 3_777_000 picoseconds. + Weight::from_parts(4_442_146, 0) + // Standard Error: 826 + .saturating_add(Weight::from_parts(29_392, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Grandpa::PendingChange` (r:1 w:1) @@ -118,10 +119,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `174` // Estimated: `2779` - // Minimum execution time: 7_294_000 picoseconds. - Weight::from_parts(7_890_823, 2779) - // Standard Error: 864 - .saturating_add(Weight::from_parts(17_669, 0).saturating_mul(a.into())) + // Minimum execution time: 7_233_000 picoseconds. + Weight::from_parts(7_972_506, 2779) + // Standard Error: 896 + .saturating_add(Weight::from_parts(15_444, 0).saturating_mul(a.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -131,27 +132,35 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(5_640_000, 0) + // Minimum execution time: 5_100_000 picoseconds. + Weight::from_parts(5_400_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:0 w:1) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_serving_rate_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `627` - // Estimated: `4092` - // Minimum execution time: 20_679_000 picoseconds. - Weight::from_parts(21_500_000, 4092) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `747` + // Estimated: `4212` + // Minimum execution time: 27_641_000 picoseconds. + Weight::from_parts(28_633_000, 4212) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -160,15 +169,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxDifficulty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_difficulty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_119_000 picoseconds. - Weight::from_parts(26_870_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_653_000 picoseconds. + Weight::from_parts(34_704_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -177,11 +190,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MinDifficulty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_difficulty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 25_999_000 picoseconds. - Weight::from_parts(26_890_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_482_000 picoseconds. + Weight::from_parts(34_554_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -192,13 +205,17 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_890_000 picoseconds. - Weight::from_parts(16_571_000, 4084) + // Minimum execution time: 15_799_000 picoseconds. + Weight::from_parts(16_420_000, 4084) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -207,15 +224,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::WeightsVersionKey` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_weights_version_key() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_109_000 picoseconds. - Weight::from_parts(26_960_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_192_000 picoseconds. + Weight::from_parts(34_204_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -224,15 +245,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::BondsMovingAverage` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_bonds_moving_average() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_049_000 picoseconds. - Weight::from_parts(27_130_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_432_000 picoseconds. + Weight::from_parts(34_495_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -241,15 +266,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::BondsPenalty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_bonds_penalty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_179_000 picoseconds. - Weight::from_parts(27_021_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_633_000 picoseconds. + Weight::from_parts(34_354_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -260,15 +289,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxAllowedValidators` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_allowed_validators() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 27_652_000 picoseconds. - Weight::from_parts(28_463_000, 4235) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 35_015_000 picoseconds. + Weight::from_parts(35_896_000, 4383) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -277,11 +310,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Difficulty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_difficulty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_359_000 picoseconds. - Weight::from_parts(27_091_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_442_000 picoseconds. + Weight::from_parts(34_434_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -292,13 +325,17 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_729_000 picoseconds. - Weight::from_parts(16_811_000, 4084) + // Minimum execution time: 15_770_000 picoseconds. + Weight::from_parts(16_320_000, 4084) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -307,15 +344,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TargetRegistrationsPerInterval` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_target_registrations_per_interval() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_169_000 picoseconds. - Weight::from_parts(26_990_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_472_000 picoseconds. + Weight::from_parts(34_103_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -326,15 +367,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::ActivityCutoff` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_activity_cutoff() -> Weight { // Proof Size summary in bytes: - // Measured: `832` - // Estimated: `4297` - // Minimum execution time: 28_202_000 picoseconds. - Weight::from_parts(29_676_000, 4297) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 34_023_000 picoseconds. + Weight::from_parts(35_165_000, 4383) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -343,11 +388,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Rho` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_rho() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 23_193_000 picoseconds. - Weight::from_parts(23_735_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 30_646_000 picoseconds. + Weight::from_parts(31_268_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -358,13 +403,17 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 16_009_000 picoseconds. - Weight::from_parts(16_501_000, 4084) + // Minimum execution time: 15_719_000 picoseconds. + Weight::from_parts(16_301_000, 4084) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -377,15 +426,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MinAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_allowed_uids() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 29_495_000 picoseconds. - Weight::from_parts(30_577_000, 4235) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 36_909_000 picoseconds. + Weight::from_parts(37_890_000, 4383) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -400,15 +453,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_allowed_uids() -> Weight { // Proof Size summary in bytes: - // Measured: `820` - // Estimated: `4285` - // Minimum execution time: 34_475_000 picoseconds. - Weight::from_parts(35_696_000, 4285) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `968` + // Estimated: `4433` + // Minimum execution time: 42_299_000 picoseconds. + Weight::from_parts(43_381_000, 4433) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -417,15 +474,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MinAllowedWeights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_allowed_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_209_000 picoseconds. - Weight::from_parts(27_151_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_472_000 picoseconds. + Weight::from_parts(34_664_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -434,15 +495,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::ImmunityPeriod` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_immunity_period() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_239_000 picoseconds. - Weight::from_parts(26_920_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_582_000 picoseconds. + Weight::from_parts(34_414_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -451,15 +516,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxRegistrationsPerBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_registrations_per_block() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 25_969_000 picoseconds. - Weight::from_parts(26_951_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_953_000 picoseconds. + Weight::from_parts(34_604_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -470,15 +539,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxBurn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `797` - // Estimated: `4262` - // Minimum execution time: 28_954_000 picoseconds. - Weight::from_parts(29_765_000, 4262) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `945` + // Estimated: `4410` + // Minimum execution time: 35_726_000 picoseconds. + Weight::from_parts(36_949_000, 4410) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -489,11 +562,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MinBurn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `772` - // Estimated: `4237` - // Minimum execution time: 29_265_000 picoseconds. - Weight::from_parts(30_005_000, 4237) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `920` + // Estimated: `4385` + // Minimum execution time: 36_458_000 picoseconds. + Weight::from_parts(37_780_000, 4385) + .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworkRegistrationAllowed` (r:0 w:1) @@ -502,27 +575,35 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_683_000 picoseconds. - Weight::from_parts(7_124_000, 0) + // Minimum execution time: 6_772_000 picoseconds. + Weight::from_parts(7_113_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:1) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_tempo() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 25_758_000 picoseconds. - Weight::from_parts(26_580_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 34_965_000 picoseconds. + Weight::from_parts(36_298_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -531,15 +612,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_commit_reveal_weights_interval() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_099_000 picoseconds. - Weight::from_parts(27_551_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_693_000 picoseconds. + Weight::from_parts(34_654_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -548,11 +633,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_commit_reveal_weights_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_319_000 picoseconds. - Weight::from_parts(26_940_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_612_000 picoseconds. + Weight::from_parts(34_544_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsVersion` (r:0 w:1) @@ -561,8 +646,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_871_000 picoseconds. - Weight::from_parts(6_292_000, 0) + // Minimum execution time: 5_570_000 picoseconds. + Weight::from_parts(6_052_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::TxRateLimit` (r:0 w:1) @@ -571,16 +656,16 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_130_000 picoseconds. - Weight::from_parts(5_500_000, 0) + // Minimum execution time: 5_070_000 picoseconds. + Weight::from_parts(5_440_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn sudo_set_total_issuance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_851_000 picoseconds. - Weight::from_parts(6_102_000, 0) + // Minimum execution time: 5_791_000 picoseconds. + Weight::from_parts(5_931_000, 0) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -590,8 +675,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_890_000 picoseconds. - Weight::from_parts(16_380_000, 4084) + // Minimum execution time: 15_879_000 picoseconds. + Weight::from_parts(16_541_000, 4084) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -601,8 +686,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_259_000 picoseconds. - Weight::from_parts(5_510_000, 0) + // Minimum execution time: 5_170_000 picoseconds. + Weight::from_parts(5_500_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NominatorMinRequiredStake` (r:1 w:1) @@ -615,10 +700,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_nominator_min_required_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `912` - // Estimated: `6852` - // Minimum execution time: 28_783_000 picoseconds. - Weight::from_parts(29_535_000, 6852) + // Measured: `950` + // Estimated: `6890` + // Minimum execution time: 29_635_000 picoseconds. + Weight::from_parts(30_697_000, 6890) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -628,8 +713,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_190_000 picoseconds. - Weight::from_parts(5_390_000, 0) + // Minimum execution time: 5_039_000 picoseconds. + Weight::from_parts(5_420_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::MinDelegateTake` (r:0 w:1) @@ -638,12 +723,16 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_179_000 picoseconds. - Weight::from_parts(5_471_000, 0) + // Minimum execution time: 5_130_000 picoseconds. + Weight::from_parts(5_490_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -656,30 +745,38 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MinChildkeyTakePerSubnet` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_childkey_take_per_subnet() -> Weight { // Proof Size summary in bytes: - // Measured: `806` - // Estimated: `4271` - // Minimum execution time: 29_535_000 picoseconds. - Weight::from_parts(30_327_000, 4271) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Measured: `954` + // Estimated: `4419` + // Minimum execution time: 36_879_000 picoseconds. + Weight::from_parts(38_100_000, 4419) + .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LiquidAlphaOn` (r:0 w:1) /// Proof: `SubtensorModule::LiquidAlphaOn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_liquid_alpha_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 17_453_000 picoseconds. - Weight::from_parts(18_084_000, 4132) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 24_977_000 picoseconds. + Weight::from_parts(25_538_000, 4280) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LiquidAlphaOn` (r:1 w:0) @@ -688,11 +785,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::AlphaValues` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_alpha_values() -> Weight { // Proof Size summary in bytes: - // Measured: `814` - // Estimated: `4279` - // Minimum execution time: 25_918_000 picoseconds. - Weight::from_parts(26_509_000, 4279) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `962` + // Estimated: `4427` + // Minimum execution time: 33_152_000 picoseconds. + Weight::from_parts(34_224_000, 4427) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::ColdkeySwapAnnouncementDelay` (r:0 w:1) @@ -701,8 +798,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_190_000 picoseconds. - Weight::from_parts(5_511_000, 0) + // Minimum execution time: 5_180_000 picoseconds. + Weight::from_parts(5_370_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::ColdkeySwapReannouncementDelay` (r:0 w:1) @@ -711,8 +808,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_270_000 picoseconds. - Weight::from_parts(5_500_000, 0) + // Minimum execution time: 4_980_000 picoseconds. + Weight::from_parts(5_360_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::DissolveNetworkScheduleDuration` (r:0 w:1) @@ -721,23 +818,27 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_139_000 picoseconds. - Weight::from_parts(5_440_000, 0) + // Minimum execution time: 5_209_000 picoseconds. + Weight::from_parts(5_591_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TransferToggle` (r:0 w:1) /// Proof: `SubtensorModule::TransferToggle` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_toggle_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 19_987_000 picoseconds. - Weight::from_parts(20_628_000, 4132) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 27_702_000 picoseconds. + Weight::from_parts(28_343_000, 4280) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `AdminUtils::PrecompileEnable` (r:1 w:0) @@ -746,8 +847,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 6_161_000 picoseconds. - Weight::from_parts(6_382_000, 3507) + // Minimum execution time: 5_851_000 picoseconds. + Weight::from_parts(6_162_000, 3507) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `SubtensorModule::SubnetMovingAlpha` (r:0 w:1) @@ -756,8 +857,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_665_000 picoseconds. - Weight::from_parts(2_945_000, 0) + // Minimum execution time: 2_635_000 picoseconds. + Weight::from_parts(2_955_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::EMAPriceHalvingBlocks` (r:0 w:1) @@ -766,12 +867,16 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_857_000 picoseconds. - Weight::from_parts(4_158_000, 0) + // Minimum execution time: 3_797_000 picoseconds. + Weight::from_parts(4_198_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -780,41 +885,49 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::AlphaSigmoidSteepness` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_alpha_sigmoid_steepness() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 23_253_000 picoseconds. - Weight::from_parts(23_824_000, 4235) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 30_396_000 picoseconds. + Weight::from_parts(31_438_000, 4383) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Yuma3On` (r:0 w:1) /// Proof: `SubtensorModule::Yuma3On` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_yuma3_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 20_408_000 picoseconds. - Weight::from_parts(20_928_000, 4132) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 27_962_000 picoseconds. + Weight::from_parts(28_583_000, 4280) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::BondsResetOn` (r:0 w:1) /// Proof: `SubtensorModule::BondsResetOn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_bonds_reset_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 22_281_000 picoseconds. - Weight::from_parts(23_013_000, 4132) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 29_565_000 picoseconds. + Weight::from_parts(30_507_000, 4280) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -825,8 +938,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_729_000 picoseconds. - Weight::from_parts(16_490_000, 4084) + // Minimum execution time: 15_720_000 picoseconds. + Weight::from_parts(16_351_000, 4084) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -840,24 +953,28 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `712` // Estimated: `4177` - // Minimum execution time: 24_325_000 picoseconds. - Weight::from_parts(25_427_000, 4177) + // Minimum execution time: 25_036_000 picoseconds. + Weight::from_parts(25_687_000, 4177) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:0 w:1) /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_subtoken_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 16_661_000 picoseconds. - Weight::from_parts(17_342_000, 4132) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 24_526_000 picoseconds. + Weight::from_parts(25_467_000, 4280) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::AdminFreezeWindow` (r:0 w:1) @@ -866,8 +983,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_279_000 picoseconds. - Weight::from_parts(5_540_000, 0) + // Minimum execution time: 5_119_000 picoseconds. + Weight::from_parts(5_450_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:0 w:1) @@ -876,27 +993,35 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_260_000 picoseconds. - Weight::from_parts(5_620_000, 0) + // Minimum execution time: 5_239_000 picoseconds. + Weight::from_parts(5_471_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ImmuneOwnerUidsLimit` (r:0 w:1) /// Proof: `SubtensorModule::ImmuneOwnerUidsLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_owner_immune_neuron_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 16_961_000 picoseconds. - Weight::from_parts(17_663_000, 4132) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 24_385_000 picoseconds. + Weight::from_parts(25_107_000, 4280) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -909,11 +1034,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_trim_to_max_allowed_uids() -> Weight { // Proof Size summary in bytes: - // Measured: `785` - // Estimated: `4250` - // Minimum execution time: 29_645_000 picoseconds. - Weight::from_parts(30_477_000, 4250) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `933` + // Estimated: `4398` + // Minimum execution time: 37_540_000 picoseconds. + Weight::from_parts(38_302_000, 4398) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::MinNonImmuneUids` (r:0 w:1) @@ -922,8 +1047,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_953_000 picoseconds. - Weight::from_parts(7_134_000, 0) + // Minimum execution time: 6_523_000 picoseconds. + Weight::from_parts(6_783_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `SubtensorModule::MaxEpochsPerBlock` (r:0 w:1) + /// Proof: `SubtensorModule::MaxEpochsPerBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn sudo_set_max_epochs_per_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_219_000 picoseconds. + Weight::from_parts(5_530_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } } @@ -937,10 +1072,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_037_000 picoseconds. - Weight::from_parts(4_538_772, 0) - // Standard Error: 736 - .saturating_add(Weight::from_parts(25_351, 0).saturating_mul(a.into())) + // Minimum execution time: 3_777_000 picoseconds. + Weight::from_parts(4_442_146, 0) + // Standard Error: 826 + .saturating_add(Weight::from_parts(29_392, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Grandpa::PendingChange` (r:1 w:1) @@ -950,10 +1085,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `174` // Estimated: `2779` - // Minimum execution time: 7_294_000 picoseconds. - Weight::from_parts(7_890_823, 2779) - // Standard Error: 864 - .saturating_add(Weight::from_parts(17_669, 0).saturating_mul(a.into())) + // Minimum execution time: 7_233_000 picoseconds. + Weight::from_parts(7_972_506, 2779) + // Standard Error: 896 + .saturating_add(Weight::from_parts(15_444, 0).saturating_mul(a.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -963,27 +1098,35 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(5_640_000, 0) + // Minimum execution time: 5_100_000 picoseconds. + Weight::from_parts(5_400_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:0 w:1) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_serving_rate_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `627` - // Estimated: `4092` - // Minimum execution time: 20_679_000 picoseconds. - Weight::from_parts(21_500_000, 4092) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `747` + // Estimated: `4212` + // Minimum execution time: 27_641_000 picoseconds. + Weight::from_parts(28_633_000, 4212) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -992,15 +1135,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxDifficulty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_difficulty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_119_000 picoseconds. - Weight::from_parts(26_870_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_653_000 picoseconds. + Weight::from_parts(34_704_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1009,11 +1156,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MinDifficulty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_difficulty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 25_999_000 picoseconds. - Weight::from_parts(26_890_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_482_000 picoseconds. + Weight::from_parts(34_554_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1024,13 +1171,17 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_890_000 picoseconds. - Weight::from_parts(16_571_000, 4084) + // Minimum execution time: 15_799_000 picoseconds. + Weight::from_parts(16_420_000, 4084) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1039,15 +1190,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::WeightsVersionKey` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_weights_version_key() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_109_000 picoseconds. - Weight::from_parts(26_960_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_192_000 picoseconds. + Weight::from_parts(34_204_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1056,15 +1211,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::BondsMovingAverage` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_bonds_moving_average() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_049_000 picoseconds. - Weight::from_parts(27_130_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_432_000 picoseconds. + Weight::from_parts(34_495_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1073,15 +1232,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::BondsPenalty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_bonds_penalty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_179_000 picoseconds. - Weight::from_parts(27_021_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_633_000 picoseconds. + Weight::from_parts(34_354_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1092,15 +1255,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxAllowedValidators` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_allowed_validators() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 27_652_000 picoseconds. - Weight::from_parts(28_463_000, 4235) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 35_015_000 picoseconds. + Weight::from_parts(35_896_000, 4383) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1109,11 +1276,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Difficulty` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_difficulty() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_359_000 picoseconds. - Weight::from_parts(27_091_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_442_000 picoseconds. + Weight::from_parts(34_434_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1124,13 +1291,17 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_729_000 picoseconds. - Weight::from_parts(16_811_000, 4084) + // Minimum execution time: 15_770_000 picoseconds. + Weight::from_parts(16_320_000, 4084) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1139,15 +1310,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TargetRegistrationsPerInterval` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_target_registrations_per_interval() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_169_000 picoseconds. - Weight::from_parts(26_990_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_472_000 picoseconds. + Weight::from_parts(34_103_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1158,15 +1333,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::ActivityCutoff` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_activity_cutoff() -> Weight { // Proof Size summary in bytes: - // Measured: `832` - // Estimated: `4297` - // Minimum execution time: 28_202_000 picoseconds. - Weight::from_parts(29_676_000, 4297) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 34_023_000 picoseconds. + Weight::from_parts(35_165_000, 4383) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1175,11 +1354,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Rho` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_rho() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 23_193_000 picoseconds. - Weight::from_parts(23_735_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 30_646_000 picoseconds. + Weight::from_parts(31_268_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1190,13 +1369,17 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 16_009_000 picoseconds. - Weight::from_parts(16_501_000, 4084) + // Minimum execution time: 15_719_000 picoseconds. + Weight::from_parts(16_301_000, 4084) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1209,15 +1392,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MinAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_allowed_uids() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 29_495_000 picoseconds. - Weight::from_parts(30_577_000, 4235) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 36_909_000 picoseconds. + Weight::from_parts(37_890_000, 4383) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1232,15 +1419,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_allowed_uids() -> Weight { // Proof Size summary in bytes: - // Measured: `820` - // Estimated: `4285` - // Minimum execution time: 34_475_000 picoseconds. - Weight::from_parts(35_696_000, 4285) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `968` + // Estimated: `4433` + // Minimum execution time: 42_299_000 picoseconds. + Weight::from_parts(43_381_000, 4433) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1249,15 +1440,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MinAllowedWeights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_allowed_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_209_000 picoseconds. - Weight::from_parts(27_151_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_472_000 picoseconds. + Weight::from_parts(34_664_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1266,15 +1461,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::ImmunityPeriod` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_immunity_period() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_239_000 picoseconds. - Weight::from_parts(26_920_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_582_000 picoseconds. + Weight::from_parts(34_414_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1283,15 +1482,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxRegistrationsPerBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_registrations_per_block() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 25_969_000 picoseconds. - Weight::from_parts(26_951_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_953_000 picoseconds. + Weight::from_parts(34_604_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1302,15 +1505,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxBurn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_max_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `797` - // Estimated: `4262` - // Minimum execution time: 28_954_000 picoseconds. - Weight::from_parts(29_765_000, 4262) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `945` + // Estimated: `4410` + // Minimum execution time: 35_726_000 picoseconds. + Weight::from_parts(36_949_000, 4410) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1321,11 +1528,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MinBurn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `772` - // Estimated: `4237` - // Minimum execution time: 29_265_000 picoseconds. - Weight::from_parts(30_005_000, 4237) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `920` + // Estimated: `4385` + // Minimum execution time: 36_458_000 picoseconds. + Weight::from_parts(37_780_000, 4385) + .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworkRegistrationAllowed` (r:0 w:1) @@ -1334,27 +1541,35 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_683_000 picoseconds. - Weight::from_parts(7_124_000, 0) + // Minimum execution time: 6_772_000 picoseconds. + Weight::from_parts(7_113_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:1) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_tempo() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 25_758_000 picoseconds. - Weight::from_parts(26_580_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 34_965_000 picoseconds. + Weight::from_parts(36_298_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1363,15 +1578,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_commit_reveal_weights_interval() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_099_000 picoseconds. - Weight::from_parts(27_551_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_693_000 picoseconds. + Weight::from_parts(34_654_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1380,11 +1599,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_commit_reveal_weights_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 26_319_000 picoseconds. - Weight::from_parts(26_940_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 33_612_000 picoseconds. + Weight::from_parts(34_544_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsVersion` (r:0 w:1) @@ -1393,8 +1612,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_871_000 picoseconds. - Weight::from_parts(6_292_000, 0) + // Minimum execution time: 5_570_000 picoseconds. + Weight::from_parts(6_052_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::TxRateLimit` (r:0 w:1) @@ -1403,16 +1622,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_130_000 picoseconds. - Weight::from_parts(5_500_000, 0) + // Minimum execution time: 5_070_000 picoseconds. + Weight::from_parts(5_440_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn sudo_set_total_issuance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_851_000 picoseconds. - Weight::from_parts(6_102_000, 0) + // Minimum execution time: 5_791_000 picoseconds. + Weight::from_parts(5_931_000, 0) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1422,8 +1641,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_890_000 picoseconds. - Weight::from_parts(16_380_000, 4084) + // Minimum execution time: 15_879_000 picoseconds. + Weight::from_parts(16_541_000, 4084) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1433,8 +1652,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_259_000 picoseconds. - Weight::from_parts(5_510_000, 0) + // Minimum execution time: 5_170_000 picoseconds. + Weight::from_parts(5_500_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NominatorMinRequiredStake` (r:1 w:1) @@ -1447,10 +1666,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_nominator_min_required_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `912` - // Estimated: `6852` - // Minimum execution time: 28_783_000 picoseconds. - Weight::from_parts(29_535_000, 6852) + // Measured: `950` + // Estimated: `6890` + // Minimum execution time: 29_635_000 picoseconds. + Weight::from_parts(30_697_000, 6890) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1460,8 +1679,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_190_000 picoseconds. - Weight::from_parts(5_390_000, 0) + // Minimum execution time: 5_039_000 picoseconds. + Weight::from_parts(5_420_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::MinDelegateTake` (r:0 w:1) @@ -1470,12 +1689,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_179_000 picoseconds. - Weight::from_parts(5_471_000, 0) + // Minimum execution time: 5_130_000 picoseconds. + Weight::from_parts(5_490_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1488,30 +1711,38 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MinChildkeyTakePerSubnet` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_min_childkey_take_per_subnet() -> Weight { // Proof Size summary in bytes: - // Measured: `806` - // Estimated: `4271` - // Minimum execution time: 29_535_000 picoseconds. - Weight::from_parts(30_327_000, 4271) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + // Measured: `954` + // Estimated: `4419` + // Minimum execution time: 36_879_000 picoseconds. + Weight::from_parts(38_100_000, 4419) + .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LiquidAlphaOn` (r:0 w:1) /// Proof: `SubtensorModule::LiquidAlphaOn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_liquid_alpha_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 17_453_000 picoseconds. - Weight::from_parts(18_084_000, 4132) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 24_977_000 picoseconds. + Weight::from_parts(25_538_000, 4280) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LiquidAlphaOn` (r:1 w:0) @@ -1520,11 +1751,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::AlphaValues` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_alpha_values() -> Weight { // Proof Size summary in bytes: - // Measured: `814` - // Estimated: `4279` - // Minimum execution time: 25_918_000 picoseconds. - Weight::from_parts(26_509_000, 4279) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `962` + // Estimated: `4427` + // Minimum execution time: 33_152_000 picoseconds. + Weight::from_parts(34_224_000, 4427) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::ColdkeySwapAnnouncementDelay` (r:0 w:1) @@ -1533,8 +1764,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_190_000 picoseconds. - Weight::from_parts(5_511_000, 0) + // Minimum execution time: 5_180_000 picoseconds. + Weight::from_parts(5_370_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::ColdkeySwapReannouncementDelay` (r:0 w:1) @@ -1543,8 +1774,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_270_000 picoseconds. - Weight::from_parts(5_500_000, 0) + // Minimum execution time: 4_980_000 picoseconds. + Weight::from_parts(5_360_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::DissolveNetworkScheduleDuration` (r:0 w:1) @@ -1553,23 +1784,27 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_139_000 picoseconds. - Weight::from_parts(5_440_000, 0) + // Minimum execution time: 5_209_000 picoseconds. + Weight::from_parts(5_591_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TransferToggle` (r:0 w:1) /// Proof: `SubtensorModule::TransferToggle` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_toggle_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 19_987_000 picoseconds. - Weight::from_parts(20_628_000, 4132) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 27_702_000 picoseconds. + Weight::from_parts(28_343_000, 4280) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `AdminUtils::PrecompileEnable` (r:1 w:0) @@ -1578,8 +1813,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `42` // Estimated: `3507` - // Minimum execution time: 6_161_000 picoseconds. - Weight::from_parts(6_382_000, 3507) + // Minimum execution time: 5_851_000 picoseconds. + Weight::from_parts(6_162_000, 3507) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `SubtensorModule::SubnetMovingAlpha` (r:0 w:1) @@ -1588,8 +1823,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_665_000 picoseconds. - Weight::from_parts(2_945_000, 0) + // Minimum execution time: 2_635_000 picoseconds. + Weight::from_parts(2_955_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::EMAPriceHalvingBlocks` (r:0 w:1) @@ -1598,12 +1833,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_857_000 picoseconds. - Weight::from_parts(4_158_000, 0) + // Minimum execution time: 3_797_000 picoseconds. + Weight::from_parts(4_198_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1612,41 +1851,49 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::AlphaSigmoidSteepness` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_alpha_sigmoid_steepness() -> Weight { // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `4235` - // Minimum execution time: 23_253_000 picoseconds. - Weight::from_parts(23_824_000, 4235) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `918` + // Estimated: `4383` + // Minimum execution time: 30_396_000 picoseconds. + Weight::from_parts(31_438_000, 4383) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Yuma3On` (r:0 w:1) /// Proof: `SubtensorModule::Yuma3On` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_yuma3_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 20_408_000 picoseconds. - Weight::from_parts(20_928_000, 4132) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 27_962_000 picoseconds. + Weight::from_parts(28_583_000, 4280) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::BondsResetOn` (r:0 w:1) /// Proof: `SubtensorModule::BondsResetOn` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_bonds_reset_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 22_281_000 picoseconds. - Weight::from_parts(23_013_000, 4132) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 29_565_000 picoseconds. + Weight::from_parts(30_507_000, 4280) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1657,8 +1904,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `619` // Estimated: `4084` - // Minimum execution time: 15_729_000 picoseconds. - Weight::from_parts(16_490_000, 4084) + // Minimum execution time: 15_720_000 picoseconds. + Weight::from_parts(16_351_000, 4084) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1672,24 +1919,28 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `712` // Estimated: `4177` - // Minimum execution time: 24_325_000 picoseconds. - Weight::from_parts(25_427_000, 4177) + // Minimum execution time: 25_036_000 picoseconds. + Weight::from_parts(25_687_000, 4177) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:0 w:1) /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_subtoken_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 16_661_000 picoseconds. - Weight::from_parts(17_342_000, 4132) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 24_526_000 picoseconds. + Weight::from_parts(25_467_000, 4280) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::AdminFreezeWindow` (r:0 w:1) @@ -1698,8 +1949,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_279_000 picoseconds. - Weight::from_parts(5_540_000, 0) + // Minimum execution time: 5_119_000 picoseconds. + Weight::from_parts(5_450_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:0 w:1) @@ -1708,27 +1959,35 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_260_000 picoseconds. - Weight::from_parts(5_620_000, 0) + // Minimum execution time: 5_239_000 picoseconds. + Weight::from_parts(5_471_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ImmuneOwnerUidsLimit` (r:0 w:1) /// Proof: `SubtensorModule::ImmuneOwnerUidsLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_set_owner_immune_neuron_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `667` - // Estimated: `4132` - // Minimum execution time: 16_961_000 picoseconds. - Weight::from_parts(17_663_000, 4132) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `815` + // Estimated: `4280` + // Minimum execution time: 24_385_000 picoseconds. + Weight::from_parts(25_107_000, 4280) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -1741,11 +2000,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) fn sudo_trim_to_max_allowed_uids() -> Weight { // Proof Size summary in bytes: - // Measured: `785` - // Estimated: `4250` - // Minimum execution time: 29_645_000 picoseconds. - Weight::from_parts(30_477_000, 4250) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `933` + // Estimated: `4398` + // Minimum execution time: 37_540_000 picoseconds. + Weight::from_parts(38_302_000, 4398) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::MinNonImmuneUids` (r:0 w:1) @@ -1754,8 +2013,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_953_000 picoseconds. - Weight::from_parts(7_134_000, 0) + // Minimum execution time: 6_523_000 picoseconds. + Weight::from_parts(6_783_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `SubtensorModule::MaxEpochsPerBlock` (r:0 w:1) + /// Proof: `SubtensorModule::MaxEpochsPerBlock` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn sudo_set_max_epochs_per_block() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 5_219_000 picoseconds. + Weight::from_parts(5_530_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } } diff --git a/pallets/commitments/src/lib.rs b/pallets/commitments/src/lib.rs index 2bbf8ecaf1..b5f28a0968 100644 --- a/pallets/commitments/src/lib.rs +++ b/pallets/commitments/src/lib.rs @@ -406,6 +406,8 @@ impl Pallet { let original_fields = registration.info.fields.clone(); let mut remain_fields = Vec::new(); let mut revealed_fields = Vec::new(); + let mut saw_timelock = false; + let mut processed_timelock = false; for data in original_fields { match data { @@ -413,6 +415,7 @@ impl Pallet { encrypted, reveal_round, } => { + saw_timelock = true; total_weight = total_weight.saturating_add(T::DbWeight::get().reads(1)); let pulse = match pallet_drand::Pulses::::get(reveal_round) { Some(p) => p, @@ -425,6 +428,8 @@ impl Pallet { } }; + processed_timelock = true; + let signature_bytes = pulse .signature .strip_prefix(b"0x") @@ -478,6 +483,29 @@ impl Pallet { } } + if !saw_timelock { + TimelockedIndex::::mutate(|idx| { + idx.remove(&(netuid, who.clone())); + }); + total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + continue; + } + + // Do not rewrite CommitmentOf every block for entries whose reveal round is + // not yet available in the drand pulse storage. The hook has only performed + // the index, commitment, and pulse reads accounted above. + if !processed_timelock { + continue; + } + + let Ok(remaining_fields) = BoundedVec::try_from(remain_fields) else { + log::error!( + "Failed to build BoundedVec for remain_fields; this should be impossible \ + because remain_fields is a subset of the original commitment fields" + ); + continue; + }; + if !revealed_fields.is_empty() { let mut existing_reveals = RevealedCommitments::::get(netuid, &who).unwrap_or_default(); @@ -489,7 +517,6 @@ impl Pallet { // Push newly revealed items onto the tail of existing_reveals and emit the event for revealed_bytes in revealed_fields { existing_reveals.push((revealed_bytes, block_u64)); - Self::deposit_event(Event::CommitmentRevealed { netuid, who: who.clone(), @@ -506,8 +533,7 @@ impl Pallet { total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); } - registration.info.fields = BoundedVec::try_from(remain_fields) - .map_err(|_| "Failed to build BoundedVec for remain_fields")?; + registration.info.fields = remaining_fields; match registration.info.fields.is_empty() { true => { @@ -534,7 +560,6 @@ impl Pallet { TimelockedIndex::::mutate(|idx| { idx.remove(&(netuid, who.clone())); }); - total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); } diff --git a/pallets/commitments/src/mock.rs b/pallets/commitments/src/mock.rs index 3db0e8f312..10bbf7bbb1 100644 --- a/pallets/commitments/src/mock.rs +++ b/pallets/commitments/src/mock.rs @@ -3,9 +3,8 @@ use crate as pallet_commitments; use frame_support::{ derive_impl, pallet_prelude::{Get, TypeInfo}, - traits::{ConstU32, ConstU64, InherentBuilder}, + traits::{ConstU32, ConstU64}, }; -use frame_system::offchain::CreateTransactionBase; use sp_core::H256; use sp_runtime::{ BuildStorage, @@ -169,37 +168,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - // Create a dummy sr25519 signature from a raw byte array - let dummy_raw = [0u8; 64]; - let dummy_signature = sp_core::sr25519::Signature::from(dummy_raw); - let signature = test_crypto::Signature::from(dummy_signature); - Some(UncheckedExtrinsic::new_signed( - call, - nonce.into(), - signature, - (), - )) + UncheckedExtrinsic::new_bare(call) } } diff --git a/pallets/drand/src/lib.rs b/pallets/drand/src/lib.rs index 130f796a90..f9a281038a 100644 --- a/pallets/drand/src/lib.rs +++ b/pallets/drand/src/lib.rs @@ -43,8 +43,7 @@ use codec::Encode; use frame_support::{pallet_prelude::*, traits::Randomness}; use frame_system::{ offchain::{ - AppCrypto, CreateInherent, CreateSignedTransaction, SendUnsignedTransaction, SignedPayload, - Signer, SigningTypes, + AppCrypto, CreateBare, SendUnsignedTransaction, SignedPayload, Signer, SigningTypes, }, pallet_prelude::BlockNumberFor, }; @@ -162,9 +161,7 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: - CreateSignedTransaction> + CreateInherent> + frame_system::Config - { + pub trait Config: CreateBare> + SigningTypes + frame_system::Config { /// The identifier type for an offchain worker. type AuthorityId: AppCrypto; /// something that knows how to verify beacon pulses diff --git a/pallets/drand/src/mock.rs b/pallets/drand/src/mock.rs index 3be3a6a8d1..aa370292b1 100644 --- a/pallets/drand/src/mock.rs +++ b/pallets/drand/src/mock.rs @@ -3,14 +3,14 @@ use crate::verifier::*; use crate::*; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU16, ConstU64, InherentBuilder}, + traits::{ConstU16, ConstU64}, }; use sp_core::{H256, sr25519::Signature}; use sp_keystore::{KeystoreExt, testing::MemoryKeystore}; use sp_runtime::{ BuildStorage, testing::TestXt, - traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + traits::{BlakeTwo256, IdentityLookup, Verify}, }; type Block = frame_system::mocking::MockBlock; @@ -52,7 +52,6 @@ impl frame_system::Config for Test { } type Extrinsic = TestXt; -type AccountId = <::Signer as IdentifyAccount>::AccountId; impl frame_system::offchain::SigningTypes for Test { type Public = ::Signer; @@ -67,28 +66,12 @@ where type Extrinsic = Extrinsic; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: RuntimeCall) -> Self::Extrinsic { - Extrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: RuntimeCall, - _public: ::Signer, - _account: AccountId, - nonce: u64, - ) -> Option { - Some(Extrinsic::new_signed(call, nonce, (), ())) + Extrinsic::new_bare(call) } } diff --git a/pallets/limit-orders/Cargo.toml b/pallets/limit-orders/Cargo.toml new file mode 100644 index 0000000000..48ffc61dcb --- /dev/null +++ b/pallets/limit-orders/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "pallet-limit-orders" +version = "0.1.0" +edition.workspace = true + +[dependencies] +codec = { workspace = true, features = ["derive"] } +frame-benchmarking = { workspace = true, optional = true } +sp-io = { workspace = true, optional = true } +sp-keyring = { workspace = true, optional = true } +frame-support.workspace = true +frame-system.workspace = true +scale-info.workspace = true +sp-core.workspace = true +sp-runtime.workspace = true +sp-std.workspace = true +log.workspace = true +substrate-fixed.workspace = true +subtensor-runtime-common.workspace = true +subtensor-macros.workspace = true +subtensor-swap-interface.workspace = true + +[dev-dependencies] +sp-io.workspace = true +sp-keyring.workspace = true +sp-keystore.workspace = true + +[lints] +workspace = true + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-io?/std", + "sp-keyring?/std", + "sp-keystore/std", + "sp-runtime/std", + "sp-std/std", + "log/std", + "substrate-fixed/std", + "subtensor-runtime-common/std", + "subtensor-swap-interface/std", +] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "sp-io", + "subtensor-runtime-common/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks", +] \ No newline at end of file diff --git a/pallets/limit-orders/README.md b/pallets/limit-orders/README.md new file mode 100644 index 0000000000..669980739a --- /dev/null +++ b/pallets/limit-orders/README.md @@ -0,0 +1,273 @@ +# pallet-limit-orders + +A FRAME pallet for off-chain signed limit orders on Bittensor subnets. + +Users sign orders off-chain and submit them to a relayer. The relayer batches +orders targeting the same subnet and submits them via `execute_batched_orders`, +which nets the buy and sell sides, executes a single AMM pool swap for the +residual, and distributes outputs pro-rata to all participants. This minimises +price impact compared to executing each order independently against the pool. + +MEV protection is available for free: any caller can wrap `execute_orders` or +`execute_batched_orders` inside `pallet_shield::submit_encrypted` to hide the +batch contents from the mempool until the block is proposed. + +--- + +## Order lifecycle + +``` +User signs VersionedOrder::V1(Order) off-chain + │ + ▼ +Relayer submits via execute_orders Relayer submits via execute_batched_orders + (one-by-one, best-effort) (aggregated, atomic) + │ │ + ├─ Invalid / expired / ├─ Any order invalid / expired / + │ price-not-met → │ price-not-met / root netuid → + │ skipped, emits OrderSkipped │ entire batch fails (DispatchError) + │ with DispatchError reason │ + │ │ + └─ Valid → executed └─ All orders valid → net pool swap + │ → distribute pro-rata + └─ order_id written to Orders as Fulfilled + (prevents replay) + +User can cancel at any time via cancel_order + └─ order_id written to Orders as Cancelled +``` + +--- + +## Data structures + +### `VersionedOrder` + +Versioned wrapper around an order payload. Currently has one variant: + +| Variant | Description | +|---------|-------------| +| `V1(Order)` | First version of the order schema. | + +Versioning lets the pallet accept orders signed against different schemas +simultaneously. When a new variant is added (`V2`, etc.), old `V1` signed orders +remain valid because the `OrderId` and signature both cover the full +`VersionedOrder` encoding (including the version discriminant byte). + +### `Order` + +The payload that a user signs off-chain, wrapped inside `VersionedOrder`. Never +stored in full on-chain — only the `blake2_256` hash of the `VersionedOrder` +encoding (`OrderId`) is persisted. + +| Field | Type | Description | +|-----------------|-------------|-------------| +| `signer` | `AccountId` | Coldkey that authorises the order. For buy types: pays TAO. For sell types: owns the staked alpha. | +| `hotkey` | `AccountId` | Hotkey to stake to (buy types) or unstake from (sell types). | +| `netuid` | `NetUid` | Target subnet. | +| `order_type` | `OrderType` | One of `LimitBuy`, `TakeProfit`, or `StopLoss` (see table below). | +| `amount` | `u64` | Input amount in raw units. TAO for buy types; alpha for sell types. | +| `limit_price` | `u64` | Price threshold in TAO/alpha raw units. Trigger direction depends on `OrderType` (see table below). | +| `expiry` | `u64` | Unix timestamp in milliseconds. Order must not execute after this time. | +| `fee_rate` | `Perbill` | Per-order fee as a fraction of the input amount. `Perbill::zero()` = no fee. | +| `fee_recipient` | `AccountId` | Account that receives the fee collected for this order. | +| `relayer` | `Option` | If `Some`, restricts execution to a single designated relayer account. Any attempt by a different account to execute this order is rejected with `RelayerMissMatch`. `None` = any relayer may execute. | +| `max_slippage` | `Option` | Maximum acceptable slippage in parts per billion applied to `limit_price` at swap time. `None` = no slippage protection (execute at market). When `Some(p)`: Buy ceiling = `limit_price + limit_price * p`; Sell floor = `limit_price - limit_price * p`. Both saturate at `u64` bounds. | + +### `OrderType` + +| Variant | Action | Triggers when | Use case | +|--------------|---------------|-------------------------|----------| +| `LimitBuy` | Buy alpha | price ≤ `limit_price` | Enter a position at or below a target price. | +| `TakeProfit` | Sell alpha | price ≥ `limit_price` | Exit a position once price rises to a profit target. | +| `StopLoss` | Sell alpha | price ≤ `limit_price` | Exit a position to limit downside if price falls to a floor. | + +### `SignedOrder` + +Envelope submitted by the relayer: the `VersionedOrder` payload plus the user's +sr25519 signature over the SCALE encoding of the `VersionedOrder` (including the +version discriminant). Only sr25519 signatures are accepted. Signature +verification uses the inner `order.signer` as the expected public key. + +### `OrderStatus` + +Terminal state of a processed order, stored under its `OrderId`. + +| Variant | Meaning | +|-------------|---------| +| `Fulfilled` | Order was successfully executed. | +| `Cancelled` | User registered a cancellation intent before execution. | + +--- + +## Storage + +### `Orders: StorageMap` + +Maps an `OrderId` (blake2_256 of the SCALE-encoded `VersionedOrder`) to its +terminal `OrderStatus`. Absence means the order has never been seen and is still +executable (provided it is valid). Presence means it is permanently closed — +neither `Fulfilled` nor `Cancelled` orders can be re-executed. + +--- + +## Config + +| Item | Type | Description | +|-----------------------|---------------------------------------------------|-------------| +| `SwapInterface` | `OrderSwapInterface` | Full swap + balance execution interface. Implemented by `pallet_subtensor::Pallet`. Provides `buy_alpha`, `sell_alpha`, `transfer_tao`, `transfer_staked_alpha`, and `current_alpha_price`. | +| `TimeProvider` | `UnixTime` | Current wall-clock time for expiry checks. | +| `MaxOrdersPerBatch` | `Get` (constant) | Maximum number of orders accepted in a single `execute_orders` or `execute_batched_orders` call. Should equal `floor(max_block_weight / per_order_weight)`. | +| `PalletId` | `Get` (constant) | Used to derive the pallet intermediary account (`PalletId::into_account_truncating`). This account temporarily holds pooled TAO and staked alpha during `execute_batched_orders`. | +| `PalletHotkey` | `Get` (constant) | Hotkey the pallet intermediary account stakes to/from during batch execution. Must be a dedicated hotkey registered on every subnet the pallet may operate on. Operators should register it as a non-validator neuron. | +| `WeightInfo` | `weights::WeightInfo` | Benchmarked weight functions for each extrinsic. Use `weights::SubstrateWeight` in production and `()` in tests. | + +--- + +## Extrinsics + +### `execute_orders(orders)` — call index 0 + +**Origin:** any signed account (typically a relayer). + +Executes a list of signed limit orders one by one, each interacting with the +AMM pool independently. Orders that fail validation or whose price condition is +not met are silently skipped — a single bad order does not revert the batch. + +**Fee handling:** each order's `fee_rate` is deducted from the input amount and +forwarded to that order's `fee_recipient` after execution. + +**When to use:** suitable for small batches or when orders target different +subnets. Use `execute_batched_orders` for same-subnet batches to reduce price +impact. + +--- + +### `execute_batched_orders(netuid, orders)` — call index 1 + +**Origin:** any signed account (typically a relayer). + +Aggregates all valid orders targeting `netuid` into a single net pool +interaction: + +1. **Validate & classify** — if any order has the wrong netuid, an invalid + signature, an already-processed id, a past expiry, a price condition not met, + or targets the root netuid (0), the **entire call fails** with the + corresponding error. All orders must be valid for execution to proceed. Valid + orders are split into buy-side (`LimitBuy`) and sell-side (`TakeProfit`, + `StopLoss`) groups. For buy orders the net TAO (after fee) is pre-computed + here. Each order's `effective_swap_limit` (derived from `limit_price` and + `max_slippage`) is computed and stored for use in the pool swap. + +2. **Collect assets** — gross TAO is pulled from each buyer's free balance into + the pallet intermediary account. Gross alpha stake is moved from each seller's + `(coldkey, hotkey)` position to the pallet intermediary's `(pallet_account, + pallet_hotkey)` position. + +3. **Net pool swap** — buy TAO and sell alpha are converted to a common TAO + basis at the current spot price and offset against each other. Only the + residual amount touches the pool in a single swap: + - Buy-dominant: residual TAO is sent to the pool; pool returns alpha. Price ceiling = `min(effective_swap_limit)` across all buy orders. + - Sell-dominant: residual alpha is sent to the pool; pool returns TAO. Price floor = `max(effective_swap_limit)` across all sell orders. + - Perfectly offset: no pool interaction. + +4. **Distribute alpha pro-rata** — every buyer receives their share of the total + available alpha (pool output + seller passthrough alpha). Share is + proportional to each buyer's net TAO contribution. Integer division floors + each share; any remainder stays in the pallet intermediary account as dust. + +5. **Distribute TAO pro-rata** — every seller receives their share of the total + available TAO (pool output + buyer passthrough TAO), minus their order's + fee. Share is proportional to each seller's alpha valued at the current spot + price. Integer division floors each share; any remainder stays in the pallet + intermediary account as dust. + +6. **Collect fees** — buy-side fees (withheld from each order's TAO input) and + sell-side fees (withheld from each order's TAO output) are accumulated per + unique `fee_recipient` and forwarded in a single transfer per recipient. + +7. **Emit `GroupExecutionSummary`.** + +> **Note:** rounding dust (alpha and TAO) accumulates in the pallet intermediary +> account between batches. If an emission epoch fires while dust is present, the +> pallet earns emissions it never distributes. + +--- + +### `cancel_order(order)` — call index 2 + +**Origin:** the order's `signer` (coldkey). + +Registers a cancellation intent by writing the `OrderId` into `Orders` as +`Cancelled`. Once cancelled an order can never be executed. The full +`VersionedOrder` payload is required so the pallet can derive the `OrderId`. + +--- + +## Events + +| Event | Fields | Emitted when | +|-------|--------|--------------| +| `OrderExecuted` | `order_id`, `signer`, `netuid`, `side` | An individual order was successfully executed (by either extrinsic). | +| `OrderSkipped` | `order_id`, `reason` | An order was skipped by `execute_orders` (bad signature, expired, wrong netuid, already processed, price condition not met, or root netuid). `reason` is the `DispatchError` that caused the skip. Not emitted by `execute_batched_orders` — invalid orders there cause the whole call to fail. | +| `OrderCancelled` | `order_id`, `signer` | The signer registered a cancellation via `cancel_order`. | +| `GroupExecutionSummary` | `netuid`, `net_side`, `net_amount`, `actual_out`, `executed_count` | Emitted once per `execute_batched_orders` call summarising the net pool trade. `net_side` is `Buy` if TAO was sent to the pool, `Sell` if alpha was sent. `net_amount` and `actual_out` are zero when the two sides perfectly offset. | + +--- + +## Errors + +| Error | Cause | +|-------|-------| +| `InvalidSignature` | Signature does not match the order payload and signer. Also used as a catch-all for failed validation in `execute_orders`. | +| `OrderAlreadyProcessed` | The `OrderId` is already present in `Orders` (either `Fulfilled` or `Cancelled`). | +| `OrderExpired` | `now > order.expiry`. Only returned as a hard error by `execute_batched_orders`; silently skipped in `execute_orders`. | +| `PriceConditionNotMet` | Current spot price is beyond the order's `limit_price`. Only returned as a hard error by `execute_batched_orders`; silently skipped in `execute_orders`. | +| `OrderNetUidMismatch` | An order inside a `execute_batched_orders` call targets a different netuid than the batch parameter. | +| `RootNetUidNotAllowed` | The order or batch targets netuid 0 (root). Root uses a fixed 1:1 stable mechanism with no AMM — limit orders are not meaningful there. | +| `Unauthorized` | Caller of `cancel_order` is not the order's `signer`. | +| `SwapReturnedZero` | The pool swap returned zero output for a non-zero residual input. | +| `RelayerMissMatch` | The caller is not the relayer designated in the order's `relayer` field. Only raised when the field is `Some`. | + +--- + +## Fee model + +Fees are specified per-order via `fee_rate: Perbill` and `fee_recipient: +AccountId` fields on the `Order` struct. There is no global protocol fee or +admin key. + +All fees are collected in TAO regardless of order side. + +| Order type | Fee deducted from | Timing | +|-------------------------|-------------------|--------| +| `LimitBuy` | TAO input | Pre-computed in `validate_and_classify`, before pool swap. | +| `TakeProfit`, `StopLoss`| TAO output | Deducted in `distribute_tao_pro_rata`, after pool swap. | + +Fee formula: `fee = fee_rate * amount` (using `Perbill` multiplication, which +upcasts to u128 internally to avoid overflow). + +At the end of each batch, fees are accumulated per unique `fee_recipient` and +forwarded in a single transfer per recipient. If multiple orders share the same +`fee_recipient`, they result in exactly one transfer rather than one per order. + +--- + +## Known limitations + +### `max_slippage` is semantically inverted for `StopLoss` orders + +`StopLoss` sells are triggered when the spot price *falls* to `limit_price`. +`max_slippage` derives a sell floor as `limit_price - limit_price * slippage`, +which is computed from the (higher) trigger threshold. By the time the order +fires, the actual market price will typically be **below** `limit_price`, so +the derived floor will almost always exceed the real fill price, causing the +swap to be rejected. + +**Consequence:** Applying `max_slippage` to a `StopLoss` order will usually +prevent it from executing. In `execute_orders` the order is silently skipped; +in `execute_batched_orders` the entire batch fails. + +**Recommendation:** Relayers should set `max_slippage: None` on `StopLoss` +orders. If slippage protection is desired, apply it at the relayer layer by +choosing a conservative `limit_price` rather than relying on `max_slippage`. diff --git a/pallets/limit-orders/src/benchmarking.rs b/pallets/limit-orders/src/benchmarking.rs new file mode 100644 index 0000000000..79bc60f516 --- /dev/null +++ b/pallets/limit-orders/src/benchmarking.rs @@ -0,0 +1,186 @@ +//! Benchmarks for Limit Orders Pallet +#![allow( + clippy::arithmetic_side_effects, + clippy::indexing_slicing, + clippy::unwrap_used +)] +use crate::{NetUid, OrderType, Orders}; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; +use sp_core::{Get, H256}; +use sp_runtime::{AccountId32, MultiSignature, Perbill, traits::AccountIdConversion}; +extern crate alloc; +use crate::{Call, Config, Pallet}; +use codec::Encode; + +/// Sign a versioned order using the runtime keystore (no `full_crypto` required). +/// +/// The key identified by `public` must already be registered in the keystore +/// (e.g. via `sp_io::crypto::sr25519_generate`) before calling this. +fn sign_order( + public: sp_core::sr25519::Public, + order: &crate::VersionedOrder, +) -> crate::SignedOrder { + let sig = sp_io::crypto::sr25519_sign( + sp_core::crypto::key_types::ACCOUNT, + &public, + &order.encode(), + ) + .unwrap(); + crate::SignedOrder { + order: order.clone(), + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + } +} + +/// Generate a deterministic sr25519 key for benchmark index `i` and return its +/// public key. The key is inserted into the runtime keystore so it can sign. +fn benchmark_key(i: u32) -> (sp_core::sr25519::Public, AccountId32) { + let seed = alloc::format!("//BenchSigner{}", i).into_bytes(); + let public = sp_io::crypto::sr25519_generate(sp_core::crypto::key_types::ACCOUNT, Some(seed)); + let account = AccountId32::from(public); + (public, account) +} + +pub fn order_id(order: &crate::VersionedOrder) -> H256 { + crate::pallet::Pallet::::derive_order_id(order) +} + +/// Build `n` signed benchmark orders for `netuid`, one per distinct signer. +/// +/// For each index `i` in `0..n` the function: +/// - derives a deterministic sr25519 key via `benchmark_key(i)`, +/// - calls `T::SwapInterface::set_up_acc_for_benchmark` so the account has +/// sufficient balance / stake, +/// - constructs a worst-case `LimitBuy` order (amount = 1 TAO, price = u64::MAX, +/// expiry = u64::MAX, fee 1 %, distinct fee recipient), and +/// - signs it with the generated key. +fn make_benchmark_orders( + n: u32, + netuid: NetUid, +) -> alloc::vec::Vec> { + use subtensor_swap_interface::OrderSwapInterface; + + let mut orders = alloc::vec::Vec::new(); + + for i in 0..n { + let (public, account_id) = benchmark_key(i); + let account: T::AccountId = account_id.into(); + let fee_recipient: T::AccountId = frame_benchmarking::account("fee_recipient", i, 0); + + T::SwapInterface::set_up_acc_for_benchmark(&account, &account); + + let order = crate::VersionedOrder::V1(crate::Order { + signer: account.clone(), + hotkey: account.clone(), + netuid, + order_type: OrderType::LimitBuy, + amount: 1_000_000_000u64, + limit_price: u64::MAX, + expiry: u64::MAX, + fee_rate: Perbill::from_percent(1), + fee_recipient, + relayer: None, + max_slippage: None, + chain_id: T::ChainId::get(), + partial_fills_enabled: false, + }); + orders.push(sign_order::(public, &order)); + } + + orders +} + +#[benchmarks] +mod benchmarks { + use super::*; + use frame_support::traits::Get; + use subtensor_swap_interface::OrderSwapInterface; + + #[benchmark] + fn cancel_order() { + let (public, account_id) = benchmark_key(0); + let account: T::AccountId = account_id.into(); + + let order = crate::VersionedOrder::V1(crate::Order { + signer: account.clone(), + hotkey: account.clone(), + netuid: NetUid::from(1u16), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: 2_000_000_000, + expiry: 1_000_000_000, + fee_rate: Perbill::zero(), + fee_recipient: account.clone(), + relayer: None, + max_slippage: None, + chain_id: T::ChainId::get(), + partial_fills_enabled: false, + }); + let signed = sign_order::(public, &order); + + #[extrinsic_call] + _(RawOrigin::Signed(account.clone()), signed.order.clone()); + + let id = order_id::(&signed.order); + assert_eq!(Orders::::get(id), Some(crate::OrderStatus::Cancelled)); + } + + #[benchmark] + fn set_pallet_status() { + #[extrinsic_call] + _(RawOrigin::Root, false); + + assert_eq!(crate::LimitOrdersEnabled::::get(), false); + } + + /// Worst case: `n` orders each with a distinct signer (coldkey/hotkey) and a + /// distinct fee recipient, maximising per-order storage reads and fee transfers. + #[benchmark] + fn execute_orders(n: Linear<1, { T::MaxOrdersPerBatch::get() }>) { + let netuid = NetUid::from(1u16); + crate::LimitOrdersEnabled::::set(true); + T::SwapInterface::set_up_netuid_for_benchmark(netuid); + + let orders = make_benchmark_orders::(n, netuid); + + let bounded_orders: frame_support::BoundedVec<_, T::MaxOrdersPerBatch> = + frame_support::BoundedVec::try_from(orders).unwrap(); + let caller: T::AccountId = frame_benchmarking::account("caller", 0, 0); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), bounded_orders, false); + } + + /// Worst case: `n` buy orders each with a distinct signer and fee recipient, + /// maximising asset-collection reads, pro-rata distribution writes, and the + /// number of unique fee-transfer recipients in `collect_fees`. + #[benchmark] + fn execute_batched_orders(n: Linear<1, { T::MaxOrdersPerBatch::get() }>) { + let netuid = NetUid::from(1u16); + crate::LimitOrdersEnabled::::set(true); + T::SwapInterface::set_up_netuid_for_benchmark(netuid); + + // Set up the pallet intermediary so the net pool swap and alpha + // distribution transfers succeed. + let pallet_acct: T::AccountId = T::PalletId::get().into_account_truncating(); + let pallet_hotkey: T::AccountId = T::PalletHotkey::get(); + T::SwapInterface::set_up_acc_for_benchmark(&pallet_hotkey, &pallet_acct); + + let orders = make_benchmark_orders::(n, netuid); + + let bounded_orders: frame_support::BoundedVec<_, T::MaxOrdersPerBatch> = + frame_support::BoundedVec::try_from(orders).unwrap(); + let caller: T::AccountId = frame_benchmarking::account("caller", 0, 0); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), netuid, bounded_orders); + } + + impl_benchmark_test_suite!( + Pallet, + crate::tests::mock::new_test_ext(), + crate::tests::mock::Test + ); +} diff --git a/pallets/limit-orders/src/lib.rs b/pallets/limit-orders/src/lib.rs new file mode 100644 index 0000000000..1d5548c529 --- /dev/null +++ b/pallets/limit-orders/src/lib.rs @@ -0,0 +1,1300 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +pub use pallet::*; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub(crate) mod migrations; +#[cfg(test)] +mod tests; +pub mod weights; + +type MigrationKeyMaxLen = frame_support::traits::ConstU32<128>; + +use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; +use frame_support::{BoundedVec, traits::ConstU32}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_runtime::{ + AccountId32, MultiSignature, Perbill, + traits::{ConstBool, Verify}, +}; +use substrate_fixed::types::U64F64; +use subtensor_macros::freeze_struct; +use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; +use subtensor_swap_interface::OrderSwapInterface; + +// ── Data structures ────────────────────────────────────────────────────────── + +/// Internal direction of a net pool trade. Used only for `GroupExecutionSummary` +/// and pool-swap bookkeeping; not part of the public order payload. +#[derive( + Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Clone, PartialEq, Eq, Debug, +)] +pub enum OrderSide { + Buy, + Sell, +} + +/// The user-facing order type. Each variant encodes both the execution action +/// (buy alpha / sell alpha) and the price-trigger direction. +/// +/// | Variant | Action | Triggers when | +/// |--------------|--------|---------------------| +/// | `LimitBuy` | Buy | price ≤ limit_price | +/// | `TakeProfit` | Sell | price ≥ limit_price | +/// | `StopLoss` | Sell | price ≤ limit_price | +#[derive( + Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Clone, PartialEq, Eq, Debug, +)] +pub enum OrderType { + LimitBuy, + TakeProfit, + StopLoss, +} + +impl OrderType { + /// `true` if this order results in buying alpha (staking into subnet). + pub fn is_buy(&self) -> bool { + matches!(self, OrderType::LimitBuy) + } +} + +/// The canonical order payload that users sign off-chain. +/// Only its H256 hash is stored on-chain; the full struct is submitted by the +/// admin at execution time (or by the user at cancellation time). +#[allow(clippy::multiple_bound_locations)] // bounds on AccountId required by FRAME derives +#[freeze_struct("27c7eedb92261456")] +#[derive( + Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Clone, PartialEq, Eq, Debug, +)] +pub struct Order { + /// The coldkey that authorised this order (pays TAO for buys; owns the + /// staked alpha for sells). + pub signer: AccountId, + /// The hotkey to stake to (buy) or unstake from (sell). + pub hotkey: AccountId, + /// Target subnet. + pub netuid: NetUid, + /// Order type (LimitBuy, TakeProfit, or StopLoss). + pub order_type: OrderType, + /// Input amount: TAO (raw) for Buy, alpha (raw) for Sell. + pub amount: u64, + /// Price threshold in ×10⁹ scale (same as the `current_alpha_price` RPC endpoint). + /// A value of `1_000_000_000` represents a price of 1.0 TAO/alpha. + /// Sub-unity prices (e.g. 0.5 TAO/alpha) are expressed as `500_000_000`. + /// Buy: maximum acceptable price. Sell: minimum acceptable price. + /// `u64::MAX` means no ceiling (buy at any price); `0` means no floor (sell at any price). + pub limit_price: u64, + /// Unix timestamp in milliseconds after which this order must not be executed. + pub expiry: u64, + /// Fee rate applied to this order's TAO amount (input for buys, output for sells). + pub fee_rate: Perbill, + /// Account that receives the fee collected from this order. + pub fee_recipient: AccountId, + /// Accounts authorized to relay this order. When set, only an account present + /// in this list may submit the execution transaction. Supports up to 10 relayers. + pub relayer: Option>>, + /// Maximum slippage tolerance in parts per billion applied to `limit_price` + /// at execution time. `None` = no protection (execute at market). + /// - Buy: effective price ceiling = `limit_price + limit_price * max_slippage` + /// - Sell: effective price floor = `limit_price - limit_price * max_slippage` + pub max_slippage: Option, + /// EVM-compatible chain ID that this order is bound to. + /// Prevents replay of testnet-signed orders on mainnet and vice versa. + pub chain_id: u64, + /// Wether partial fills are enabled + pub partial_fills_enabled: bool, +} + +/// Versioned wrapper around an order payload. +/// +/// Adding a new variant in the future (e.g. `V2`) lets the pallet accept orders +/// signed against either schema simultaneously, preventing old signed orders from +/// being invalidated by a schema upgrade. +#[derive( + Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Clone, PartialEq, Eq, Debug, +)] +pub enum VersionedOrder { + V1(Order), +} + +impl VersionedOrder { + /// Returns a reference to the inner order regardless of version. + pub fn inner(&self) -> &Order { + match self { + VersionedOrder::V1(order) => order, + } + } +} + +/// The envelope the admin submits on-chain: the versioned order payload plus +/// the user's signature over the SCALE-encoded `VersionedOrder`. +/// +/// Signature verification is performed against `order.inner().signer` (the AccountId) +/// directly. Only sr25519 signatures are accepted; ed25519 and ecdsa variants +/// of `MultiSignature` are rejected at validation time. +#[freeze_struct("9dd5a8ac812dc504")] +#[derive( + Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Clone, PartialEq, Eq, Debug, +)] +pub struct SignedOrder { + pub order: VersionedOrder, + /// Sr25519 signature over `SCALE_ENCODE(VersionedOrder)`. + pub signature: MultiSignature, + /// Whether we want a partial fill for this order + pub partial_fill: Option, +} + +#[derive( + Encode, Decode, DecodeWithMemTracking, TypeInfo, MaxEncodedLen, Clone, PartialEq, Eq, Debug, +)] +pub enum OrderStatus { + /// The order was successfully executed. + Fulfilled, + /// The order was partially filled, with the amount already fulfilled in the enum + PartiallyFilled(u64), + /// The user registered a cancellation intent before execution. + Cancelled, +} + +/// Classified, fee-adjusted entry produced by `validate_and_classify`. +/// Used in every in-memory batch pipeline step; never stored on-chain. +#[derive(Debug, PartialEq)] +pub(crate) struct OrderEntry { + pub(crate) order_id: H256, + pub(crate) signer: AccountId, + pub(crate) hotkey: AccountId, + pub(crate) side: OrderType, + /// Actual input amount being processed this execution (partial or full, before fee). + pub(crate) gross: u64, + /// Full order amount as signed by the user. Used to determine terminal status. + pub(crate) order_amount: u64, + /// Net input amount (after fee). + /// For buys: `gross - fee_rate * gross`. For sells: equals `gross` (fee on TAO output). + pub(crate) net: u64, + /// Per-order fee rate. + pub(crate) fee_rate: Perbill, + /// Per-order fee recipient. + pub(crate) fee_recipient: AccountId, + /// Effective price limit passed to the pool swap. + /// For buys: ceiling (max TAO per alpha the pool may charge). + /// For sells: floor (min TAO per alpha the pool must return). + /// Derived from `limit_price` and `max_slippage` during classification. + pub(crate) effective_swap_limit: u64, + /// Present when this execution covers only part of the order. + pub(crate) partial_fill: Option, +} + +// ── Pallet ─────────────────────────────────────────────────────────────────── + +#[frame_support::pallet] +#[allow(clippy::expect_used)] +pub mod pallet { + use super::*; + use crate::weights::WeightInfo as _; + use frame_support::{ + PalletId, + pallet_prelude::*, + traits::{Get, UnixTime}, + transactional, + }; + use frame_system::pallet_prelude::*; + use sp_runtime::traits::AccountIdConversion; + use sp_std::collections::btree_set::BTreeSet; + use sp_std::vec::Vec; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// Full swap + balance execution interface (see [`OrderSwapInterface`]). + type SwapInterface: OrderSwapInterface; + + /// Time provider for expiry checks. + type TimeProvider: UnixTime; + + /// Maximum number of orders in a single `execute_orders` call. + /// Should equal `floor(max_block_weight / per_order_weight)`. + #[pallet::constant] + type MaxOrdersPerBatch: Get; + + /// PalletId used to derive the intermediary account for batch execution. + /// + /// The derived account temporarily holds pooled TAO and staked alpha + /// during `execute_batched_orders` before distributing to order signers. + #[pallet::constant] + type PalletId: Get; + + /// Hotkey registered in each subnet that the pallet's intermediary + /// account stakes to/from during batch execution. + /// + /// This must be a hotkey registered on every subnet the pallet may + /// operate on. Operators should register a dedicated hotkey and set + /// this in the runtime configuration. + #[pallet::constant] + type PalletHotkey: Get; + + /// Weight information for the pallet's extrinsics. + type WeightInfo: crate::weights::WeightInfo; + + /// EVM-compatible chain ID used to bind orders to a specific chain. + /// Wire to `pallet_evm_chain_id` in the runtime via `ConfigurableChainId`. + type ChainId: Get; + } + + // ── Storage ─────────────────────────────────────────────────────────────── + + /// Tracks the on-chain status of a known `OrderId`. + /// Absent ⇒ never seen (still executable if valid). + /// Present ⇒ Fulfilled or Cancelled (both are terminal). + #[pallet::storage] + pub type Orders = StorageMap<_, Blake2_128Concat, H256, OrderStatus, OptionQuery>; + + /// Switch to enable/disable the pallet. + /// Defaults to `false` so bare node deployments are safe; genesis sets it to `true`. + #[pallet::storage] + pub type LimitOrdersEnabled = StorageValue<_, bool, ValueQuery, ConstBool>; + + /// Tracks which named migrations have already been applied. + /// Keyed by a short migration name; value is always `true`. + #[pallet::storage] + pub type HasMigrationRun = + StorageMap<_, Identity, BoundedVec, bool, ValueQuery>; + + // ── Events ──────────────────────────────────────────────────────────────── + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A limit order was successfully executed. + OrderExecuted { + order_id: H256, + signer: T::AccountId, + netuid: NetUid, + order_type: OrderType, + /// Input amount: TAO (raw) for Buy orders, alpha (raw) for Sell orders. + amount_in: u64, + /// Output amount: alpha (raw) received for Buy orders, TAO (raw) received for Sell orders (after fee). + amount_out: u64, + }, + /// An order was skipped during execution. + OrderSkipped { + order_id: H256, + reason: sp_runtime::DispatchError, + }, + /// A user registered a cancellation intent for their order. + OrderCancelled { + order_id: H256, + signer: T::AccountId, + }, + /// Summary emitted once per `execute_batched_orders` call. + GroupExecutionSummary { + /// The subnet all orders in this batch belong to. + netuid: NetUid, + /// Direction of the net pool trade (Buy = net TAO into pool). + net_side: OrderSide, + /// Net amount sent to the pool (TAO for Buy, alpha for Sell). + /// Zero when buys and sells perfectly offset each other. + net_amount: u64, + /// Tokens received back from the pool. + /// Zero when `net_amount` is zero. + actual_out: u64, + /// Number of orders that were successfully executed. + executed_count: u32, + }, + /// Root has either enabled(true) or disabled(false) the pallet + LimitOrdersPalletStatusChanged { enabled: bool }, + } + + // ── Errors ──────────────────────────────────────────────────────────────── + + #[pallet::error] + pub enum Error { + /// The provided signature does not match the order payload and signer. + InvalidSignature, + /// The order has already been Fulfilled or Cancelled. + OrderAlreadyProcessed, + /// Order has been cancelled + OrderCancelled, + /// The order's expiry timestamp is in the past. + OrderExpired, + /// The current market price does not satisfy the order's limit price. + PriceConditionNotMet, + /// Caller is not the order signer (required for cancellation). + Unauthorized, + /// The pool swap returned zero output for a non-zero input. + SwapReturnedZero, + /// Root netuid (0) is not allowed for limit orders. + RootNetUidNotAllowed, + /// An order in the batch targets a different netuid than the batch netuid parameter. + OrderNetUidMismatch, + /// Limit orders are disabled + LimitOrdersDisabled, + /// Relayer not the same as specified in the order + RelayerMissMatch, + /// Partial fills not enabled for this order + PartialFillsNotEnabled, + /// Incorrect partial fill amount provided + IncorrectPartialFillAmount, + /// A relayer must be set on the order when using partial fills + RelayerRequiredForPartialFill, + /// The order's chain_id does not match the current chain. + ChainIdMismatch, + /// The pallet hotkey has not been registered to the pallet account. + /// Call on_runtime_upgrade or wait for genesis to complete registration + /// before enabling the pallet. + PalletHotkeyNotRegistered, + /// A TAO -> alpha conversion overflowed the fixed-point range. + ArithmeticOverflow, + /// The same order appears more than once in a single batch. + DuplicateOrderInBatch, + /// An order's pro-rata share in the batch rounded down to zero. The whole + /// batch is rejected so the order's input is never consumed without + /// delivering any output (conservation), and the order stays retryable in a + /// differently-composed batch. + ZeroShareInBatch, + } + + // ── Hooks ───────────────────────────────────────────────────────────────── + + // ── Genesis ─────────────────────────────────────────────────────────────── + + #[pallet::genesis_config] + #[derive(frame_support::DefaultNoBound)] + pub struct GenesisConfig { + #[serde(skip)] + pub _phantom: core::marker::PhantomData, + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + let _ = T::SwapInterface::register_pallet_hotkey( + &Pallet::::pallet_account(), + &T::PalletHotkey::get(), + ); + // Enable the pallet on all networks that start from this genesis. + // The storage default is `false` (safe for bare upgrades); genesis + // explicitly opts new chains in. + LimitOrdersEnabled::::set(true); + } + } + + // ── Hooks ───────────────────────────────────────────────────────────────── + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let mut weight = frame_support::weights::Weight::from_parts(0, 0); + + weight = weight.saturating_add(migrations::migrate_register_pallet_hotkey::()); + + weight + } + } + + // ── Extrinsics ──────────────────────────────────────────────────────────── + + #[pallet::call] + impl Pallet { + /// Execute a batch of signed limit orders. Admin-gated. + /// + /// The `should_fail` flag controls how individual order failures are + /// handled: + /// + /// - When `false` (best-effort): orders whose price condition is not yet + /// met are silently skipped so that a single stale order cannot block + /// the rest of the batch. Orders that fail for any other reason + /// (expired, bad signature, etc.) are also skipped; the admin is + /// expected to filter these off-chain. + /// - When `true` (all-or-nothing): the first order failure aborts the + /// whole batch by returning the underlying error, reverting any orders + /// already executed in this call. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::execute_orders(orders.len() as u32))] + pub fn execute_orders( + origin: OriginFor, + orders: BoundedVec, T::MaxOrdersPerBatch>, + should_fail: bool, + ) -> DispatchResult { + let relayer = ensure_signed(origin)?; + ensure!( + LimitOrdersEnabled::::get(), + Error::::LimitOrdersDisabled + ); + + for signed_order in orders { + let order_id = Self::derive_order_id(&signed_order.order); + if let Err(reason) = Self::try_execute_order(signed_order, order_id, &relayer) { + if should_fail { + // All-or-nothing: abort the batch, reverting prior orders. + return Err(reason); + } + // Best-effort: individual order failures do not revert the batch. + Self::deposit_event(Event::OrderSkipped { order_id, reason }); + } + } + + Ok(()) + } + + /// Execute a batch of signed limit orders for a single subnet using + /// aggregated (netted) pool interaction. + /// + /// Unlike `execute_orders`, which hits the pool once per order, this + /// extrinsic: + /// + /// 1. Validates all orders (bad signature / expired / already processed / + /// price-not-met orders are skipped and emit `OrderSkipped`). + /// 2. Fetches the current price once. + /// 3. Aggregates all valid buy inputs (TAO) and sell inputs (alpha). + /// 4. Nets the two sides: only the residual amount touches the pool in + /// a single swap, minimising price impact. + /// 5. Distributes outputs pro-rata: + /// - Dominant-side orders split the pool output proportionally to + /// their individual net amounts. + /// - Offset-side orders are filled internally at the current price + /// (no pool interaction for them). + /// 6. Collects protocol fees (TAO for buy orders, alpha → TAO for sell + /// orders) and routes them to `FeeCollector`. + /// + /// All orders in the batch must target `netuid`. Orders for a different + /// subnet are skipped. + #[pallet::call_index(1)] + #[pallet::weight(T::WeightInfo::execute_batched_orders(orders.len() as u32))] + pub fn execute_batched_orders( + origin: OriginFor, + netuid: NetUid, + orders: BoundedVec, T::MaxOrdersPerBatch>, + ) -> DispatchResult { + let relayer = ensure_signed(origin)?; + ensure!( + LimitOrdersEnabled::::get(), + Error::::LimitOrdersDisabled + ); + + Self::do_execute_batched_orders(netuid, orders, relayer) + } + + /// Register a cancellation intent for an order. + /// + /// Must be called by the order's signer. The full `Order` payload is + /// provided so the pallet can derive the `OrderId`. Once marked + /// Cancelled, the order can never be executed. + #[pallet::call_index(2)] + #[pallet::weight(T::WeightInfo::cancel_order())] + pub fn cancel_order( + origin: OriginFor, + order: VersionedOrder, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + ensure!(order.inner().signer == who, Error::::Unauthorized); + + let order_id = Self::derive_order_id(&order); + + ensure!( + Orders::::get(order_id).is_none(), + Error::::OrderAlreadyProcessed + ); + + Orders::::insert(order_id, OrderStatus::Cancelled); + Self::deposit_event(Event::OrderCancelled { + order_id, + signer: who, + }); + + Ok(()) + } + + /// Set a status for the limit orders pallet + /// + /// Must be called by root + /// It allows disabling or enabling the pallet + /// true means enabling, false means disabling + #[pallet::call_index(3)] + #[pallet::weight(T::WeightInfo::set_pallet_status())] + pub fn set_pallet_status(origin: OriginFor, enabled: bool) -> DispatchResult { + ensure_root(origin)?; + + if enabled { + ensure!( + T::SwapInterface::pallet_hotkey_registered( + &Self::pallet_account(), + &T::PalletHotkey::get(), + ), + Error::::PalletHotkeyNotRegistered + ); + } + + LimitOrdersEnabled::::set(enabled); + + Self::deposit_event(Event::LimitOrdersPalletStatusChanged { enabled }); + + Ok(()) + } + } + + // ── Internal helpers ────────────────────────────────────────────────────── + + impl Pallet { + /// Compute the effective price limit passed to the pool swap. + /// + /// - `None` slippage → no constraint: `u64::MAX` for buys (no ceiling), + /// `0` for sells (no floor). + /// - `Some(p)` → widens `limit_price` by the slippage fraction: + /// - Buy: ceiling = `limit_price + limit_price * p` (saturating) + /// - Sell: floor = `limit_price - limit_price * p` (saturating) + pub(crate) fn compute_effective_swap_limit( + is_buy: bool, + limit_price: u64, + max_slippage: Option, + ) -> u64 { + match max_slippage { + None => { + if is_buy { + u64::MAX + } else { + 0 + } + } + Some(slippage) => { + let delta = slippage.mul_floor(limit_price); + if is_buy { + limit_price.saturating_add(delta) + } else { + limit_price.saturating_sub(delta) + } + } + } + } + + /// Derive the on-chain `OrderId` as blake2_256 over the SCALE-encoded order. + pub fn derive_order_id(order: &VersionedOrder) -> H256 { + H256(sp_core::hashing::blake2_256(&order.encode())) + } + + /// Account derived from the pallet's `PalletId`. + pub(crate) fn pallet_account() -> T::AccountId { + T::PalletId::get().into_account_truncating() + } + + /// Transfer `fee_tao` from `signer` to `recipient`. + /// Returns an error if the transfer fails, causing the surrounding operation to revert. + /// Does nothing when `fee_tao` is zero. + fn forward_fee( + signer: &T::AccountId, + recipient: &T::AccountId, + fee_tao: TaoBalance, + ) -> DispatchResult { + if fee_tao.is_zero() { + return Ok(()); + } + T::SwapInterface::transfer_tao(signer, recipient, fee_tao) + } + + /// Validates all execution preconditions for a signed order. + /// Checks that the order's netuid is not root (0), that the signature is valid, + /// the order has not been processed, is not expired, and the price condition is met. + /// The batch netuid match (order.netuid == batch netuid) is checked separately by callers. + pub(crate) fn is_order_valid( + signed_order: &SignedOrder, + order_id: H256, + now_ms: u64, + current_price: U64F64, + relayer: &T::AccountId, + ) -> DispatchResult { + let order = signed_order.order.inner(); + ensure!(!order.netuid.is_root(), Error::::RootNetUidNotAllowed); + ensure!( + order.chain_id == T::ChainId::get(), + Error::::ChainIdMismatch + ); + ensure!( + matches!(signed_order.signature, MultiSignature::Sr25519(_)) + && signed_order + .signature + .verify(signed_order.order.encode().as_slice(), &order.signer), + Error::::InvalidSignature + ); + let order_status = Orders::::get(order_id); + ensure!( + order_status != Some(OrderStatus::Fulfilled), + Error::::OrderAlreadyProcessed + ); + ensure!( + order_status != Some(OrderStatus::Cancelled), + Error::::OrderCancelled + ); + ensure!(now_ms <= order.expiry, Error::::OrderExpired); + // Scale current_price to ×10⁹ to match the limit_price field, which is + // expressed in the same ×10⁹ scale as the `current_alpha_price` RPC endpoint. + // This allows sub-unity prices (e.g. 0.5 TAO/alpha = 500_000_000) to be + // represented and compared correctly. + let scaled_price = current_price + .saturating_mul(U64F64::from_num(1_000_000_000u64)) + .saturating_to_num::(); + ensure!( + match order.order_type { + OrderType::TakeProfit => scaled_price >= order.limit_price, + OrderType::StopLoss | OrderType::LimitBuy => scaled_price <= order.limit_price, + }, + Error::::PriceConditionNotMet + ); + if let Some(forced_relayers) = order.relayer.as_ref() { + ensure!( + forced_relayers.contains(relayer), + Error::::RelayerMissMatch + ); + } + if let Some(partial_fill) = signed_order.partial_fill { + ensure!( + order.relayer.is_some(), + Error::::RelayerRequiredForPartialFill + ); + ensure!( + order.partial_fills_enabled, + Error::::PartialFillsNotEnabled + ); + let max_fill = + if let Some(OrderStatus::PartiallyFilled(already_filled)) = order_status { + order.amount.saturating_sub(already_filled) + } else { + order.amount + }; + ensure!( + partial_fill > 0 && partial_fill <= max_fill, + Error::::IncorrectPartialFillAmount + ); + } else { + // `partial_fill == None` means a one-shot full execution of `order.amount` + // (see `compute_order_status`, which returns `Fulfilled` for `None`). This is + // only valid for an order that has not been filled yet. Against an order that is + // already `PartiallyFilled(n)` the cumulative-fill cap above is skipped, so the + // execution path would re-run the full `order.amount` on top of the `n` already + // filled — over-debiting the signer and breaking the `sum(fills) <= order.amount` + // conservation invariant. Reject it: the remaining amount must be completed with an + // explicit `partial_fill = Some(order.amount - n)`. + ensure!( + !matches!(order_status, Some(OrderStatus::PartiallyFilled(_))), + Error::::IncorrectPartialFillAmount + ); + } + Ok(()) + } + + /// Compute the new `OrderStatus` to write after filling `fill_amount` of an order. + /// + /// Reads the current on-chain status to find any already-filled amount, adds + /// `fill_amount`, and returns `Fulfilled` when the total reaches `order_amount`. + /// Pass `None` for `fill_amount` when the order is being fully executed in one shot. + pub(crate) fn compute_order_status( + order_id: H256, + fill_amount: Option, + order_amount: u64, + ) -> OrderStatus { + let Some(fill) = fill_amount else { + return OrderStatus::Fulfilled; + }; + let already_filled = + if let Some(OrderStatus::PartiallyFilled(n)) = Orders::::get(order_id) { + n + } else { + 0 + }; + let new_total = already_filled.saturating_add(fill); + if new_total >= order_amount { + OrderStatus::Fulfilled + } else { + OrderStatus::PartiallyFilled(new_total) + } + } + + /// Attempt to execute one signed order. Returns an error on any + /// validation or execution failure without panicking. + /// + /// `#[transactional]` makes the whole body a single storage layer: the + /// swap (`buy_alpha`/`sell_alpha`, themselves transactional), the fee + /// transfer, and the `Orders::insert` either all commit together or all + /// roll back together. + #[transactional] + fn try_execute_order( + signed_order: SignedOrder, + order_id: H256, + relayer: &T::AccountId, + ) -> DispatchResult { + let order = signed_order.order.inner(); + let now_ms = T::TimeProvider::now().as_millis() as u64; + let current_price = T::SwapInterface::current_alpha_price(order.netuid); + + Self::is_order_valid(&signed_order, order_id, now_ms, current_price, relayer)?; + + let effective_swap_limit = Self::compute_effective_swap_limit( + order.order_type.is_buy(), + order.limit_price, + order.max_slippage, + ); + + // Execute the swap, taking the order's fee from the input (buys) or output (sells). + // `effective_swap_limit` enforces slippage protection: for buys it caps the price + // ceiling; for sells it sets a minimum floor. When `max_slippage` is None the + // limit is u64::MAX (buys) or 0 (sells), matching previous market-order behaviour. + let (amount_in, amount_out) = if order.order_type.is_buy() { + // partial fill validations have passed, it is safe here to do this + let tao_in = TaoBalance::from(signed_order.partial_fill.unwrap_or(order.amount)); + // Deduct fee from TAO input before swapping. + let fee_tao = TaoBalance::from(order.fee_rate.mul_floor(tao_in.to_u64())); + let tao_after_fee = tao_in.saturating_sub(fee_tao); + + let alpha_out = T::SwapInterface::buy_alpha( + &order.signer, + &order.hotkey, + order.netuid, + tao_after_fee, + TaoBalance::from(effective_swap_limit), + true, + )?; + + // Forward the fee TAO to the order's fee recipient. + Self::forward_fee(&order.signer, &order.fee_recipient, fee_tao)?; + (tao_after_fee.to_u64(), alpha_out.to_u64()) + } else { + // partial fill validations have passed, it is safe here to do this + let alpha_in = + AlphaBalance::from(signed_order.partial_fill.unwrap_or(order.amount)); + + // Sell the full alpha amount; fee is taken from the TAO output. + let tao_out = T::SwapInterface::sell_alpha( + &order.signer, + &order.hotkey, + order.netuid, + alpha_in, + TaoBalance::from(effective_swap_limit), + true, + )?; + + // Deduct fee from TAO output and forward to the order's fee recipient. + let fee_tao = TaoBalance::from(order.fee_rate.mul_floor(tao_out.to_u64())); + Self::forward_fee(&order.signer, &order.fee_recipient, fee_tao)?; + (alpha_in.to_u64(), tao_out.saturating_sub(fee_tao).to_u64()) + }; + + // Mark as fulfilled or partially filled and emit event. + let status = + Self::compute_order_status(order_id, signed_order.partial_fill, order.amount); + Orders::::insert(order_id, status); + Self::deposit_event(Event::OrderExecuted { + order_id, + signer: order.signer.clone(), + netuid: order.netuid, + order_type: order.order_type.clone(), + amount_in, + amount_out, + }); + + Ok(()) + } + + /// Thin orchestrator for `execute_batched_orders`. + /// + /// All-or-nothing: any `Err` returned here (e.g. a `ZeroShareInBatch` rejection + /// during distribution) rolls back the whole batch — including the up-front + /// `collect_assets` debits and the pool swap — via FRAME's default per-dispatch + /// storage layer, so no signer is left debited without receiving output. + fn do_execute_batched_orders( + netuid: NetUid, + orders: BoundedVec, T::MaxOrdersPerBatch>, + relayer: T::AccountId, + ) -> DispatchResult { + ensure!(!netuid.is_root(), Error::::RootNetUidNotAllowed); + + let now_ms = T::TimeProvider::now().as_millis() as u64; + let current_price = T::SwapInterface::current_alpha_price(netuid); + + // Validate all orders; any invalid order causes the entire batch to fail. + let (valid_buys, valid_sells) = + Self::validate_and_classify(netuid, &orders, now_ms, current_price, relayer)?; + + let executed_count = valid_buys.len().saturating_add(valid_sells.len()) as u32; + if executed_count == 0 { + return Ok(()); + } + + let total_buy_net: u128 = valid_buys.iter().map(|e| e.net as u128).sum(); + let total_sell_net: u128 = valid_sells.iter().map(|e| e.net as u128).sum(); + let total_sell_tao_equiv: u128 = Self::alpha_to_tao(total_sell_net, current_price); + + let pallet_acct = Self::pallet_account(); + let pallet_hotkey = T::PalletHotkey::get(); + + // Pull all input assets into the pallet intermediary before touching the pool. + Self::collect_assets( + &valid_buys, + &valid_sells, + &pallet_acct, + &pallet_hotkey, + netuid, + )?; + + // Derive the tightest slippage constraint from the dominant side: + // buy-dominant → min of all buy ceilings; sell-dominant → max of all sell floors. + let pool_price_limit = if total_buy_net >= total_sell_tao_equiv { + valid_buys + .iter() + .map(|e| e.effective_swap_limit) + .min() + .unwrap_or(u64::MAX) + } else { + valid_sells + .iter() + .map(|e| e.effective_swap_limit) + .max() + .unwrap_or(0) + }; + + // Execute a single pool swap for the residual (buy TAO minus sell TAO-equiv, or vice versa). + let (net_side, actual_out) = Self::net_pool_swap( + total_buy_net, + total_sell_net, + total_sell_tao_equiv, + current_price, + &pallet_acct, + &pallet_hotkey, + netuid, + pool_price_limit, + )?; + + // Give every buyer their pro-rata share of (pool alpha output + offset sell alpha). + Self::distribute_alpha_pro_rata( + &valid_buys, + actual_out, + total_buy_net, + total_sell_net, + &net_side, + current_price, + &pallet_acct, + &pallet_hotkey, + netuid, + )?; + + // Give every seller their pro-rata share of (pool TAO output + offset buy TAO), + // deducting per-order fees from each payout; returns accumulated sell fees by recipient. + let sell_fees = Self::distribute_tao_pro_rata( + &valid_sells, + actual_out, + total_buy_net, + total_sell_tao_equiv, + &net_side, + current_price, + &pallet_acct, + netuid, + )?; + + // Merge buy and sell fees by recipient and transfer once per unique recipient. + Self::collect_fees(&valid_buys, sell_fees, &pallet_acct)?; + + let net_amount = Self::net_amount_for_event( + &net_side, + total_buy_net, + total_sell_net, + total_sell_tao_equiv, + current_price, + )?; + Self::deposit_event(Event::GroupExecutionSummary { + netuid, + net_side, + net_amount, + actual_out: actual_out as u64, + executed_count, + }); + + Ok(()) + } + + /// Validate every order against `netuid`, signature, expiry, and price. + /// Valid orders are split into two BoundedVecs by side. + /// Each entry is `(order_id, signer, hotkey, gross, net, fee)`. + /// + /// Returns an error immediately if any order fails validation (wrong netuid, + /// invalid signature, expired, already processed, or price condition not met). + pub(crate) fn validate_and_classify( + netuid: NetUid, + orders: &BoundedVec, T::MaxOrdersPerBatch>, + now_ms: u64, + current_price: U64F64, + relayer: T::AccountId, + ) -> Result< + ( + BoundedVec, T::MaxOrdersPerBatch>, + BoundedVec, T::MaxOrdersPerBatch>, + ), + DispatchError, + > { + let mut buys = BoundedVec::new(); + let mut sells = BoundedVec::new(); + + // Track which order_ids we have already seen in this batch. A repeated + // order_id is never legitimate within a single batch. + let mut seen_order_ids: BTreeSet = BTreeSet::new(); + + for signed_order in orders.iter() { + let order_id = Self::derive_order_id(&signed_order.order); + + // Hard-fail on the first duplicate order_id in the batch (covers both + // buys and sells). BTreeSet::insert returns false if already present. + ensure!( + seen_order_ids.insert(order_id), + Error::::DuplicateOrderInBatch + ); + + let order = signed_order.order.inner(); + + // Hard-fail if the order targets a different subnet than the batch netuid. + ensure!(order.netuid == netuid, Error::::OrderNetUidMismatch); + + // Hard-fail on any per-order validation error (signature, expiry, price, root). + Self::is_order_valid(signed_order, order_id, now_ms, current_price, &relayer)?; + + let amount_in = signed_order.partial_fill.unwrap_or(order.amount); + let net = if order.order_type.is_buy() { + // Buy: fee on TAO input — net is the amount that reaches the pool. + amount_in.saturating_sub(order.fee_rate.mul_floor(amount_in)) + } else { + // Sell: fee on TAO output — full alpha enters the pool; the fee is + // deducted from the TAO payout later in `distribute_tao_pro_rata`. + amount_in + }; + + let effective_swap_limit = Self::compute_effective_swap_limit( + order.order_type.is_buy(), + order.limit_price, + order.max_slippage, + ); + + let entry = OrderEntry { + order_id, + signer: order.signer.clone(), + hotkey: order.hotkey.clone(), + side: order.order_type.clone(), + gross: amount_in, + order_amount: order.amount, + net, + fee_rate: order.fee_rate, + fee_recipient: order.fee_recipient.clone(), + effective_swap_limit, + partial_fill: signed_order.partial_fill, + }; + + // try_push cannot fail: both vecs share the same bound as `orders`. + if entry.side.is_buy() { + let _ = buys.try_push(entry); + } else { + let _ = sells.try_push(entry); + } + } + + Ok((buys, sells)) + } + + /// Pull gross TAO from each buyer and gross staked alpha from each seller + /// into the pallet intermediary account, bypassing the pool. + fn collect_assets( + buys: &BoundedVec, T::MaxOrdersPerBatch>, + sells: &BoundedVec, T::MaxOrdersPerBatch>, + pallet_acct: &T::AccountId, + pallet_hotkey: &T::AccountId, + netuid: NetUid, + ) -> DispatchResult { + for e in buys.iter() { + T::SwapInterface::transfer_tao(&e.signer, pallet_acct, TaoBalance::from(e.gross))?; + } + for e in sells.iter() { + T::SwapInterface::transfer_staked_alpha( + &e.signer, + &e.hotkey, + pallet_acct, + pallet_hotkey, + netuid, + AlphaBalance::from(e.gross), + true, // validate_sender: check user's rate limit, subnet, min stake + false, // set_receiver_limit: do not rate-limit the pallet intermediary + )?; + } + Ok(()) + } + + /// Execute a single pool swap for the net (residual) amount. + /// Returns `(net_side, actual_out)` where `actual_out` is in the output + /// token units (alpha for Buy, TAO for Sell). + /// + /// `price_limit` encodes the tightest slippage constraint across all dominant-side + /// orders: a ceiling for buy-dominant swaps, a floor for sell-dominant swaps. + #[allow(clippy::too_many_arguments)] + fn net_pool_swap( + total_buy_net: u128, + total_sell_net: u128, + total_sell_tao_equiv: u128, + current_price: U64F64, + pallet_acct: &T::AccountId, + pallet_hotkey: &T::AccountId, + netuid: NetUid, + price_limit: u64, + ) -> Result<(OrderSide, u128), DispatchError> { + if total_buy_net >= total_sell_tao_equiv { + let net_tao = (total_buy_net.saturating_sub(total_sell_tao_equiv)) as u64; + let actual_alpha = if net_tao > 0 { + let out = T::SwapInterface::buy_alpha( + pallet_acct, + pallet_hotkey, + netuid, + TaoBalance::from(net_tao), + TaoBalance::from(price_limit), + false, + )? + .to_u64() as u128; + ensure!(out > 0, Error::::SwapReturnedZero); + out + } else { + 0u128 + }; + Ok((OrderSide::Buy, actual_alpha)) + } else { + let total_buy_alpha_equiv = Self::tao_to_alpha(total_buy_net, current_price)?; + let net_alpha = (total_sell_net.saturating_sub(total_buy_alpha_equiv)) as u64; + let actual_tao = if net_alpha > 0 { + let out = T::SwapInterface::sell_alpha( + pallet_acct, + pallet_hotkey, + netuid, + AlphaBalance::from(net_alpha), + TaoBalance::from(price_limit), + false, + )? + .to_u64() as u128; + ensure!(out > 0, Error::::SwapReturnedZero); + out + } else { + 0u128 + }; + Ok((OrderSide::Sell, actual_tao)) + } + } + + /// Distribute alpha pro-rata to ALL buyers and mark their orders fulfilled. + /// + /// - Buy-dominant: total alpha = pool output + sell-side alpha (passed through). + /// - Sell-dominant: total alpha = buy-side TAO converted at `current_price`. + #[allow(clippy::too_many_arguments)] + pub(crate) fn distribute_alpha_pro_rata( + buys: &BoundedVec, T::MaxOrdersPerBatch>, + actual_out: u128, + total_buy_net: u128, + total_sell_net: u128, + net_side: &OrderSide, + current_price: U64F64, + pallet_acct: &T::AccountId, + pallet_hotkey: &T::AccountId, + netuid: NetUid, + ) -> DispatchResult { + let total_alpha: u128 = match net_side { + OrderSide::Buy => actual_out.saturating_add(total_sell_net), + OrderSide::Sell => Self::tao_to_alpha(total_buy_net, current_price)?, + }; + + for e in buys.iter() { + let share: u64 = if total_buy_net > 0 { + total_alpha + .saturating_mul(e.net as u128) + .checked_div(total_buy_net) + .unwrap_or(0) as u64 + } else { + 0 + }; + // A floored-to-zero share means this buyer's input was already collected + // by `collect_assets` but the pool/offset alpha cannot pay them even one + // unit. Hard-fail the whole batch (FRAME's per-dispatch storage layer rolls + // back `collect_assets` and the pool swap) rather than silently skipping the + // transfer while still marking the order terminal — which would consume the + // signer's TAO for zero alpha and permanently close the order. + ensure!(share > 0, Error::::ZeroShareInBatch); + T::SwapInterface::transfer_staked_alpha( + pallet_acct, + pallet_hotkey, + &e.signer, + &e.hotkey, + netuid, + AlphaBalance::from(share), + false, // validate_sender: skip — pallet intermediary needs no validation + true, // set_receiver_limit: rate-limit the buyer after they receive stake + )?; + let status = Self::compute_order_status(e.order_id, e.partial_fill, e.order_amount); + Orders::::insert(e.order_id, status); + Self::deposit_event(Event::OrderExecuted { + order_id: e.order_id, + signer: e.signer.clone(), + netuid, + order_type: e.side.clone(), + amount_in: e.gross, + amount_out: share, + }); + } + Ok(()) + } + + /// Distribute TAO pro-rata to ALL sellers and mark their orders fulfilled. + /// + /// - Sell-dominant: total TAO = pool output + buy-side TAO (passed through). + /// - Buy-dominant: each seller receives their alpha valued at `current_price`. + /// + /// Fee on TAO output: `ppb(share)` is withheld from each seller's payout and + /// left in the pallet account. Returns the total sell-side fee TAO accumulated. + #[allow(clippy::too_many_arguments)] + pub(crate) fn distribute_tao_pro_rata( + sells: &BoundedVec, T::MaxOrdersPerBatch>, + actual_out: u128, + total_buy_net: u128, + total_sell_tao_equiv: u128, + net_side: &OrderSide, + current_price: U64F64, + pallet_acct: &T::AccountId, + netuid: NetUid, + ) -> Result, DispatchError> { + let total_tao: u128 = match net_side { + OrderSide::Sell => actual_out.saturating_add(total_buy_net), + OrderSide::Buy => total_sell_tao_equiv, + }; + + // Accumulate sell-side fees by recipient (one entry per unique recipient). + let mut sell_fees: Vec<(T::AccountId, u64)> = Vec::new(); + + for e in sells.iter() { + let sell_tao_equiv = Self::alpha_to_tao(e.net as u128, current_price); + let gross_share: u64 = if total_sell_tao_equiv > 0 { + total_tao + .saturating_mul(sell_tao_equiv) + .checked_div(total_sell_tao_equiv) + .unwrap_or(0) as u64 + } else { + 0u64 + }; + let fee = e.fee_rate.mul_floor(gross_share); + let net_share = gross_share.saturating_sub(fee); + + // A floored-to-zero payout means this seller's alpha was already collected + // by `collect_assets` but the pool/offset TAO cannot pay them even one unit + // (after fee). Hard-fail the whole batch (rolled back by the dispatch storage + // layer) rather than no-op the transfer while still marking the order terminal, + // which would consume the seller's alpha for zero TAO and close the order. + ensure!(net_share > 0, Error::::ZeroShareInBatch); + + if fee > 0 { + if let Some(entry) = sell_fees.iter_mut().find(|(r, _)| r == &e.fee_recipient) { + entry.1 = entry.1.saturating_add(fee); + } else { + sell_fees.push((e.fee_recipient.clone(), fee)); + } + } + + T::SwapInterface::transfer_tao( + pallet_acct, + &e.signer, + TaoBalance::from(net_share), + )?; + let status = Self::compute_order_status(e.order_id, e.partial_fill, e.order_amount); + Orders::::insert(e.order_id, status); + Self::deposit_event(Event::OrderExecuted { + order_id: e.order_id, + signer: e.signer.clone(), + netuid, + order_type: e.side.clone(), + amount_in: e.gross, + amount_out: net_share, + }); + } + Ok(sell_fees) + } + + /// Forward accumulated fees to their respective recipients. + /// + /// Merges buy-side fees (withheld from TAO input) and sell-side fees + /// (withheld from TAO output, passed in as `sell_fees`) by recipient, + /// then performs one TAO transfer per unique `fee_recipient`. + /// All transfers are best-effort and do not revert the batch on failure. + pub(crate) fn collect_fees( + buys: &BoundedVec, T::MaxOrdersPerBatch>, + sell_fees: Vec<(T::AccountId, u64)>, + pallet_acct: &T::AccountId, + ) -> DispatchResult { + // Start with sell fees; fold in buy fees. + // Buy fee was already computed in `validate_and_classify` as `gross - net`, + // so we recover it here without recomputing. + let mut fees: Vec<(T::AccountId, u64)> = sell_fees; + for e in buys.iter() { + let fee = e.gross.saturating_sub(e.net); + if fee > 0 { + if let Some(entry) = fees.iter_mut().find(|(r, _)| r == &e.fee_recipient) { + entry.1 = entry.1.saturating_add(fee); + } else { + fees.push((e.fee_recipient.clone(), fee)); + } + } + } + + // One transfer per unique fee recipient. + for (recipient, amount) in fees { + Self::forward_fee(pallet_acct, &recipient, TaoBalance::from(amount))?; + } + + // TODO: sweep rounding dust and any emissions accrued on the pallet account. + // Pro-rata integer division leaves small alpha residuals in (pallet_account, + // pallet_hotkey) after each batch. Over time these accumulate and, if an + // emission epoch fires while the dust is present, the pallet earns emissions + // it never distributes. Fix: add `staked_alpha(coldkey, hotkey, netuid) -> + // AlphaBalance` to `OrderSwapInterface`, then sell the full remaining balance + // here and forward the TAO to `FeeCollector`. + Ok(()) + } + + /// Compute the net amount field for the `GroupExecutionSummary` event. + pub(crate) fn net_amount_for_event( + net_side: &OrderSide, + total_buy_net: u128, + total_sell_net: u128, + total_sell_tao_equiv: u128, + current_price: U64F64, + ) -> Result { + match net_side { + OrderSide::Buy => Ok((total_buy_net.saturating_sub(total_sell_tao_equiv)) as u64), + OrderSide::Sell => { + let buy_alpha_equiv = Self::tao_to_alpha(total_buy_net, current_price)? as u64; + Ok((total_sell_net as u64).saturating_sub(buy_alpha_equiv)) + } + } + } + + /// Convert a TAO amount to alpha at `price` (TAO/alpha). + /// + /// A zero `price` yields `Ok(0)` (no alpha is purchasable). A genuine + /// fixed-point overflow returns `Err(ArithmeticOverflow)` so the caller + /// aborts the batch. + fn tao_to_alpha(tao: u128, price: U64F64) -> Result { + if price == U64F64::from_num(0u32) { + return Ok(0u128); + } + U64F64::saturating_from_num(tao) + .checked_div(price) + .map(|alpha| alpha.saturating_to_num::()) + .ok_or(Error::::ArithmeticOverflow.into()) + } + + /// Convert an alpha amount to TAO at `price` (TAO/alpha). + fn alpha_to_tao(alpha: u128, price: U64F64) -> u128 { + price + .saturating_mul(U64F64::saturating_from_num(alpha)) + .saturating_to_num::() + } + } +} diff --git a/pallets/limit-orders/src/migrations/migrate_register_pallet_hotkey.rs b/pallets/limit-orders/src/migrations/migrate_register_pallet_hotkey.rs new file mode 100644 index 0000000000..f3d6b4ef3f --- /dev/null +++ b/pallets/limit-orders/src/migrations/migrate_register_pallet_hotkey.rs @@ -0,0 +1,52 @@ +use alloc::string::String; +use frame_support::{BoundedVec, traits::Get, weights::Weight}; + +use crate::*; + +fn migration_key() -> BoundedVec { + BoundedVec::truncate_from(b"migrate_register_pallet_hotkey".to_vec()) +} + +/// One-shot migration that disables the limit-orders pallet on first upgrade and +/// registers the pallet intermediary hotkey if it has not been registered yet. +/// +/// Guarded by `HasMigrationRun` so it is safe to include in every runtime upgrade: +/// subsequent calls return immediately after a single storage read. +pub fn migrate_register_pallet_hotkey() -> Weight { + let migration_name = migration_key(); + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // Register the pallet intermediary hotkey if it has not been registered yet. + let pallet_acct = Pallet::::pallet_account(); + let pallet_hotkey = T::PalletHotkey::get(); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + + if !T::SwapInterface::pallet_hotkey_registered(&pallet_acct, &pallet_hotkey) { + let _ = T::SwapInterface::register_pallet_hotkey(&pallet_acct, &pallet_hotkey); + // register_pallet_hotkey writes Owner, OwnedHotkeys, StakingHotkeys + weight = weight.saturating_add(T::DbWeight::get().writes(3)); + } + + HasMigrationRun::::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/limit-orders/src/migrations/mod.rs b/pallets/limit-orders/src/migrations/mod.rs new file mode 100644 index 0000000000..391730d481 --- /dev/null +++ b/pallets/limit-orders/src/migrations/mod.rs @@ -0,0 +1,2 @@ +mod migrate_register_pallet_hotkey; +pub use migrate_register_pallet_hotkey::*; diff --git a/pallets/limit-orders/src/tests/auxiliary.rs b/pallets/limit-orders/src/tests/auxiliary.rs new file mode 100644 index 0000000000..1049c84f74 --- /dev/null +++ b/pallets/limit-orders/src/tests/auxiliary.rs @@ -0,0 +1,1667 @@ +#![allow(clippy::expect_used, clippy::unwrap_used, clippy::indexing_slicing)] +//! Unit tests for the auxiliary helper functions in `pallet-limit-orders`. +//! +//! Extrinsics are NOT tested here. Each section focuses on one helper. + +use frame_support::{BoundedVec, assert_noop, assert_ok, traits::ConstU32}; +use sp_core::H256; +use sp_keyring::Sr25519Keyring as AccountKeyring; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::NetUid; + +use sp_runtime::Perbill; + +use crate::pallet::Pallet as LimitOrders; +use crate::{OrderEntry, OrderSide, OrderStatus, OrderType, Orders}; + +use super::mock::*; + +// ───────────────────────────────────────────────────────────────────────────── +// net_amount_for_event +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn net_amount_for_event_buy_dominant() { + new_test_ext().execute_with(|| { + // Buys = 1000 TAO net, sells TAO-equiv = 300 TAO → net 700 TAO buy-side + let price = U64F64::from_num(2u32); // 2 TAO/alpha + let net = LimitOrders::::net_amount_for_event( + &OrderSide::Buy, + 1_000u128, // total_buy_net (TAO) + 150u128, // total_sell_net (alpha) ← not used in Buy branch + 300u128, // total_sell_tao_equiv + price, + ) + .expect("conversion does not overflow"); + assert_eq!(net, 700u64); + }); +} + +#[test] +fn net_amount_for_event_sell_dominant() { + new_test_ext().execute_with(|| { + // Sells = 500 alpha net, buys TAO = 200 TAO at price 2 → buy_alpha_equiv = 100 + // net sell = 500 - 100 = 400 alpha + let price = U64F64::from_num(2u32); // 2 TAO/alpha → 1 alpha = 2 TAO + let net = LimitOrders::::net_amount_for_event( + &OrderSide::Sell, + 200u128, // total_buy_net (TAO) + 500u128, // total_sell_net (alpha) + 400u128, // total_sell_tao_equiv (not used in Sell branch directly) + price, + ) + .expect("conversion does not overflow"); + // buy_alpha_equiv = 200 / 2 = 100; net = 500 - 100 = 400 + assert_eq!(net, 400u64); + }); +} + +#[test] +fn net_amount_for_event_perfectly_offset() { + new_test_ext().execute_with(|| { + // Buys = 200 TAO, sells TAO-equiv = 200 → net = 0 (buy-side result = 0) + let price = U64F64::from_num(2u32); + let net = LimitOrders::::net_amount_for_event( + &OrderSide::Buy, + 200u128, + 100u128, + 200u128, + price, + ) + .expect("conversion does not overflow"); + assert_eq!(net, 0u64); + }); +} + +#[test] +fn net_amount_for_event_sell_overflow_returns_error() { + new_test_ext().execute_with(|| { + let tiny_price = U64F64::from_bits(1); + assert_eq!( + LimitOrders::::net_amount_for_event( + &OrderSide::Sell, + u128::MAX, + 500u128, + 0u128, + tiny_price, + ), + Err(Error::::ArithmeticOverflow.into()), + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// validate_and_classify +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn validate_and_classify_separates_buys_and_sells() { + new_test_ext().execute_with(|| { + // Current time = 1_000_000 ms; expiry = 2_000_000 ms (well in the future). + MockTime::set(1_000_000); + // Price = 1.0 TAO/alpha. + MockSwap::set_price(1.0); + + let buy_order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000u64, // amount in TAO + 2_000_000_000u64, // limit_price: willing to pay up to 2 TAO/alpha in ×10⁹ scale (scaled=1_000_000_000 ≤ 2_000_000_000 ✓) + 2_000_000u64, // expiry ms + Perbill::zero(), + fee_recipient(), + None, + ); + let sell_order = make_signed_order( + AccountKeyring::Bob, + alice(), + netuid(), + OrderType::TakeProfit, + 500u64, // amount in alpha + 1_000_000_000u64, // limit_price: sell if price >= 1 TAO/alpha in ×10⁹ scale (scaled=1_000_000_000 >= 1_000_000_000 ✓) + 2_000_000u64, + Perbill::zero(), + fee_recipient(), + None, + ); + + let orders = bounded(vec![buy_order, sell_order]); + let (buys, sells) = LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(1u32), + bob(), + ) + .expect("validate_and_classify should succeed"); + + assert_eq!(buys.len(), 1, "expected 1 valid buy"); + assert_eq!(sells.len(), 1, "expected 1 valid sell"); + + // Buy entry: gross=1000, net=1000 (0% fee_rate) + let buy = &buys[0]; + assert_eq!(buy.signer, alice()); + assert_eq!(buy.gross, 1_000u64); + assert_eq!(buy.net, 1_000u64); + assert_eq!(buy.fee_rate, Perbill::zero()); + + // Sell entry: gross=500, net=500 (fee applied on TAO output, not alpha input) + let sell = &sells[0]; + assert_eq!(sell.signer, bob()); + assert_eq!(sell.gross, 500u64); + assert_eq!(sell.net, 500u64); + }); +} + +#[test] +fn validate_and_classify_fails_for_wrong_netuid() { + new_test_ext().execute_with(|| { + // An order whose netuid does not match the batch netuid must cause a hard failure. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let wrong_netuid_order = make_signed_order( + AccountKeyring::Alice, + bob(), + NetUid::from(99u16), // different netuid + OrderType::LimitBuy, + 1_000u64, + 2_000_000_000u64, // 2.0 in ×10⁹ scale + 2_000_000u64, + Perbill::zero(), + fee_recipient(), + None, + ); + + let orders = bounded(vec![wrong_netuid_order]); + assert_noop!( + LimitOrders::::validate_and_classify( + netuid(), // batch is for netuid 1 + &orders, + 1_000_000u64, + U64F64::from_num(1u32), + bob() + ), + crate::Error::::OrderNetUidMismatch + ); + }); +} + +#[test] +fn validate_and_classify_fails_for_expired_order() { + new_test_ext().execute_with(|| { + // now_ms = 2_000_001, expiry = 2_000_000 → expired → hard failure. + MockTime::set(2_000_001); + MockSwap::set_price(1.0); + + let expired = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000u64, + 2_000_000_000u64, // 2.0 in ×10⁹ scale + 2_000_000u64, // expiry already past + Perbill::zero(), + fee_recipient(), + None, + ); + + let orders = bounded(vec![expired]); + assert_noop!( + LimitOrders::::validate_and_classify( + netuid(), + &orders, + 2_000_001u64, + U64F64::from_num(1u32), + bob() + ), + crate::Error::::OrderExpired + ); + }); +} + +#[test] +fn validate_and_classify_fails_for_price_condition_not_met_for_buy() { + new_test_ext().execute_with(|| { + // Price = 3.0 TAO/alpha, scaled = 3_000_000_000, buyer's limit = 2_000_000_000 (2.0 in ×10⁹) → scaled > limit → hard failure. + MockTime::set(1_000_000); + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000u64, + 2_000_000_000u64, // 2.0 in ×10⁹ scale + 2_000_000u64, + Perbill::zero(), + fee_recipient(), + None, + ); + + let orders = bounded(vec![order]); + assert_noop!( + LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(3u32), // current price = 3 > limit 2 → fails + bob() + ), + crate::Error::::PriceConditionNotMet + ); + }); +} + +#[test] +fn validate_and_classify_fails_for_already_processed_order() { + new_test_ext().execute_with(|| { + // An order already marked Fulfilled must cause a hard failure. + MockTime::set(1_000_000); + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000u64, + 2_000_000_000u64, // 2.0 in ×10⁹ scale + 2_000_000u64, + Perbill::zero(), + fee_recipient(), + None, + ); + + // Pre-mark as fulfilled on-chain. + let oid = LimitOrders::::derive_order_id(&order.order); + Orders::::insert(oid, OrderStatus::Fulfilled); + + let orders = bounded(vec![order]); + assert_noop!( + LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(1u32), + bob() + ), + crate::Error::::OrderAlreadyProcessed + ); + }); +} + +#[test] +fn validate_and_classify_applies_buy_fee_to_net() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + // 1_000_000 ppb = 0.1% + // amount = 1_000_000_000, fee = 1_000_000, net = 999_000_000 + + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000_000_000u64, + u64::MAX, // limit price: accept any price + 2_000_000u64, + Perbill::from_parts(1_000_000), // 0.1% fee + fee_recipient(), + None, + ); + + let orders = bounded(vec![order]); + let (buys, _) = LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(1u32), + bob(), + ) + .expect("validate_and_classify should succeed"); + + assert_eq!(buys.len(), 1); + let entry = &buys[0]; + assert_eq!(entry.gross, 1_000_000_000u64); + assert_eq!(entry.fee_rate, Perbill::from_parts(1_000_000)); + assert_eq!(entry.net, 999_000_000u64); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// compute_effective_swap_limit +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn compute_effective_swap_limit_buy_no_slippage() { + new_test_ext().execute_with(|| { + // No slippage → u64::MAX (no ceiling). + let limit = LimitOrders::::compute_effective_swap_limit(true, 1_000, None); + assert_eq!(limit, u64::MAX); + }); +} + +#[test] +fn compute_effective_swap_limit_sell_no_slippage() { + new_test_ext().execute_with(|| { + // No slippage → 0 (no floor). + let limit = LimitOrders::::compute_effective_swap_limit(false, 1_000, None); + assert_eq!(limit, 0); + }); +} + +#[test] +fn compute_effective_swap_limit_buy_one_percent() { + new_test_ext().execute_with(|| { + // 1% slippage on a buy with limit_price=1000 → ceiling = 1010. + let limit = LimitOrders::::compute_effective_swap_limit( + true, + 1_000, + Some(Perbill::from_percent(1)), + ); + assert_eq!(limit, 1_010); + }); +} + +#[test] +fn compute_effective_swap_limit_sell_one_percent() { + new_test_ext().execute_with(|| { + // 1% slippage on a sell with limit_price=1000 → floor = 990. + let limit = LimitOrders::::compute_effective_swap_limit( + false, + 1_000, + Some(Perbill::from_percent(1)), + ); + assert_eq!(limit, 990); + }); +} + +#[test] +fn compute_effective_swap_limit_sell_saturates_at_zero() { + new_test_ext().execute_with(|| { + // 100% slippage on a sell with limit_price=500 → floor saturates at 0. + let limit = LimitOrders::::compute_effective_swap_limit( + false, + 500, + Some(Perbill::from_percent(100)), + ); + assert_eq!(limit, 0); + }); +} + +#[test] +fn compute_effective_swap_limit_buy_saturates_at_u64_max() { + new_test_ext().execute_with(|| { + // 100% slippage on a buy with limit_price=u64::MAX → ceiling saturates at u64::MAX. + let limit = LimitOrders::::compute_effective_swap_limit( + true, + u64::MAX, + Some(Perbill::from_percent(100)), + ); + assert_eq!(limit, u64::MAX); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// validate_and_classify — effective_swap_limit propagation +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn validate_and_classify_stores_effective_swap_limit_for_buy() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + // 1% slippage on limit_price=2_000_000_000 (2.0 in ×10⁹) → ceiling = 2_020_000_000. + // price=1.0, scaled=1_000_000_000 <= 2_000_000_000 ✓. + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 500u64, + 2_000_000_000u64, // 2.0 in ×10⁹ scale + 2_000_000u64, + Perbill::zero(), + fee_recipient(), + None, + ); + // Override max_slippage on the inner order after signing — we need to rebuild + // the signed order so the signature covers the updated payload. + let new_inner = { + let mut o = order.order.inner().clone(); + o.max_slippage = Some(Perbill::from_percent(1)); + o + }; + let versioned = crate::VersionedOrder::V1(new_inner.clone()); + let sig = AccountKeyring::Alice.pair().sign(&versioned.encode()); + let signed_with_slippage = crate::SignedOrder { + order: versioned, + signature: sp_runtime::MultiSignature::Sr25519(sig), + partial_fill: None, + }; + + let orders = bounded(vec![signed_with_slippage]); + let (buys, _) = LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(1u32), + bob(), + ) + .expect("should succeed"); + + assert_eq!(buys[0].effective_swap_limit, 2_020_000_000); + }); +} + +#[test] +fn validate_and_classify_stores_effective_swap_limit_for_sell() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + // Price must be >= limit_price (in ×10⁹ scale) for TakeProfit to trigger. + // limit_price=1_000_000_000 (1.0 in ×10⁹), 1% slippage → floor = 990_000_000. + let new_inner = crate::Order { + signer: AccountKeyring::Alice.to_account_id(), + hotkey: bob(), + netuid: netuid(), + order_type: OrderType::TakeProfit, + amount: 500u64, + limit_price: 1_000_000_000u64, // 1.0 in ×10⁹ scale + expiry: u64::MAX, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: Some(Perbill::from_percent(1)), + chain_id: 945, + partial_fills_enabled: false, + }; + let versioned = crate::VersionedOrder::V1(new_inner); + let sig = AccountKeyring::Alice.pair().sign(&versioned.encode()); + let signed = crate::SignedOrder { + order: versioned, + signature: sp_runtime::MultiSignature::Sr25519(sig), + partial_fill: None, + }; + + let orders = bounded(vec![signed]); + let (_, sells) = LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(2u32), // current_price=2.0, scaled=2_000_000_000 >= limit_price=1_000_000_000 ✓ + bob(), + ) + .expect("should succeed"); + + assert_eq!(sells[0].effective_swap_limit, 990_000_000); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// validate_and_classify — relayer enforcement +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn validate_and_classify_fails_for_wrong_relayer() { + new_test_ext().execute_with(|| { + // Order explicitly locks execution to charlie(); submitting as bob() must fail. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000u64, + u64::MAX, + 2_000_000u64, + Perbill::zero(), + fee_recipient(), + Some(BoundedVec::try_from(vec![charlie()]).unwrap()), // only charlie may relay this order + ); + + let orders = bounded(vec![order]); + assert_noop!( + LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(1u32), + bob() // wrong relayer + ), + crate::Error::::RelayerMissMatch + ); + }); +} + +#[test] +fn validate_and_classify_succeeds_for_correct_relayer() { + new_test_ext().execute_with(|| { + // Same setup as above but now the correct relayer (charlie) is used. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000u64, + u64::MAX, + 2_000_000u64, + Perbill::zero(), + fee_recipient(), + Some(BoundedVec::try_from(vec![charlie()]).unwrap()), // only charlie may relay this order + ); + + let orders = bounded(vec![order]); + let (buys, sells) = LimitOrders::::validate_and_classify( + netuid(), + &orders, + 1_000_000u64, + U64F64::from_num(1u32), + charlie(), // correct relayer + ) + .expect("validate_and_classify should succeed"); + + assert_eq!(buys.len(), 1, "expected 1 valid buy"); + assert_eq!(sells.len(), 0); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// distribute_alpha_pro_rata +// ───────────────────────────────────────────────────────────────────────────── +// +// Scenario A – buy-dominant, pool rate = 1:1 +// ─────────────────────────────────────────── +// Both buyers and sellers are present, but buys exceed sells in TAO terms. +// Sellers are settled first (they receive TAO in distribute_tao_pro_rata). +// Their alpha (200 total) stays in the pallet account as passthrough for buyers. +// The residual buy TAO hits the pool and returns 800 alpha (at 1:1 rate). +// +// 3 buyers: Alice 300 TAO net, Bob 200 TAO net, Charlie 500 TAO net (total 1000) +// Sellers contributed 200 alpha (passthrough, no pool interaction). +// Net residual TAO to pool = 1000 - 200 = 800 TAO → pool returns 800 alpha (1:1). +// Total alpha available to buyers = 800 (pool) + 200 (seller passthrough) = 1000. +// +// Pro-rata shares (proportional to each buyer's net TAO): +// Alice: 1000 * 300 / 1000 = 300 alpha +// Bob: 1000 * 200 / 1000 = 200 alpha +// Charlie: 1000 * 500 / 1000 = 500 alpha +// +// Scenario B – sell-dominant +// ─────────────────────────── +// Both buyers and sellers are present, but sells exceed buys in TAO terms. +// Buyers are settled from the sellers' alpha directly (no pool for them). +// The residual sell alpha hits the pool; sellers receive TAO in distribute_tao_pro_rata. +// +// 2 buyers: Alice 400 TAO net, Bob 600 TAO net (total 1000) +// Price = 2.0 TAO/alpha → total alpha for buyers = 1000 / 2 = 500 alpha. +// +// Pro-rata shares: +// Alice: 500 * 400 / 1000 = 200 alpha +// Bob: 500 * 600 / 1000 = 300 alpha +// +// Scenario C – buy-dominant, pool rate != 1:1 +// ──────────────────────────────────────────────────────── +// Same structure as Scenario A but the pool returns fewer alpha than the TAO +// sent in, simulating realistic AMM. Pro-rata is computed over +// whatever the pool actually returned — the distribution logic is rate-agnostic. +// +// 3 buyers: Alice 300 TAO net, Bob 200 TAO net, Charlie 500 TAO net (total 1000) +// Sellers contributed 200 alpha (passthrough). +// Net residual TAO to pool = 800 TAO → pool returns 750 alpha (slippage). +// Total alpha available to buyers = 750 (pool) + 200 (seller passthrough) = 950. +// +// Pro-rata shares: +// Alice: 950 * 300 / 1000 = 285 alpha +// Bob: 950 * 200 / 1000 = 190 alpha +// Charlie: 950 * 500 / 1000 = 475 alpha +// +// Scenario D – buy-dominant, indivisible remainder (dust) +// ───────────────────────────────────────────────────────── +// Integer division floors every share. The sum of floors is strictly less than +// total_alpha when total_alpha is not divisible by total_buy_net. +// The leftover alpha stays in the pallet intermediary account (never transferred). +// +// 3 buyers: Alice 1 TAO net, Bob 1 TAO net, Charlie 1 TAO net (total 3) +// Pool returns 10 alpha; no sellers → total_alpha = 10. +// +// Pro-rata shares (floor): +// Alice: floor(10 * 1 / 3) = 3 alpha +// Bob: floor(10 * 1 / 3) = 3 alpha +// Charlie: floor(10 * 1 / 3) = 3 alpha +// Total distributed: 9 alpha +// Dust remaining in pallet account: 10 - 9 = 1 alpha (never transferred) + +fn make_buy_entry( + order_id: H256, + signer: AccountId, + hotkey: AccountId, + gross: u64, + net: u64, + fee_rate: Perbill, + fee_recipient: AccountId, +) -> OrderEntry { + OrderEntry { + order_id, + signer, + hotkey, + side: OrderType::LimitBuy, + gross, + order_amount: gross, + net, + fee_rate, + fee_recipient, + effective_swap_limit: u64::MAX, // no slippage constraint + partial_fill: None, + } +} + +fn bounded_buy_entries( + v: Vec>, +) -> BoundedVec, ConstU32<64>> { + BoundedVec::try_from(v).unwrap() +} + +fn bounded_sell_entries( + v: Vec>, +) -> BoundedVec, ConstU32<64>> { + BoundedVec::try_from(v).unwrap() +} + +#[test] +fn distribute_alpha_pro_rata_buy_dominant_scenario_a() { + new_test_ext().execute_with(|| { + // Pool returned 800 alpha; sell-side passthrough = 200 alpha. + // Total = 1000 alpha distributed across 3 buyers (300, 200, 500 TAO net). + // Expected shares: Alice 300, Bob 200, Charlie 500. + + let hotkey = AccountKeyring::Dave.to_account_id(); + let entries = bounded_buy_entries(vec![ + make_buy_entry( + H256::repeat_byte(1), + alice(), + hotkey.clone(), + 300, + 300, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(2), + bob(), + hotkey.clone(), + 200, + 200, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(3), + charlie(), + hotkey.clone(), + 500, + 500, + Perbill::zero(), + fee_recipient(), + ), + ]); + let pallet_acct = PalletHotkeyAccount::get(); // reuse as coldkey for brevity + let pallet_hk = PalletHotkeyAccount::get(); + + LimitOrders::::distribute_alpha_pro_rata( + &entries, + 800u128, // actual_out from pool (alpha) + 1_000u128, // total_buy_net (TAO) + 200u128, // total_sell_net (alpha passthrough) + &OrderSide::Buy, + U64F64::from_num(1u32), + &pallet_acct, + &pallet_hk, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::alpha_transfers(); + // 3 transfers expected (one per buyer) + assert_eq!(transfers.len(), 3); + + // Check each recipient's amount (signer is to_coldkey). + let alice_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &alice()) + .unwrap() + .5; + let bob_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &bob()) + .unwrap() + .5; + let charlie_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &charlie()) + .unwrap() + .5; + + assert_eq!(alice_amt, 300u64, "Alice should receive 300 alpha"); + assert_eq!(bob_amt, 200u64, "Bob should receive 200 alpha"); + assert_eq!(charlie_amt, 500u64, "Charlie should receive 500 alpha"); + }); +} + +#[test] +fn distribute_alpha_pro_rata_sell_dominant_scenario_b() { + new_test_ext().execute_with(|| { + // Price = 2.0 TAO/alpha; buyers have 400 + 600 = 1000 TAO net. + // Total alpha = 1000 / 2 = 500. + // Expected: Alice 200 alpha, Bob 300 alpha. + + let hotkey = AccountKeyring::Dave.to_account_id(); + let entries = bounded_buy_entries(vec![ + make_buy_entry( + H256::repeat_byte(4), + alice(), + hotkey.clone(), + 400, + 400, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(5), + bob(), + hotkey.clone(), + 600, + 600, + Perbill::zero(), + fee_recipient(), + ), + ]); + let pallet_acct = PalletHotkeyAccount::get(); + let pallet_hk = PalletHotkeyAccount::get(); + + LimitOrders::::distribute_alpha_pro_rata( + &entries, + 0u128, // actual_out unused in sell-dominant branch + 1_000u128, // total_buy_net (TAO) + 999u128, // total_sell_net — doesn't matter for sell-dominant logic + &OrderSide::Sell, + U64F64::from_num(2u32), // price = 2 TAO/alpha + &pallet_acct, + &pallet_hk, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::alpha_transfers(); + assert_eq!(transfers.len(), 2); + + let alice_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &alice()) + .unwrap() + .5; + let bob_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &bob()) + .unwrap() + .5; + + assert_eq!(alice_amt, 200u64, "Alice should receive 200 alpha"); + assert_eq!(bob_amt, 300u64, "Bob should receive 300 alpha"); + }); +} + +#[test] +fn distribute_alpha_pro_rata_buy_dominant_scenario_c() { + new_test_ext().execute_with(|| { + // Scenario C: same buyer setup as A but pool returns 750 alpha (slippage) + // instead of 800. Proves pro-rata is computed over actual pool output and + // is therefore rate-agnostic — the distribution logic doesn't assume 1:1. + // + // Net residual TAO to pool = 800 TAO → pool returns 750 alpha (not 800). + // Total alpha = 750 (pool) + 200 (seller passthrough) = 950. + // + // Expected shares: + // Alice: 950 * 300 / 1000 = 285 alpha + // Bob: 950 * 200 / 1000 = 190 alpha + // Charlie: 950 * 500 / 1000 = 475 alpha + + let hotkey = AccountKeyring::Dave.to_account_id(); + let entries = bounded_buy_entries(vec![ + make_buy_entry( + H256::repeat_byte(6), + alice(), + hotkey.clone(), + 300, + 300, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(7), + bob(), + hotkey.clone(), + 200, + 200, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(8), + charlie(), + hotkey.clone(), + 500, + 500, + Perbill::zero(), + fee_recipient(), + ), + ]); + let pallet_acct = PalletHotkeyAccount::get(); + let pallet_hk = PalletHotkeyAccount::get(); + + LimitOrders::::distribute_alpha_pro_rata( + &entries, + 750u128, // actual_out from pool (750, not 800 — slippage) + 1_000u128, // total_buy_net (TAO) + 200u128, // total_sell_net (alpha passthrough) + &OrderSide::Buy, + U64F64::from_num(1u32), + &pallet_acct, + &pallet_hk, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::alpha_transfers(); + assert_eq!(transfers.len(), 3); + + let alice_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &alice()) + .unwrap() + .5; + let bob_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &bob()) + .unwrap() + .5; + let charlie_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &charlie()) + .unwrap() + .5; + + assert_eq!( + alice_amt, 285u64, + "Alice receives 950 * 300/1000 = 285 alpha" + ); + assert_eq!(bob_amt, 190u64, "Bob receives 950 * 200/1000 = 190 alpha"); + assert_eq!( + charlie_amt, 475u64, + "Charlie receives 950 * 500/1000 = 475 alpha" + ); + }); +} + +#[test] +fn distribute_alpha_pro_rata_dust_remains_in_pallet_scenario_d() { + new_test_ext().execute_with(|| { + // Scenario D: total_alpha = 10, three equal buyers (total_buy_net = 3). + // floor(10 * 1/3) = 3 each → 9 distributed → 1 alpha dust stays in pallet. + + let hotkey = AccountKeyring::Dave.to_account_id(); + let pallet_acct = PalletHotkeyAccount::get(); + let pallet_hk = PalletHotkeyAccount::get(); + + // Seed the pallet account with the 10 alpha it would hold after collect_assets + // and the pool swap (actual_out=10, no sellers). + MockSwap::set_alpha_balance(pallet_acct.clone(), pallet_hk.clone(), netuid(), 10); + + let entries = bounded_buy_entries(vec![ + make_buy_entry( + H256::repeat_byte(9), + alice(), + hotkey.clone(), + 1, + 1, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(10), + bob(), + hotkey.clone(), + 1, + 1, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(11), + charlie(), + hotkey.clone(), + 1, + 1, + Perbill::zero(), + fee_recipient(), + ), + ]); + + LimitOrders::::distribute_alpha_pro_rata( + &entries, + 10u128, // actual_out from pool + 3u128, // total_buy_net (TAO) — not divisible into 10 evenly + 0u128, // total_sell_net — no sellers + &OrderSide::Buy, + U64F64::from_num(1u32), + &pallet_acct, + &pallet_hk, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::alpha_transfers(); + assert_eq!(transfers.len(), 3); + + let alice_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &alice()) + .unwrap() + .5; + let bob_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &bob()) + .unwrap() + .5; + let charlie_amt = transfers + .iter() + .find(|(_, _, to_ck, _, _, _)| to_ck == &charlie()) + .unwrap() + .5; + + assert_eq!(alice_amt, 3u64, "floor(10 * 1/3) = 3"); + assert_eq!(bob_amt, 3u64, "floor(10 * 1/3) = 3"); + assert_eq!(charlie_amt, 3u64, "floor(10 * 1/3) = 3"); + + // The pallet account started with 10 and sent out 9 — 1 alpha dust remains + // in the pallet account, not burnt, not distributed. + let pallet_remaining = MockSwap::alpha_balance(&pallet_acct, &pallet_hk, netuid()); + assert_eq!( + pallet_remaining, 1u64, + "1 alpha dust stays in pallet account, not burnt" + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// distribute_tao_pro_rata +// ───────────────────────────────────────────────────────────────────────────── +// +// Scenario A – sell-dominant, fee = 0 +// ───────────────────────────────────── +// Both buyers and sellers are present, but sells exceed buys in TAO terms. +// Buyers are settled first (they receive alpha in distribute_alpha_pro_rata). +// The residual sell alpha hits the pool; pool returns TAO. +// Buy-side TAO also stays in pallet as passthrough for sellers. +// +// 2 sellers: Alice 400 alpha, Bob 600 alpha (total 1000 alpha) +// Price = 2.0 TAO/alpha → sell_tao_equiv: Alice 800, Bob 1200, total 2000. +// Pool returned 1200 TAO for the residual alpha; buy passthrough = 800 TAO. +// Total TAO available to sellers = 1200 (pool) + 800 (buy passthrough) = 2000. +// +// Pro-rata shares (proportional to each seller's TAO-equiv): +// Alice: 2000 * 800 / 2000 = 800 TAO +// Bob: 2000 * 1200 / 2000 = 1200 TAO +// +// Scenario B – sell-dominant, fee = 1% (10_000_000 ppb) +// ──────────────────────────────────────────────────────── +// Same structure as Scenario A. Fee is deducted from each seller's gross TAO +// payout; the withheld TAO stays in the pallet account for collect_fees. +// +// Alice gross=800, fee=8 (1% of 800), net=792 TAO +// Bob gross=1200, fee=12, net=1188 TAO +// Total sell fee returned: 20 TAO +// +// Scenario C – buy-dominant +// ────────────────────────── +// Both buyers and sellers are present, but buys exceed sells in TAO terms. +// Sellers receive their alpha valued at current_price — no pool interaction +// for them. The TAO they receive comes from the buyers' collected TAO directly. +// +// 2 sellers: Alice 300 alpha, Bob 200 alpha (total 500 alpha) +// Price = 2.0 TAO/alpha → sell_tao_equiv: Alice 600, Bob 400, total 1000. +// Buy-dominant branch: total_tao = total_sell_tao_equiv = 1000 TAO. +// +// Shares: +// Alice: 1000 * 600 / 1000 = 600 TAO +// Bob: 1000 * 400 / 1000 = 400 TAO +// +// Scenario D – sell-dominant, indivisible remainder (dust) +// ───────────────────────────────────────────────────────── +// Integer division floors every gross share. The leftover TAO stays in the +// pallet intermediary account (never transferred, not burnt). +// +// 3 sellers: Alice 1 alpha, Bob 1 alpha, Charlie 1 alpha (total 3 alpha) +// Price = 1.0 TAO/alpha → sell_tao_equiv = 1 each, total_sell_tao_equiv = 3. +// No buyers; actual_out from pool = 10 TAO, buy passthrough = 0. +// total_tao = 10 + 0 = 10. +// +// Pro-rata shares (floor): +// Alice: floor(10 * 1 / 3) = 3 TAO +// Bob: floor(10 * 1 / 3) = 3 TAO +// Charlie: floor(10 * 1 / 3) = 3 TAO +// Total distributed: 9 TAO +// Dust remaining in pallet account: 10 - 9 = 1 TAO (never transferred) + +#[test] +fn distribute_tao_pro_rata_sell_dominant_no_fee_scenario_a() { + new_test_ext().execute_with(|| { + // Price = 2, total_tao = 1200 (pool) + 800 (buy passthrough) = 2000 + // Alice alpha=400 → tao_equiv=800; Bob alpha=600 → tao_equiv=1200. + // total_sell_tao_equiv = 2000. + // Shares: Alice 800, Bob 1200. + + let hotkey = AccountKeyring::Dave.to_account_id(); + let entries = bounded_sell_entries(vec![ + make_buy_entry( + H256::repeat_byte(6), + alice(), + hotkey.clone(), + 400, + 400, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(7), + bob(), + hotkey.clone(), + 600, + 600, + Perbill::zero(), + fee_recipient(), + ), + ]); + let pallet_acct = PalletHotkeyAccount::get(); + + let sell_fees = LimitOrders::::distribute_tao_pro_rata( + &entries, + 1_200u128, // actual_out (pool TAO) + 800u128, // total_buy_net (buy passthrough TAO) + 2_000u128, // total_sell_tao_equiv (Alice 800 + Bob 1200) + &OrderSide::Sell, + U64F64::from_num(2u32), + &pallet_acct, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::tao_transfers(); + assert_eq!(transfers.len(), 2); + let alice_tao = transfers + .iter() + .find(|(_, to, _)| to == &alice()) + .unwrap() + .2; + let bob_tao = transfers.iter().find(|(_, to, _)| to == &bob()).unwrap().2; + + assert_eq!(alice_tao, 800u64, "Alice should receive 800 TAO"); + assert_eq!(bob_tao, 1_200u64, "Bob should receive 1200 TAO"); + assert_eq!( + sell_fees, + vec![] as Vec<(AccountId, u64)>, + "No fees at 0 ppb" + ); + }); +} + +#[test] +fn distribute_tao_pro_rata_sell_dominant_with_fee_scenario_b() { + new_test_ext().execute_with(|| { + // Same setup as above but fee = 10_000_000 ppb = 1%. + // Alice gross=800, fee=8, net=792; Bob gross=1200, fee=12, net=1188. + // Total sell fee = 20. + + let hotkey = AccountKeyring::Dave.to_account_id(); + let entries = bounded_sell_entries(vec![ + make_buy_entry( + H256::repeat_byte(8), + alice(), + hotkey.clone(), + 400, + 400, + Perbill::from_parts(10_000_000), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(9), + bob(), + hotkey.clone(), + 600, + 600, + Perbill::from_parts(10_000_000), + fee_recipient(), + ), + ]); + let pallet_acct = PalletHotkeyAccount::get(); + + let sell_fees = LimitOrders::::distribute_tao_pro_rata( + &entries, + 1_200u128, + 800u128, + 2_000u128, + &OrderSide::Sell, + U64F64::from_num(2u32), + &pallet_acct, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::tao_transfers(); + assert_eq!(transfers.len(), 2); + let alice_tao = transfers + .iter() + .find(|(_, to, _)| to == &alice()) + .unwrap() + .2; + let bob_tao = transfers.iter().find(|(_, to, _)| to == &bob()).unwrap().2; + + assert_eq!(alice_tao, 792u64, "Alice net after 1% fee on 800"); + assert_eq!(bob_tao, 1_188u64, "Bob net after 1% fee on 1200"); + assert_eq!( + sell_fees, + vec![(fee_recipient(), 20u64)], + "total sell fee = 8 + 12" + ); + }); +} + +#[test] +fn distribute_tao_pro_rata_buy_dominant_scenario_c() { + new_test_ext().execute_with(|| { + // Buy-dominant: total_tao = total_sell_tao_equiv = 1000. + // Alice alpha=300 → tao_equiv=600; Bob alpha=200 → tao_equiv=400. + // Shares: Alice 600, Bob 400. + + let hotkey = AccountKeyring::Dave.to_account_id(); + let entries = bounded_sell_entries(vec![ + make_buy_entry( + H256::repeat_byte(10), + alice(), + hotkey.clone(), + 300, + 300, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(11), + bob(), + hotkey.clone(), + 200, + 200, + Perbill::zero(), + fee_recipient(), + ), + ]); + let pallet_acct = PalletHotkeyAccount::get(); + + let sell_fees = LimitOrders::::distribute_tao_pro_rata( + &entries, + 0u128, // actual_out unused in Buy-dominant branch + 0u128, // total_buy_net unused in Buy-dominant branch + 1_000u128, // total_sell_tao_equiv (total_tao = this in Buy branch) + &OrderSide::Buy, + U64F64::from_num(2u32), + &pallet_acct, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::tao_transfers(); + assert_eq!(transfers.len(), 2); + let alice_tao = transfers + .iter() + .find(|(_, to, _)| to == &alice()) + .unwrap() + .2; + let bob_tao = transfers.iter().find(|(_, to, _)| to == &bob()).unwrap().2; + + assert_eq!(alice_tao, 600u64, "Alice should receive 600 TAO"); + assert_eq!(bob_tao, 400u64, "Bob should receive 400 TAO"); + assert_eq!(sell_fees, vec![] as Vec<(AccountId, u64)>); + }); +} + +#[test] +fn distribute_tao_pro_rata_dust_remains_in_pallet_scenario_d() { + new_test_ext().execute_with(|| { + // Scenario D: total_tao = 10, three equal sellers (total_sell_tao_equiv = 3). + // floor(10 * 1/3) = 3 each → 9 distributed → 1 TAO dust stays in pallet. + + let hotkey = AccountKeyring::Dave.to_account_id(); + let pallet_acct = PalletHotkeyAccount::get(); + + // Seed the pallet account with the 10 TAO it would hold after collect_assets + // and the pool swap (actual_out=10, no buyers). + MockSwap::set_tao_balance(pallet_acct.clone(), 10); + + let entries = bounded_sell_entries(vec![ + make_buy_entry( + H256::repeat_byte(12), + alice(), + hotkey.clone(), + 1, + 1, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(13), + bob(), + hotkey.clone(), + 1, + 1, + Perbill::zero(), + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(14), + charlie(), + hotkey.clone(), + 1, + 1, + Perbill::zero(), + fee_recipient(), + ), + ]); + + let sell_fees = LimitOrders::::distribute_tao_pro_rata( + &entries, + 10u128, // actual_out from pool (TAO) + 0u128, // total_buy_net — no buyers + 3u128, // total_sell_tao_equiv — not divisible into 10 evenly + &OrderSide::Sell, + U64F64::from_num(1u32), + &pallet_acct, + netuid(), + ) + .unwrap(); + + let transfers = MockSwap::tao_transfers(); + assert_eq!(transfers.len(), 3); + + let alice_tao = transfers + .iter() + .find(|(_, to, _)| to == &alice()) + .unwrap() + .2; + let bob_tao = transfers.iter().find(|(_, to, _)| to == &bob()).unwrap().2; + let charlie_tao = transfers + .iter() + .find(|(_, to, _)| to == &charlie()) + .unwrap() + .2; + + assert_eq!(alice_tao, 3u64, "floor(10 * 1/3) = 3"); + assert_eq!(bob_tao, 3u64, "floor(10 * 1/3) = 3"); + assert_eq!(charlie_tao, 3u64, "floor(10 * 1/3) = 3"); + assert_eq!(sell_fees, vec![] as Vec<(AccountId, u64)>); + + // The pallet account started with 10 TAO and sent out 9 — 1 TAO dust remains, + // not burnt, not distributed. + let pallet_remaining = MockSwap::tao_balance(&pallet_acct); + assert_eq!( + pallet_remaining, 1u64, + "1 TAO dust stays in pallet account, not burnt" + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// collect_fees +// ───────────────────────────────────────────────────────────────────────────── +// +// Scenario: +// 2 buy orders with fees 50 and 150 TAO → total_buy_fee = 200 TAO. +// sell_fee_tao passed in = 80 TAO. +// Total fee = 280 TAO forwarded to FeeCollector in one transfer. + +#[test] +fn collect_fees_forwards_combined_fees_to_collector() { + new_test_ext().execute_with(|| { + let hotkey = AccountKeyring::Dave.to_account_id(); + // Buy entries carry fee in field index 5. + let buys = bounded_buy_entries(vec![ + make_buy_entry( + H256::repeat_byte(20), + alice(), + hotkey.clone(), + 1_000, + 950, + Perbill::from_parts(50_000_000), // 5% of 1000 = 50 + fee_recipient(), + ), + make_buy_entry( + H256::repeat_byte(21), + bob(), + hotkey.clone(), + 1_500, + 1_350, + Perbill::from_parts(100_000_000), // 10% of 1500 = 150 + fee_recipient(), + ), + ]); + let pallet_acct = PalletHotkeyAccount::get(); + + assert_ok!(LimitOrders::::collect_fees( + &buys, + vec![(fee_recipient(), 80u64)], + &pallet_acct + )); + + let tao_transfers = MockSwap::tao_transfers(); + assert_eq!(tao_transfers.len(), 1, "single transfer to fee_recipient"); + let (from, to, amount) = &tao_transfers[0]; + assert_eq!(from, &pallet_acct, "fee comes from pallet account"); + assert_eq!(to, &fee_recipient(), "fee goes to fee_recipient"); + assert_eq!(*amount, 280u64, "total fee = 200 (buy) + 80 (sell)"); + }); +} + +#[test] +fn collect_fees_no_transfer_when_zero_fees() { + new_test_ext().execute_with(|| { + // No buy fees, no sell fee. + let hotkey = AccountKeyring::Dave.to_account_id(); + let buys = bounded_buy_entries(vec![make_buy_entry( + H256::repeat_byte(22), + alice(), + hotkey, + 1_000, + 1_000, + Perbill::zero(), + fee_recipient(), + )]); + let pallet_acct = PalletHotkeyAccount::get(); + + assert_ok!(LimitOrders::::collect_fees( + &buys, + vec![], + &pallet_acct + )); + + let tao_transfers = MockSwap::tao_transfers(); + assert_eq!(tao_transfers.len(), 0, "no transfer when total fee is zero"); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// is_order_valid +// ───────────────────────────────────────────────────────────────────────────── + +use crate::Error; +use codec::Encode; +use sp_core::Pair; +use sp_runtime::MultiSignature; +use subtensor_swap_interface::OrderSwapInterface; + +fn make_valid_signed_order() -> (crate::SignedOrder, sp_core::H256) { + let keyring = AccountKeyring::Alice; + let order = crate::VersionedOrder::V1(crate::Order { + signer: keyring.to_account_id(), + hotkey: AccountKeyring::Bob.to_account_id(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: u64::MAX, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + let id = H256(sp_io::hashing::blake2_256(&order.encode())); + let sig = keyring.pair().sign(&order.encode()); + let signed = crate::SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + }; + (signed, id) +} + +#[test] +fn is_order_valid_returns_ok_for_well_formed_order() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + let (signed, id) = make_valid_signed_order(); + let price = MockSwap::current_alpha_price(netuid()); + assert_ok!(LimitOrders::::is_order_valid( + &signed, + id, + 1_000_000, + price, + &bob() + )); + }); +} + +#[test] +fn is_order_valid_invalid_signature_returns_error() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + let (mut signed, id) = make_valid_signed_order(); + // Replace with a signature from a different key. + let wrong_sig = AccountKeyring::Bob.pair().sign(&signed.order.encode()); + signed.signature = MultiSignature::Sr25519(wrong_sig); + let price = MockSwap::current_alpha_price(netuid()); + assert_noop!( + LimitOrders::::is_order_valid(&signed, id, 1_000_000, price, &bob()), + Error::::InvalidSignature + ); + }); +} + +#[test] +fn is_order_valid_non_sr25519_signature_returns_error() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + let (mut signed, id) = make_valid_signed_order(); + let ed_pair = sp_core::ed25519::Pair::from_legacy_string("//Alice", None); + let ed_sig = ed_pair.sign(&signed.order.encode()); + signed.signature = MultiSignature::Ed25519(ed_sig); + let price = MockSwap::current_alpha_price(netuid()); + assert_noop!( + LimitOrders::::is_order_valid(&signed, id, 1_000_000, price, &bob()), + Error::::InvalidSignature + ); + }); +} + +#[test] +fn is_order_valid_already_processed_returns_error() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + let (signed, id) = make_valid_signed_order(); + Orders::::insert(id, crate::OrderStatus::Fulfilled); + let price = MockSwap::current_alpha_price(netuid()); + assert_noop!( + LimitOrders::::is_order_valid(&signed, id, 1_000_000, price, &bob()), + Error::::OrderAlreadyProcessed + ); + }); +} + +#[test] +fn is_order_valid_expired_order_returns_error() { + new_test_ext().execute_with(|| { + MockSwap::set_price(1.0); + let (signed, _id) = make_valid_signed_order(); + // now_ms (2_000_001) > expiry (u64::MAX is fine, so use a low expiry order). + // Re-build a signed order with a past expiry. + let keyring = AccountKeyring::Alice; + let order = crate::VersionedOrder::V1(crate::Order { + expiry: 500_000, + ..signed.order.inner().clone() + }); + let id2 = H256(sp_io::hashing::blake2_256(&order.encode())); + let sig = keyring.pair().sign(&order.encode()); + let signed2 = crate::SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + }; + let price = MockSwap::current_alpha_price(netuid()); + assert_noop!( + LimitOrders::::is_order_valid(&signed2, id2, 1_000_000, price, &bob()), + Error::::OrderExpired + ); + }); +} + +#[test] +fn is_order_valid_price_condition_not_met_returns_error() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + // Price 5.0, scaled = 5_000_000_000 > limit_price 2_000_000_000 (2.0 in ×10⁹) → LimitBuy condition (scaled ≤ limit) not met. + MockSwap::set_price(5.0); + let keyring = AccountKeyring::Alice; + let order = crate::VersionedOrder::V1(crate::Order { + signer: keyring.to_account_id(), + hotkey: AccountKeyring::Bob.to_account_id(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: 2_000_000_000, // 2.0 in ×10⁹ scale + expiry: u64::MAX, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + let id = H256(sp_io::hashing::blake2_256(&order.encode())); + let sig = keyring.pair().sign(&order.encode()); + let signed = crate::SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + }; + let price = MockSwap::current_alpha_price(netuid()); + assert_noop!( + LimitOrders::::is_order_valid(&signed, id, 1_000_000, price, &bob()), + Error::::PriceConditionNotMet + ); + }); +} + +#[test] +fn is_order_valid_wrong_chain_id_returns_error() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + let keyring = AccountKeyring::Alice; + // Build an order with a chain_id that doesn't match the mock config (945). + let order = crate::VersionedOrder::V1(crate::Order { + chain_id: 9999, + ..make_valid_signed_order().0.order.inner().clone() + }); + let id = H256(sp_io::hashing::blake2_256(&order.encode())); + let sig = keyring.pair().sign(&order.encode()); + let signed = crate::SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + }; + let price = MockSwap::current_alpha_price(netuid()); + assert_noop!( + LimitOrders::::is_order_valid(&signed, id, 1_000_000, price, &bob()), + Error::::ChainIdMismatch + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// compute_order_status +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn compute_order_status_no_partial_fill_returns_fulfilled() { + new_test_ext().execute_with(|| { + let id = H256::repeat_byte(1); + // No existing state, no partial fill → Fulfilled immediately. + let status = LimitOrders::::compute_order_status(id, None, 1_000); + assert_eq!(status, OrderStatus::Fulfilled); + }); +} + +#[test] +fn compute_order_status_partial_fill_below_total_returns_partially_filled() { + new_test_ext().execute_with(|| { + let id = H256::repeat_byte(2); + // First partial fill of 400 on a 1000-unit order → PartiallyFilled(400). + let status = LimitOrders::::compute_order_status(id, Some(400), 1_000); + assert_eq!(status, OrderStatus::PartiallyFilled(400)); + }); +} + +#[test] +fn compute_order_status_partial_fill_exact_total_returns_fulfilled() { + new_test_ext().execute_with(|| { + let id = H256::repeat_byte(3); + // Single partial fill that equals the full order amount → Fulfilled. + let status = LimitOrders::::compute_order_status(id, Some(1_000), 1_000); + assert_eq!(status, OrderStatus::Fulfilled); + }); +} + +#[test] +fn compute_order_status_accumulates_previous_partial_fill() { + new_test_ext().execute_with(|| { + let id = H256::repeat_byte(4); + // Pre-seed storage as if a prior partial fill of 300 already happened. + Orders::::insert(id, OrderStatus::PartiallyFilled(300)); + + // Second fill of 400 → 300 + 400 = 700, still below 1000. + let status = LimitOrders::::compute_order_status(id, Some(400), 1_000); + assert_eq!(status, OrderStatus::PartiallyFilled(700)); + }); +} + +#[test] +fn compute_order_status_completes_order_when_accumulated_total_reaches_amount() { + new_test_ext().execute_with(|| { + let id = H256::repeat_byte(5); + Orders::::insert(id, OrderStatus::PartiallyFilled(600)); + + // Fill the remaining 400 → 600 + 400 = 1000 = order_amount → Fulfilled. + let status = LimitOrders::::compute_order_status(id, Some(400), 1_000); + assert_eq!(status, OrderStatus::Fulfilled); + }); +} + +#[test] +fn compute_order_status_ignores_fulfilled_storage_when_no_partial_fill() { + new_test_ext().execute_with(|| { + let id = H256::repeat_byte(6); + // If somehow called with no partial_fill regardless of what's in storage + // (should not happen in practice) it still returns Fulfilled. + Orders::::insert(id, OrderStatus::PartiallyFilled(500)); + let status = LimitOrders::::compute_order_status(id, None, 1_000); + assert_eq!(status, OrderStatus::Fulfilled); + }); +} diff --git a/pallets/limit-orders/src/tests/extrinsics.rs b/pallets/limit-orders/src/tests/extrinsics.rs new file mode 100644 index 0000000000..43a85a1db1 --- /dev/null +++ b/pallets/limit-orders/src/tests/extrinsics.rs @@ -0,0 +1,3470 @@ +#![allow(clippy::indexing_slicing)] +//! Integration tests for `pallet-limit-orders` extrinsics. +//! +//! Tests go through the full dispatch path: origin enforcement, storage changes, +//! and event emission are all verified. SwapInterface calls are handled by +//! `MockSwap`, which records calls and maintains in-memory balance ledgers. + +use codec::Encode; +use frame_support::{BoundedVec, assert_noop, assert_ok}; +use sp_core::Pair; +use sp_keyring::Sr25519Keyring as AccountKeyring; +use sp_runtime::{DispatchError, Perbill}; +use subtensor_runtime_common::NetUid; + +use crate::{ + Error, Order, OrderSide, OrderStatus, OrderType, Orders, VersionedOrder, pallet::Event, +}; + +type LimitOrders = crate::pallet::Pallet; + +use super::mock::*; + +/// Check that a specific pallet event was emitted. +fn assert_event(event: Event) { + assert!( + System::events() + .iter() + .any(|r| r.event == RuntimeEvent::LimitOrders(event.clone())), + "expected event not found: {event:?}", + ); +} + +// ───────────────────────────────────────────────────────────────────────────── +// cancel_order +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn cancel_order_signer_can_cancel() { + new_test_ext().execute_with(|| { + let order = VersionedOrder::V1(Order { + signer: alice(), + hotkey: bob(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: FAR_FUTURE, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + let id = order_id(&order); + + assert_ok!(LimitOrders::cancel_order( + RuntimeOrigin::signed(alice()), + order + )); + assert_eq!(Orders::::get(id), Some(OrderStatus::Cancelled)); + assert_event(Event::OrderCancelled { + order_id: id, + signer: alice(), + }); + }); +} + +#[test] +fn cancel_order_non_signer_rejected() { + new_test_ext().execute_with(|| { + let order = VersionedOrder::V1(Order { + signer: alice(), + hotkey: bob(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: FAR_FUTURE, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + // Bob tries to cancel Alice's order. + assert_noop!( + LimitOrders::cancel_order(RuntimeOrigin::signed(bob()), order), + Error::::Unauthorized + ); + }); +} + +#[test] +fn cancel_order_already_cancelled_rejected() { + new_test_ext().execute_with(|| { + let order = VersionedOrder::V1(Order { + signer: alice(), + hotkey: bob(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: FAR_FUTURE, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + let id = order_id(&order); + Orders::::insert(id, OrderStatus::Cancelled); + + assert_noop!( + LimitOrders::cancel_order(RuntimeOrigin::signed(alice()), order), + Error::::OrderAlreadyProcessed + ); + }); +} + +#[test] +fn cancel_order_already_fulfilled_rejected() { + new_test_ext().execute_with(|| { + let order = VersionedOrder::V1(Order { + signer: alice(), + hotkey: bob(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: FAR_FUTURE, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + let id = order_id(&order); + Orders::::insert(id, OrderStatus::Fulfilled); + + assert_noop!( + LimitOrders::cancel_order(RuntimeOrigin::signed(alice()), order), + Error::::OrderAlreadyProcessed + ); + }); +} + +#[test] +fn cancel_order_unsigned_rejected() { + new_test_ext().execute_with(|| { + let order = VersionedOrder::V1(Order { + signer: alice(), + hotkey: bob(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: FAR_FUTURE, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + assert_noop!( + LimitOrders::cancel_order(RuntimeOrigin::none(), order), + DispatchError::BadOrigin + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// execute_orders +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_orders_buy_order_fulfilled() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + // Price = 1.0 ≤ limit = 2.0 → condition met. + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + 2_000_000_000, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + assert_event(Event::OrderExecuted { + order_id: id, + signer: alice(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount_in: 1_000, + amount_out: 0, + }); + }); +} + +#[test] +fn execute_orders_sell_order_fulfilled() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(2.0); + // Price = 2.0, scaled = 2_000_000_000 ≥ limit = 1_000_000_000 → condition met. + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 500, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + assert_event(Event::OrderExecuted { + order_id: id, + signer: alice(), + netuid: netuid(), + order_type: OrderType::TakeProfit, + amount_in: 500, + amount_out: 0, + }); + }); +} + +#[test] +fn execute_orders_stop_loss_order_fulfilled() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(0.5); + // Price = 0.5, scaled = 500_000_000 ≤ limit = 1_000_000_000 → condition met. + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::StopLoss, + 500, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + assert_event(Event::OrderExecuted { + order_id: id, + signer: alice(), + netuid: netuid(), + order_type: OrderType::StopLoss, + amount_in: 500, + amount_out: 0, + }); + }); +} + +#[test] +fn execute_orders_stop_loss_price_not_met_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(2.0); // price 2.0, scaled=2_000_000_000 > limit 1_000_000_000 → stop loss condition not met + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::StopLoss, + 500, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert!(Orders::::get(id).is_none()); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::PriceConditionNotMet.into(), + }); + }); +} + +#[test] +fn execute_orders_expired_order_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(2_000_001); // now > expiry + MockSwap::set_price(1.0); + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + 2_000_000, // expiry in the past + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Skipped — storage untouched. + assert!(Orders::::get(id).is_none()); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::OrderExpired.into(), + }); + }); +} + +#[test] +fn execute_orders_price_not_met_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(5.0); // price 5.0, scaled=5_000_000_000 > limit 2_000_000_000 → buy condition not met + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + 2_000_000_000, // 2.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert!(Orders::::get(id).is_none()); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::PriceConditionNotMet.into(), + }); + }); +} + +// Regression tests: with the ×10⁹ scale fix, sub-unity prices can be meaningfully +// expressed as limit_price values. A price of 0.5 TAO/alpha is represented as +// 500_000_000 in ×10⁹ scale, enabling fine-grained TakeProfit thresholds below 1.0. +#[test] +fn take_profit_sub_unity_price_executes_when_limit_met() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + // Market price = 0.5 TAO/alpha → scaled = 500_000_000. + MockSwap::set_price(0.5); + + // limit_price = 400_000_000 (0.4 in ×10⁹ scale). + // TakeProfit condition: scaled_price (500_000_000) >= limit_price (400_000_000) ✓ + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 500, + 400_000_000, // 0.4 in ×10⁹ scale — below current price of 0.5 + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Executes: 500_000_000 >= 400_000_000 → condition met. + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + }); +} + +#[test] +fn take_profit_sub_unity_price_skipped_when_limit_not_met() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + // Market price = 0.5 TAO/alpha → scaled = 500_000_000. + MockSwap::set_price(0.5); + + // limit_price = 600_000_000 (0.6 in ×10⁹ scale). + // TakeProfit condition: scaled_price (500_000_000) >= limit_price (600_000_000) → FALSE. + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 500, + 600_000_000, // 0.6 in ×10⁹ scale — above current price of 0.5 + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Skipped: 500_000_000 >= 600_000_000 is false. + assert!(Orders::::get(id).is_none()); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::PriceConditionNotMet.into(), + }); + }); +} + +#[test] +fn execute_orders_already_processed_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + Orders::::insert(id, OrderStatus::Fulfilled); + + // Should succeed (batch-level) but skip this order silently. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + // Still Fulfilled (not changed). + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::OrderAlreadyProcessed.into(), + }); + }); +} + +#[test] +fn execute_orders_mixed_batch_valid_and_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let valid = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let expired = make_signed_order( + AccountKeyring::Bob, + alice(), + netuid(), + OrderType::LimitBuy, + 500, + u64::MAX, + 500_000, // already expired + Perbill::zero(), + fee_recipient(), + None, + ); + let valid_id = order_id(&valid.order); + let expired_id = order_id(&expired.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![valid, expired]), + false, + )); + + assert_eq!(Orders::::get(valid_id), Some(OrderStatus::Fulfilled)); + assert_event(Event::OrderSkipped { + order_id: expired_id, + reason: Error::::OrderExpired.into(), + }); + }); +} + +#[test] +fn execute_orders_unsigned_rejected() { + new_test_ext().execute_with(|| { + assert_noop!( + LimitOrders::execute_orders(RuntimeOrigin::none(), bounded(vec![]), false), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn execute_orders_buy_with_fee_charges_fee() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + // fee_rate = 1% (10_000_000 parts-per-billion), recipient = fee_recipient(). + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + MockSwap::set_tao_balance(alice(), 1_000); + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // One buy_alpha call for the net amount (990 TAO after 1% fee). + let buys: Vec<_> = MockSwap::log() + .into_iter() + .filter_map(|c| { + if let super::mock::SwapCall::BuyAlpha { tao, .. } = c { + Some(tao) + } else { + None + } + }) + .collect(); + assert_eq!(buys, vec![990], "main swap must use 990 TAO after 1% fee"); + + // Fee (10 TAO) forwarded directly to fee_recipient via transfer_tao. + assert_eq!(MockSwap::tao_balance(&fee_recipient()), 10); + }); +} + +#[test] +fn execute_orders_sell_with_fee_charges_fee() { + new_test_ext().execute_with(|| { + // fee = 1% (10_000_000 ppb). + // Alice sells 1_000 alpha; pool returns 800 TAO. + // fee_tao = 1% of 800 = 8 TAO, forwarded to fee_recipient via transfer_tao. + // Alice keeps 792 TAO. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_sell_tao_return(800); + MockSwap::set_alpha_balance(alice(), bob(), netuid(), 1_000); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Full 1_000 alpha sold (no alpha deducted for fee). + let sells: Vec<_> = MockSwap::log() + .into_iter() + .filter_map(|c| { + if let super::mock::SwapCall::SellAlpha { alpha, .. } = c { + Some(alpha) + } else { + None + } + }) + .collect(); + assert_eq!(sells, vec![1_000], "full alpha amount must be sold"); + + // fee_recipient received 8 TAO (1% of 800). + assert_eq!(MockSwap::tao_balance(&fee_recipient()), 8); + // Alice kept the remaining 792 TAO. + assert_eq!(MockSwap::tao_balance(&alice()), 792); + }); +} + +#[test] +fn execute_orders_empty_batch_returns_ok() { + new_test_ext().execute_with(|| { + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![]), + false, + )); + }); +} + +#[test] +fn execute_orders_fee_transfer_failure_skips_order() { + new_test_ext().execute_with(|| { + // When the fee transfer fails the entire order is rolled back and emits OrderSkipped. + // This prevents users from exploiting a tight balance to execute swaps fee-free. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(500); + MockSwap::set_tao_balance(alice(), 10_000); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + + FAIL_FEE_TRANSFER.with(|f| *f.borrow_mut() = true); + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed.clone()]), + false, + )); + FAIL_FEE_TRANSFER.with(|f| *f.borrow_mut() = false); + + // Order was skipped — not stored as Fulfilled. + let id = crate::tests::mock::order_id(&signed.order); + assert!(Orders::::get(id).is_none()); + + // OrderSkipped was emitted with the fee-transfer error as the reason. + assert_event(Event::OrderSkipped { + order_id: id, + reason: DispatchError::CannotLookup, + }); + + // fee_recipient received nothing. + assert_eq!(MockSwap::tao_balance(&fee_recipient()), 0); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// execute_orders — silent-skip behaviour +// ───────────────────────────────────────────────────────────────────────────── + +mod execute_orders_skip_invalid { + use super::*; + + /// A single expired order is silently skipped: the call returns `Ok` and + /// nothing is written to the `Orders` storage map. + #[test] + fn execute_orders_skips_expired_order() { + new_test_ext().execute_with(|| { + MockTime::set(2_000_001); // now > expiry + MockSwap::set_price(1.0); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + 2_000_000, // expiry in the past + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Skipped — storage untouched. + assert!(Orders::::get(id).is_none()); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::OrderExpired.into(), + }); + }); + } + + /// A LimitBuy with `limit_price = 0` (price ceiling below current price) + /// is silently skipped: the call returns `Ok` and nothing is written to + /// the `Orders` storage map. + #[test] + fn execute_orders_skips_price_condition_not_met() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(5.0); // price 5.0 > limit 0 → buy condition not met + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + 0, // price ceiling of 0 — never satisfied at price 5.0 + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Skipped — storage untouched. + assert!(Orders::::get(id).is_none()); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::PriceConditionNotMet.into(), + }); + }); + } + + /// A batch containing one valid order and one expired order: the call + /// returns `Ok`, the valid order is stored as `Fulfilled`, and the expired + /// order is NOT written to storage. + #[test] + fn execute_orders_valid_and_invalid_mixed() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let valid = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let expired = make_signed_order( + AccountKeyring::Bob, + alice(), + netuid(), + OrderType::LimitBuy, + 500, + u64::MAX, + 500_000, // already expired + Perbill::zero(), + fee_recipient(), + None, + ); + let valid_id = order_id(&valid.order); + let expired_id = order_id(&expired.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![valid, expired]), + false, + )); + + // Valid order executed successfully. + assert_eq!(Orders::::get(valid_id), Some(OrderStatus::Fulfilled)); + // Expired order silently skipped — not written to storage. + assert!(Orders::::get(expired_id).is_none()); + assert_event(Event::OrderSkipped { + order_id: expired_id, + reason: Error::::OrderExpired.into(), + }); + }); + } + + /// With `should_fail = true` a single expired order is NOT silently skipped: + /// the whole call fails with `OrderExpired` and storage stays untouched. + #[test] + fn execute_orders_should_fail_expired_order_reverts() { + new_test_ext().execute_with(|| { + MockTime::set(2_000_001); // now > expiry + MockSwap::set_price(1.0); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + 2_000_000, // expiry in the past + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + // all-or-nothing: the failing order makes the whole call return Err + // and assert_noop! confirms storage is unchanged. + assert_noop!( + LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + true, + ), + Error::::OrderExpired + ); + + assert!(Orders::::get(id).is_none()); + }); + } + + /// With `should_fail = true` a batch containing a VALID order followed by an + /// INVALID (expired) order reverts entirely: the valid order's effects are + /// rolled back, so it is NOT recorded as `Fulfilled` and the relayer's TAO + /// is not consumed. Contrast `execute_orders_valid_and_invalid_mixed`, where + /// the same batch with `should_fail = false` keeps the valid order. + #[test] + fn execute_orders_should_fail_valid_then_invalid_reverts_whole_batch() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let valid = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let expired = make_signed_order( + AccountKeyring::Bob, + alice(), + netuid(), + OrderType::LimitBuy, + 500, + u64::MAX, + 500_000, // already expired + Perbill::zero(), + fee_recipient(), + None, + ); + let valid_id = order_id(&valid.order); + let expired_id = order_id(&expired.order); + + // The expired order is the second in the batch; with should_fail = true + // its failure reverts the already-executed valid order too. + assert_noop!( + LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![valid, expired]), + true, + ), + Error::::OrderExpired + ); + + // Neither order survived: the valid order's Fulfilled status was rolled back. + assert!(Orders::::get(valid_id).is_none()); + assert!(Orders::::get(expired_id).is_none()); + }); + } + + /// With `should_fail = true` a price-condition-not-met order hard-fails the + /// whole call with `PriceConditionNotMet`, mirroring `execute_batched_orders` + /// rather than the best-effort skip path. + #[test] + fn execute_orders_should_fail_price_condition_not_met_reverts() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(5.0); // price 5.0 > limit 0 → buy condition not met + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + 0, // price ceiling of 0 — never satisfied at price 5.0 + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + + assert_noop!( + LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + true, + ), + Error::::PriceConditionNotMet + ); + + assert!(Orders::::get(id).is_none()); + }); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// execute_batched_orders +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_batched_orders_unsigned_rejected() { + new_test_ext().execute_with(|| { + assert_noop!( + LimitOrders::execute_batched_orders(RuntimeOrigin::none(), netuid(), bounded(vec![])), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn execute_batched_orders_all_invalid_fails() { + new_test_ext().execute_with(|| { + // An expired order causes the whole batch to fail. + MockTime::set(2_000_001); // all expired + let expired = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + 1_000_000, + Perbill::zero(), + fee_recipient(), + None, + ); + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![expired]), + ), + Error::::OrderExpired + ); + }); +} + +#[test] +fn execute_batched_orders_fails_for_wrong_netuid() { + new_test_ext().execute_with(|| { + // An order whose netuid does not match the batch netuid must cause the batch to fail. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(100); + + let wrong_net = make_signed_order( + AccountKeyring::Alice, + bob(), + NetUid::from(99u16), // wrong netuid + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), // batch targets netuid 1 + bounded(vec![wrong_net]), + ), + Error::::OrderNetUidMismatch + ); + }); +} + +#[test] +fn execute_batched_orders_price_condition_not_met_fails_entire_batch() { + new_test_ext().execute_with(|| { + // Price condition not met is a hard-fail in execute_batched_orders — + // unlike execute_orders where it silently skips the order. + MockTime::set(1_000_000); + MockSwap::set_price(100.0); // current price = 100, scaled = 100_000_000_000 + + // LimitBuy requires scaled_price <= limit_price; with limit_price=1_000_000_000 (1.0) this fails. + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + 1_000_000_000, // 1.0 in ×10⁹ scale, far below scaled price of 100_000_000_000 + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order]) + ), + Error::::PriceConditionNotMet + ); + }); +} + +#[test] +fn execute_batched_orders_buy_only_fulfills_orders_and_distributes_alpha() { + new_test_ext().execute_with(|| { + // Setup: + // Alice buys 600 TAO, Bob buys 400 TAO (total 1000 TAO net, fee=0). + // Pool returns 500 alpha (MOCK_BUY_ALPHA_RETURN). + // No sellers → total_alpha = 500. + // Pro-rata: Alice 500*600/1000=300, Bob 500*400/1000=200. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(500); + MockSwap::set_tao_balance(alice(), 600); + MockSwap::set_tao_balance(bob(), 400); + + let alice_order = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 600, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let bob_order = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 400, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let alice_id = order_id(&alice_order.order); + let bob_id = order_id(&bob_order.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_order, bob_order]), + )); + + // Both orders fulfilled. + assert_eq!(Orders::::get(alice_id), Some(OrderStatus::Fulfilled)); + assert_eq!(Orders::::get(bob_id), Some(OrderStatus::Fulfilled)); + + // Alpha distributed pro-rata. + assert_eq!(MockSwap::alpha_balance(&alice(), &dave(), netuid()), 300); + assert_eq!(MockSwap::alpha_balance(&bob(), &dave(), netuid()), 200); + + // Summary event. + assert_event(Event::GroupExecutionSummary { + netuid: netuid(), + net_side: OrderSide::Buy, + net_amount: 1_000, + actual_out: 500, + executed_count: 2, + }); + }); +} + +/// Regression test for the zero-share batch fund-loss bug. +/// +/// Bug (pre-fix): `collect_assets` debited every buyer's full TAO input up front, +/// then `distribute_alpha_pro_rata` floored each buyer's alpha share. When a +/// buyer's `share = floor(total_alpha * net / total_buy_net)` floored to 0, the +/// old code silently SKIPPED the alpha transfer (`if share > 0 { .. }`) yet STILL +/// marked the order `Fulfilled`. The victim therefore paid full TAO, received zero +/// alpha, and the order was permanently closed. +/// +/// Fix: `distribute_alpha_pro_rata` now `ensure!(share > 0, ZeroShareInBatch)`, +/// hard-failing the whole `execute_batched_orders` call. In production FRAME's +/// per-dispatch storage layer then rolls back `collect_assets` and the pool swap, +/// so no signer is debited and no order is stored. +/// +/// `assert_noop!` asserts both the error AND that no on-chain storage mutation +/// persisted — i.e. neither order is written, so neither is marked `Fulfilled`. +/// Against the old code this call returned `Ok` and wrote `Fulfilled`, so the +/// `assert_noop!` (storage-root-unchanged) would have FAILED. +/// +/// NOTE: we deliberately do NOT assert the victim's TAO balance was refunded. +/// `MockSwap` keeps balances in a `thread_local!` map that lives OUTSIDE the +/// substrate storage overlay, so `collect_assets`' debit is not transactional in +/// the mock and is not rolled back here. The balance refund is a property of the +/// real `frame_system` balances under the dispatch storage layer (exercised by the +/// L2/integration PoC), not something this mock can model. +#[test] +fn execute_batched_orders_zero_share_buyer_hard_fails() { + new_test_ext().execute_with(|| { + // Buy-only batch, price 1.0, pool alpha output pinned to 1000. + // big buyer net = 1_000_000 TAO + // victim buyer net = 1 TAO + // total_buy_net = 1_000_001 + // total_alpha = actual_out(1000) + total_sell_net(0) = 1000 + // victim share = floor(1000 * 1 / 1_000_001) = 0 → ZeroShareInBatch + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(1000); + // Distinct signer coldkeys; each buyer must be able to cover its own input. + MockSwap::set_tao_balance(alice(), 1_000_000); // big buyer (Alice) + MockSwap::set_tao_balance(bob(), 1); // victim (Bob) + + let big_buyer = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let victim = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 1, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let big_id = order_id(&big_buyer.order); + let victim_id = order_id(&victim.order); + + // The whole batch must hard-fail with ZeroShareInBatch. assert_noop! also asserts + // the storage root is unchanged, so neither order was written/marked Fulfilled — + // the core of the fix. (Old code: returned Ok and wrote Fulfilled → this fails.) + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![big_buyer, victim]), + ), + Error::::ZeroShareInBatch + ); + + // Explicit, redundant-with-assert_noop! statement of intent: no order is terminal. + assert_eq!(Orders::::get(victim_id), None); + assert_eq!(Orders::::get(big_id), None); + }); +} + +/// Guards against over-restriction: the `ZeroShareInBatch` fix must NOT reject a +/// legitimate multi-buyer batch where every buyer's floored share is at least 1. +#[test] +fn execute_batched_orders_all_nonzero_shares_still_succeeds() { + new_test_ext().execute_with(|| { + // Buy-only, price 1.0, pool alpha output = 1000, comparable buyer nets so + // neither share floors to zero: + // Alice net = 600, Bob net = 400, total_buy_net = 1000, total_alpha = 1000 + // Alice share = floor(1000 * 600 / 1000) = 600 + // Bob share = floor(1000 * 400 / 1000) = 400 + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(1000); + MockSwap::set_tao_balance(alice(), 600); + MockSwap::set_tao_balance(bob(), 400); + + let alice_order = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 600, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let bob_order = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 400, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let alice_id = order_id(&alice_order.order); + let bob_id = order_id(&bob_order.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_order, bob_order]), + )); + + // Both orders fulfilled and both buyers received non-zero alpha. + assert_eq!(Orders::::get(alice_id), Some(OrderStatus::Fulfilled)); + assert_eq!(Orders::::get(bob_id), Some(OrderStatus::Fulfilled)); + assert_eq!(MockSwap::alpha_balance(&alice(), &dave(), netuid()), 600); + assert_eq!(MockSwap::alpha_balance(&bob(), &dave(), netuid()), 400); + assert!(MockSwap::alpha_balance(&alice(), &dave(), netuid()) > 0); + assert!(MockSwap::alpha_balance(&bob(), &dave(), netuid()) > 0); + }); +} + +/// Sell-side analogue of the zero-share regression. A seller whose `net_share` +/// floors to 0 in `distribute_tao_pro_rata` must hard-fail the whole batch with +/// `ZeroShareInBatch`. `assert_noop!` proves no on-chain storage mutation persisted +/// (neither order is written/marked Fulfilled). As in the buy-side test, the +/// seller's collected alpha is not refunded *in the mock* (MockSwap balances are +/// thread_local, outside the storage overlay); the refund is a real-balance +/// property under the dispatch storage layer, not modelled here. +#[test] +fn execute_batched_orders_zero_share_seller_hard_fails() { + new_test_ext().execute_with(|| { + // Sell-only batch, price 1.0, pool TAO output pinned to 1000. + // big seller alpha = 1_000_000 → sell_tao_equiv 1_000_000 + // victim seller alpha = 1 → sell_tao_equiv 1 + // total_sell_tao_equiv = 1_000_001 + // total_tao = actual_out(1000) + total_buy_net(0) = 1000 + // victim gross_share = floor(1000 * 1 / 1_000_001) = 0 + // net_share = 0 → ZeroShareInBatch + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_sell_tao_return(1000); + MockSwap::set_alpha_balance(alice(), dave(), netuid(), 1_000_000); // big seller + MockSwap::set_alpha_balance(bob(), dave(), netuid(), 1); // victim seller + + let big_seller = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::TakeProfit, + 1_000_000, + 0, // limit=0 → accept any price + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let victim = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 1, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let big_id = order_id(&big_seller.order); + let victim_id = order_id(&victim.order); + + // The whole batch must hard-fail with ZeroShareInBatch; assert_noop! also asserts + // the storage root is unchanged, so neither order was written/marked Fulfilled. + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![big_seller, victim]), + ), + Error::::ZeroShareInBatch + ); + + // Explicit, redundant-with-assert_noop! statement of intent: no order is terminal. + assert_eq!(Orders::::get(victim_id), None); + assert_eq!(Orders::::get(big_id), None); + }); +} + +#[test] +fn execute_batched_orders_sell_only_fulfills_orders_and_distributes_tao() { + new_test_ext().execute_with(|| { + // Setup: + // Alice sells 300 alpha, Bob sells 200 alpha (total 500 alpha, fee=0). + // Price = 2.0 → sell_tao_equiv: Alice 600, Bob 400, total 1000. + // Pool returns 800 TAO (MOCK_SELL_TAO_RETURN) for the net 500 alpha. + // No buyers → total_tao = 800 + 0 = 800. + // Pro-rata: Alice 800*600/1000=480, Bob 800*400/1000=320. + MockTime::set(1_000_000); + MockSwap::set_price(2.0); + MockSwap::set_sell_tao_return(800); + MockSwap::set_alpha_balance(alice(), dave(), netuid(), 300); + MockSwap::set_alpha_balance(bob(), dave(), netuid(), 200); + + let alice_order = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::TakeProfit, + 300, + 0, + FAR_FUTURE, // limit=0 → accept any price + Perbill::zero(), + fee_recipient(), + None, + ); + let bob_order = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 200, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let alice_id = order_id(&alice_order.order); + let bob_id = order_id(&bob_order.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_order, bob_order]), + )); + + assert_eq!(Orders::::get(alice_id), Some(OrderStatus::Fulfilled)); + assert_eq!(Orders::::get(bob_id), Some(OrderStatus::Fulfilled)); + + // TAO distributed pro-rata. + assert_eq!(MockSwap::tao_balance(&alice()), 480); + assert_eq!(MockSwap::tao_balance(&bob()), 320); + + assert_event(Event::GroupExecutionSummary { + netuid: netuid(), + net_side: OrderSide::Sell, + net_amount: 500, + actual_out: 800, + executed_count: 2, + }); + }); +} + +#[test] +fn execute_batched_orders_buy_dominant_mixed() { + new_test_ext().execute_with(|| { + // Setup (fee=0, price=2.0 TAO/alpha): + // Buyers: Alice 1000 TAO, Bob 600 TAO → total_buy_net = 1600. + // Sellers: Charlie 200 alpha → sell_tao_equiv = 400 TAO. + // Net (buy-dominant): 1600 - 400 = 1200 TAO goes to pool. + // Pool returns 300 alpha (MOCK_BUY_ALPHA_RETURN). + // total_alpha for buyers = 300 (pool) + 200 (seller passthrough) = 500. + // Pro-rata buyers (by buy_net TAO): + // Alice: 500 * 1000/1600 = 312 alpha + // Bob: 500 * 600/1600 = 187 alpha + // (dust = 1 alpha stays in pallet) + // Sellers (buy-dominant branch): total_tao = total_sell_tao_equiv = 400. + // Charlie: 400 * 400/400 = 400 TAO. + MockTime::set(1_000_000); + MockSwap::set_price(2.0); + MockSwap::set_buy_alpha_return(300); + MockSwap::set_tao_balance(alice(), 1_000); + MockSwap::set_tao_balance(bob(), 600); + MockSwap::set_alpha_balance(charlie(), dave(), netuid(), 200); + + let alice_buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let bob_buy = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 600, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let charlie_sell = make_signed_order( + AccountKeyring::Charlie, + dave(), + netuid(), + OrderType::TakeProfit, + 200, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(dave()), + netuid(), + bounded(vec![alice_buy, bob_buy, charlie_sell]), + )); + + assert_eq!(MockSwap::alpha_balance(&alice(), &dave(), netuid()), 312); + assert_eq!(MockSwap::alpha_balance(&bob(), &dave(), netuid()), 187); + assert_eq!(MockSwap::tao_balance(&charlie()), 400); + + assert_event(Event::GroupExecutionSummary { + netuid: netuid(), + net_side: OrderSide::Buy, + net_amount: 1_200, + actual_out: 300, + executed_count: 3, + }); + }); +} + +#[test] +fn execute_batched_orders_sell_dominant_mixed() { + new_test_ext().execute_with(|| { + // Setup (fee=0, price=2.0 TAO/alpha): + // Buyers: Alice 200 TAO → total_buy_net = 200. + // Sellers: Bob 300 alpha, Charlie 200 alpha → total_sell_net = 500. + // sell_tao_equiv: Bob 600, Charlie 400, total 1000. + // Net (sell-dominant): buy_alpha_equiv = 200/2 = 100 alpha; + // residual sell alpha = 500 - 100 = 400 alpha → pool returns 300 TAO. + // total_tao for sellers = 300 (pool) + 200 (buy passthrough) = 500 TAO. + // Pro-rata sellers (by sell_tao_equiv): + // Bob: 500 * 600/1000 = 300 TAO + // Charlie: 500 * 400/1000 = 200 TAO + // total_alpha for buyers = buy_net / price = 200/2 = 100 alpha. + // Alice: 100 * 200/200 = 100 alpha. + MockTime::set(1_000_000); + MockSwap::set_price(2.0); + MockSwap::set_sell_tao_return(300); + MockSwap::set_tao_balance(alice(), 200); + MockSwap::set_alpha_balance(bob(), dave(), netuid(), 300); + MockSwap::set_alpha_balance(charlie(), dave(), netuid(), 200); + + let alice_buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 200, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let bob_sell = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 300, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let charlie_sell = make_signed_order( + AccountKeyring::Charlie, + dave(), + netuid(), + OrderType::TakeProfit, + 200, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(dave()), + netuid(), + bounded(vec![alice_buy, bob_sell, charlie_sell]), + )); + + assert_eq!(MockSwap::alpha_balance(&alice(), &dave(), netuid()), 100); + assert_eq!(MockSwap::tao_balance(&bob()), 300); + assert_eq!(MockSwap::tao_balance(&charlie()), 200); + + assert_event(Event::GroupExecutionSummary { + netuid: netuid(), + net_side: OrderSide::Sell, + net_amount: 400, + actual_out: 300, + executed_count: 3, + }); + }); +} + +#[test] +fn execute_batched_orders_fee_forwarded_to_collector() { + new_test_ext().execute_with(|| { + // fee = 1% (10_000_000 ppb). + // Alice buys 1000 TAO: fee = 10, net = 990. + // Pool returns 500 alpha for 990 TAO. + // collect_fees transfers 10 TAO (buy fee) to fee_recipient. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(500); + + let alice_buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_buy]), + )); + + // Fee recipient received the buy-side fee. + assert_eq!(MockSwap::tao_balance(&fee_recipient()), 10); + }); +} + +#[test] +fn execute_batched_orders_fails_for_cancelled_order() { + new_test_ext().execute_with(|| { + // A cancelled order is already processed; including it in the batch must cause a hard failure. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(100); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&signed.order); + Orders::::insert(id, OrderStatus::Cancelled); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![signed]), + ), + Error::::OrderCancelled + ); + + // Still cancelled, not changed to Fulfilled. + assert_eq!(Orders::::get(id), Some(OrderStatus::Cancelled)); + }); +} + +#[test] +fn execute_batched_orders_fees_charged_on_both_sides_when_matched_internally() { + new_test_ext().execute_with(|| { + // fee = 1% (10_000_000 ppb), price = 1.0 TAO/alpha. + // + // Alice buys 1_000 TAO → buy fee = 10 TAO, net = 990 TAO. + // Bob sells 1_000 alpha → sell_tao_equiv = 1_000 TAO. + // + // sell-dominant: residual = 1_000 - 990 = 10 alpha sent to pool. + // Pool returns 9 TAO (mocked) for that residual. + // total_tao for sellers = 9 (pool) + 990 (buy passthrough) = 999. + // Bob gross_share = 999 * 1_000/1_000 = 999. + // Sell fee = mul_floor(1%, 999) = floor(9.99) = 9; Bob nets 990 TAO. + // fee_recipient total = buy_fee(10) + sell_fee(9) = 19 TAO. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_sell_tao_return(9); + MockSwap::set_tao_balance(alice(), 1_000); + MockSwap::set_alpha_balance(bob(), dave(), netuid(), 1_000); + + let alice_buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + let bob_sell = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_buy, bob_sell]), + )); + + // Both sides charged: fee_recipient gets buy fee (10) + sell fee (9) = 19. + assert_eq!(MockSwap::tao_balance(&fee_recipient()), 19); + // Bob receives 990 TAO after sell-side fee (999 gross - 9 fee). + assert_eq!(MockSwap::tao_balance(&bob()), 990); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// net_pool_swap – SwapReturnedZero errors +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_batched_orders_buy_zero_alpha_returns_error() { + new_test_ext().execute_with(|| { + // buy_alpha returns 0 alpha for a non-zero TAO input → SwapReturnedZero. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(0); // pool gives back nothing + MockSwap::set_tao_balance(alice(), 1_000); + + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order]), + ), + Error::::SwapReturnedZero + ); + }); +} + +#[test] +fn execute_batched_orders_sell_zero_tao_returns_error() { + new_test_ext().execute_with(|| { + // sell_alpha returns 0 TAO for a non-zero alpha input → SwapReturnedZero. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_sell_tao_return(0); // pool gives back nothing + MockSwap::set_alpha_balance(alice(), bob(), netuid(), 1_000); + + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order]), + ), + Error::::SwapReturnedZero + ); + }); +} + +#[test] +fn execute_batched_orders_sell_alpha_respects_swap_fail() { + new_test_ext().execute_with(|| { + // sell_alpha should propagate DispatchError when MOCK_SWAP_FAIL is set. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_swap_fail(true); + MockSwap::set_alpha_balance(alice(), bob(), netuid(), 1_000); + + let order = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order]), + ), + DispatchError::Other("pool error") + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// fee routing – multiple recipients +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_batched_orders_fees_routed_to_different_recipients() { + new_test_ext().execute_with(|| { + // Alice and Bob both buy; Alice's fee goes to charlie(), Bob's to dave(). + // fee = 1% for both orders. + // Alice buys 1_000 TAO: fee = 10 → charlie(). + // Bob buys 1_000 TAO: fee = 10 → dave(). + // Pool returns 900 alpha total for 1_980 TAO net. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(900); + MockSwap::set_tao_balance(alice(), 1_000); + MockSwap::set_tao_balance(bob(), 1_000); + + let alice_buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + charlie(), + None, + ); + let bob_buy = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + dave(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_buy, bob_buy]), + )); + + // Each recipient gets exactly their order's fee. + assert_eq!( + MockSwap::tao_balance(&charlie()), + 10, + "charlie gets Alice's fee" + ); + assert_eq!(MockSwap::tao_balance(&dave()), 10, "dave gets Bob's fee"); + }); +} + +#[test] +fn execute_batched_orders_fees_batched_for_shared_recipient() { + new_test_ext().execute_with(|| { + // Both Alice and Bob's fees go to the same recipient (charlie()). + // Expect a single combined transfer of 20 TAO to charlie(). + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(900); + MockSwap::set_tao_balance(alice(), 1_000); + MockSwap::set_tao_balance(bob(), 1_000); + + let alice_buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + charlie(), + None, + ); + let bob_buy = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + charlie(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_buy, bob_buy]), + )); + + // One combined transfer: charlie() receives 10 + 10 = 20 TAO. + let fee_transfers: Vec<_> = MockSwap::tao_transfers() + .into_iter() + .filter(|(_, to, _)| to == &charlie()) + .collect(); + assert_eq!( + fee_transfers.len(), + 1, + "single transfer to shared recipient" + ); + assert_eq!(fee_transfers[0].2, 20, "combined fee = 20 TAO"); + }); +} + +/// 4 orders split across 2 fee recipients. +/// +/// Orders: +/// Alice LimitBuy 1_000 TAO fee_recipient = ferdie (buy-fee collector) +/// Bob LimitBuy 1_000 TAO fee_recipient = ferdie (buy-fee collector) +/// Charlie TakeProfit 1_000 α fee_recipient = fee_recipient() (sell-fee collector) +/// Eve TakeProfit 1_000 α fee_recipient = fee_recipient() (sell-fee collector) +/// +/// Neither ferdie nor fee_recipient() are order signers, so every TAO transfer +/// to those accounts is exclusively a fee transfer — making the single-transfer +/// assertion unambiguous. +/// +/// At price 1.0 (1 TAO = 1 α), fee = 1%: +/// net buy TAO = (1_000 - 10) + (1_000 - 10) = 1_980 +/// sell α equiv = 2_000 TAO → sell-dominant, residual = 20 α → pool +/// pool returns 18 TAO for residual +/// total TAO for sellers = 18 + 1_980 = 1_998 +/// each seller gross_share = 1_998 * 1_000 / 2_000 = 999 +/// sell fee = mul_floor(1%, 999) = floor(9.99) = 9 TAO each +/// +/// Expected: +/// ferdie receives 10 (Alice) + 10 (Bob) = 20 TAO (1 transfer) +/// fee_recipient() receives 9 (Charlie) + 9 (Eve) = 18 TAO (1 transfer) +#[test] +fn execute_batched_orders_four_orders_two_fee_recipients() { + new_test_ext().execute_with(|| { + let ferdie = AccountKeyring::Ferdie.to_account_id(); + let eve = AccountKeyring::Eve.to_account_id(); + + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_sell_tao_return(18); + MockSwap::set_tao_balance(alice(), 1_000); + MockSwap::set_tao_balance(bob(), 1_000); + MockSwap::set_alpha_balance(charlie(), dave(), netuid(), 1_000); + MockSwap::set_alpha_balance(eve.clone(), dave(), netuid(), 1_000); + + let alice_buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + ferdie.clone(), + None, + ); + let bob_buy = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + ferdie.clone(), + None, + ); + let charlie_sell = make_signed_order( + AccountKeyring::Charlie, + dave(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + let eve_sell = make_signed_order( + AccountKeyring::Eve, + dave(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, + FAR_FUTURE, + Perbill::from_parts(10_000_000), // 1% + fee_recipient(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(alice()), + netuid(), + bounded(vec![alice_buy, bob_buy, charlie_sell, eve_sell]), + )); + + // ferdie collects Alice's and Bob's buy fees: 10 + 10 = 20 TAO in one transfer. + let ferdie_transfers: Vec<_> = MockSwap::tao_transfers() + .into_iter() + .filter(|(_, to, _)| to == &ferdie) + .collect(); + assert_eq!(ferdie_transfers.len(), 1, "single transfer to ferdie"); + assert_eq!( + ferdie_transfers[0].2, 20, + "ferdie receives 20 TAO in buy fees" + ); + + // fee_recipient() collects Charlie's and Eve's sell fees: 10 + 10 = 20 TAO in one transfer. + let fp_transfers: Vec<_> = MockSwap::tao_transfers() + .into_iter() + .filter(|(_, to, _)| to == &fee_recipient()) + .collect(); + assert_eq!(fp_transfers.len(), 1, "single transfer to fee_recipient"); + assert_eq!( + fp_transfers[0].2, 18, + "fee_recipient receives 18 TAO in sell fees" + ); + }); +} + +/// A mixed batch (buy + sell) must not rate-limit the pallet intermediary +/// account during asset collection, which would otherwise block the +/// subsequent alpha distribution to buyers. +/// +/// Regression test: previously `transfer_staked_alpha` with a single +/// `apply_limits: true` flag set the rate-limit on `to_coldkey` (pallet) +/// during collection, then the distribution step checked `from_coldkey` +/// (pallet) and failed with `StakingOperationRateLimitExceeded`. +#[test] +fn execute_batched_orders_mixed_batch_does_not_rate_limit_pallet_intermediary() { + new_test_ext().execute_with(|| { + // Alice buys 1_000 TAO; Bob sells 500 alpha. + // Buy-dominant: residual 500 TAO goes to pool, pool returns 400 alpha. + // Total alpha = 400 (pool) + 500 (Bob passthrough) = 900 → all to Alice. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(400); + MockSwap::set_tao_balance(alice(), 1_000); + MockSwap::set_alpha_balance(bob(), dave(), netuid(), 500); + + let buy = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let sell = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 500, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + // Must succeed: collecting Bob's alpha must not rate-limit the pallet + // intermediary, so distributing alpha to Alice is not blocked. + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![buy, sell]), + )); + + // Alice received staked alpha. + assert!( + MockSwap::alpha_balance(&alice(), &dave(), netuid()) > 0, + "alice should hold staked alpha after the buy" + ); + // Alice is rate-limited after receiving stake (set_receiver_limit=true). + assert!( + MockSwap::is_rate_limited(&dave(), &alice(), netuid()), + "alice should be rate-limited after receiving stake" + ); + // Bob's hotkey on the pallet side is NOT rate-limited (set_receiver_limit=false on collect). + assert!( + !MockSwap::is_rate_limited(&dave(), &bob(), netuid()), + "bob's rate-limit should not be set by the collection step" + ); + }); +} + +/// Root changes the pallet status, extrinsics are filtered +#[test] +fn root_disables_and_extrinsics_are_filtered() { + new_test_ext().execute_with(|| { + // Disable the pallet + assert_ok!(LimitOrders::set_pallet_status(RuntimeOrigin::root(), false)); + + let sell = make_signed_order( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 500, + 0, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + // Must succeed: collecting Bob's alpha must not rate-limit the pallet + // intermediary, so distributing alpha to Alice is not blocked. + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![sell]) + ), + Error::::LimitOrdersDisabled + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// max_slippage — execute_orders passes effective_swap_limit to pool +// ───────────────────────────────────────────────────────────────────────────── + +/// Build a signed order with a specific `max_slippage` value. +#[allow(clippy::too_many_arguments)] +fn make_signed_order_with_slippage( + keyring: AccountKeyring, + hotkey: AccountId, + netuid: subtensor_runtime_common::NetUid, + order_type: OrderType, + amount: u64, + limit_price: u64, + expiry: u64, + fee_rate: sp_runtime::Perbill, + fee_recipient: AccountId, + max_slippage: Option, +) -> crate::SignedOrder { + let order = crate::VersionedOrder::V1(crate::Order { + signer: keyring.to_account_id(), + hotkey, + netuid, + order_type, + amount, + limit_price, + expiry, + fee_rate, + fee_recipient, + relayer: None, + max_slippage, + chain_id: 945, + partial_fills_enabled: false, + }); + let sig = keyring.pair().sign(&order.encode()); + crate::SignedOrder { + order, + signature: sp_runtime::MultiSignature::Sr25519(sig), + partial_fill: None, + } +} + +#[test] +fn execute_orders_buy_no_slippage_passes_u64_max_to_pool() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let signed = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, // no slippage → u64::MAX ceiling + ); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Pool must have been called with u64::MAX as price ceiling. + assert_eq!(MockSwap::buy_alpha_limit_prices(), vec![u64::MAX]); + }); +} + +#[test] +fn execute_orders_sell_no_slippage_passes_zero_to_pool() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(2.0); + + let signed = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 500, + 1_000_000_000, // 1.0 in ×10⁹ scale; price=2.0 (scaled=2_000_000_000) >= 1_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, // no slippage → 0 floor + ); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert_eq!(MockSwap::sell_alpha_limit_prices(), vec![0]); + }); +} + +#[test] +fn execute_orders_buy_one_percent_slippage_passes_ceiling_to_pool() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + // limit_price=1_000_000_000 (1.0 in ×10⁹), 1% slippage → ceiling = 1_010_000_000. + let signed = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + 1_000_000_000, // 1.0 in ×10⁹ scale; price=1.0 (scaled=1_000_000_000) <= 1_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), + ); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert_eq!(MockSwap::buy_alpha_limit_prices(), vec![1_010_000_000]); + }); +} + +#[test] +fn execute_orders_sell_one_percent_slippage_passes_floor_to_pool() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + // Price must be >= limit_price for TakeProfit to trigger. + MockSwap::set_price(2_000.0); + + // limit_price=1_000_000_000 (1.0 in ×10⁹), 1% slippage → floor = 990_000_000. + let signed = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 500, + 1_000_000_000, // 1.0 in ×10⁹ scale; price=2000.0 (scaled=2T) >= 1_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), + ); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert_eq!(MockSwap::sell_alpha_limit_prices(), vec![990_000_000]); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// max_slippage — execute_batched_orders aggregates tightest constraint +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_batched_orders_buy_dominant_uses_min_ceiling() { + new_test_ext().execute_with(|| { + // 3 buy orders with different slippage constraints. + // Alice: limit=1_000_000_000, 2% → ceiling=1_020_000_000 + // Bob: limit=1_000_000_000, 1% → ceiling=1_010_000_000 ← tightest + // Charlie (as signer, not relayer): limit=1_000_000_000, 3% → ceiling=1_030_000_000 + // Expected pool price_limit = min(1_020_000_000, 1_010_000_000, 1_030_000_000) = 1_010_000_000. + // price=1.0, scaled=1_000_000_000 <= 1_000_000_000 ✓ for all LimitBuy orders. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(500); + MockSwap::set_tao_balance(alice(), 600); + MockSwap::set_tao_balance(bob(), 200); + MockSwap::set_tao_balance(dave(), 200); + + let alice_order = make_signed_order_with_slippage( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 600, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(2)), // ceiling = 1_020_000_000 + ); + let bob_order = make_signed_order_with_slippage( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::LimitBuy, + 200, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), // ceiling = 1_010_000_000 ← tightest + ); + let dave_order = make_signed_order_with_slippage( + AccountKeyring::Dave, + dave(), + netuid(), + OrderType::LimitBuy, + 200, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(3)), // ceiling = 1_030_000_000 + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_order, bob_order, dave_order]), + )); + + // Net pool swap must have been called with the tightest ceiling = 1_010_000_000. + assert_eq!(MockSwap::buy_alpha_limit_prices(), vec![1_010_000_000]); + }); +} + +#[test] +fn execute_batched_orders_sell_dominant_uses_max_floor() { + new_test_ext().execute_with(|| { + // 3 sell orders with different slippage constraints. + // Alice: limit=1_000_000_000, 3% → floor=970_000_000 + // Bob: limit=1_000_000_000, 1% → floor=990_000_000 ← tightest (highest floor) + // Dave: limit=1_000_000_000, 2% → floor=980_000_000 + // Expected pool price_limit = max(970_000_000, 990_000_000, 980_000_000) = 990_000_000. + // Price must be >= limit_price=1_000_000_000 (1.0 in ×10⁹) for TakeProfit to trigger. + // price=2000.0, scaled=2_000_000_000_000 >= 1_000_000_000 ✓. + MockTime::set(1_000_000); + MockSwap::set_price(2_000.0); + MockSwap::set_sell_tao_return(500); + MockSwap::set_alpha_balance(alice(), dave(), netuid(), 600); + MockSwap::set_alpha_balance(bob(), dave(), netuid(), 200); + MockSwap::set_alpha_balance(dave(), dave(), netuid(), 200); + + let alice_order = make_signed_order_with_slippage( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::TakeProfit, + 600, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(3)), // floor = 970_000_000 + ); + let bob_order = make_signed_order_with_slippage( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 200, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), // floor = 990_000_000 ← tightest + ); + let dave_order = make_signed_order_with_slippage( + AccountKeyring::Dave, + dave(), + netuid(), + OrderType::TakeProfit, + 200, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(2)), // floor = 980_000_000 + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_order, bob_order, dave_order]), + )); + + // Net pool swap must have been called with the tightest floor = 990_000_000. + assert_eq!(MockSwap::sell_alpha_limit_prices(), vec![990_000_000]); + }); +} + +#[test] +fn execute_batched_orders_no_slippage_uses_unconstrained_limits() { + new_test_ext().execute_with(|| { + // Orders without max_slippage should pass u64::MAX (buy) or 0 (sell). + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(500); + MockSwap::set_tao_balance(alice(), 1_000); + + let order = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order]), + )); + + assert_eq!(MockSwap::buy_alpha_limit_prices(), vec![u64::MAX]); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// max_slippage — mixed order type coexistence +// ───────────────────────────────────────────────────────────────────────────── + +/// Sell-dominant batch: TakeProfit orders (with slippage) + StopLoss (no slippage). +/// +/// TakeProfit orders set meaningful floors; StopLoss contributes 0 (no constraint). +/// pool_price_limit = max(take_floors..., 0s) = max(take_floors). +/// All three orders are fulfilled. +#[test] +fn execute_batched_orders_takeprofit_and_stoploss_coexist_sell_dominant() { + new_test_ext().execute_with(|| { + // Price = 2000 — scaled = 2_000_000_000_000. + // TakeProfit triggers when scaled_price >= limit_price (2T >= 1_000_000_000 ✓). + // StopLoss triggers when scaled_price <= limit_price (2T <= 5_000_000_000_000 ✓). + MockTime::set(1_000_000); + MockSwap::set_price(2_000.0); + MockSwap::set_sell_tao_return(500); + + // Alice TakeProfit: limit=1_000_000_000 (1.0), 3% → floor=970_000_000. + // Bob TakeProfit: limit=1_000_000_000 (1.0), 1% → floor=990_000_000. ← tightest + // Dave StopLoss: limit=5_000_000_000_000 (5000.0), None → floor=0. + MockSwap::set_alpha_balance(alice(), dave(), netuid(), 600); + MockSwap::set_alpha_balance(bob(), dave(), netuid(), 200); + MockSwap::set_alpha_balance(dave(), alice(), netuid(), 200); + + let alice_order = make_signed_order_with_slippage( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::TakeProfit, + 600, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(3)), + ); + let bob_order = make_signed_order_with_slippage( + AccountKeyring::Bob, + dave(), + netuid(), + OrderType::TakeProfit, + 200, + 1_000_000_000, // 1.0 in ×10⁹ scale + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), + ); + let dave_stoploss = make_signed_order_with_slippage( + AccountKeyring::Dave, + alice(), + netuid(), + OrderType::StopLoss, + 200, + 5_000_000_000_000, // 5000.0 in ×10⁹ scale; scaled_price 2T <= 5T ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, // StopLoss: no slippage → floor=0, does not constrain pool + ); + + let alice_id = order_id(&alice_order.order); + let bob_id = order_id(&bob_order.order); + let dave_id = order_id(&dave_stoploss.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_order, bob_order, dave_stoploss]), + )); + + // All three fulfilled. + assert_eq!(Orders::::get(alice_id), Some(OrderStatus::Fulfilled)); + assert_eq!(Orders::::get(bob_id), Some(OrderStatus::Fulfilled)); + assert_eq!(Orders::::get(dave_id), Some(OrderStatus::Fulfilled)); + + // Pool called once with the tightest TakeProfit floor (990_000_000), not 0 from StopLoss. + assert_eq!(MockSwap::sell_alpha_limit_prices(), vec![990_000_000]); + }); +} + +/// Buy-dominant batch: LimitBuy orders (with slippage) dominant + StopLoss (no slippage) on offset side. +/// +/// The offset StopLoss is settled internally at spot price; it does not contribute +/// to the pool's price ceiling (which comes only from the dominant buy side). +/// pool_price_limit = min(buy_ceilings) = 1_010_000_000. +#[test] +fn execute_batched_orders_limitbuy_and_stoploss_offset_coexist_buy_dominant() { + new_test_ext().execute_with(|| { + // Price = 1.0, scaled = 1_000_000_000. + // LimitBuy triggers (scaled <= limit ✓). StopLoss triggers (scaled <= limit ✓). + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(900); + + // Alice LimitBuy: limit=1_000_000_000 (1.0), 2% → ceiling=1_020_000_000. + // Bob LimitBuy: limit=1_000_000_000 (1.0), 1% → ceiling=1_010_000_000. ← tightest + // Dave StopLoss: limit=2_000_000_000 (2.0), None → floor=0 (offset side, not used for pool limit). + MockSwap::set_tao_balance(alice(), 600); + MockSwap::set_tao_balance(bob(), 400); + MockSwap::set_alpha_balance(dave(), alice(), netuid(), 100); + + let alice_order = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 600, + 1_000_000_000, // 1.0 in ×10⁹ scale; scaled=1_000_000_000 <= 1_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(2)), + ); + let bob_order = make_signed_order_with_slippage( + AccountKeyring::Bob, + bob(), + netuid(), + OrderType::LimitBuy, + 400, + 1_000_000_000, // 1.0 in ×10⁹ scale; scaled=1_000_000_000 <= 1_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), + ); + let dave_stoploss = make_signed_order_with_slippage( + AccountKeyring::Dave, + alice(), + netuid(), + OrderType::StopLoss, + 100, + 2_000_000_000, // 2.0 in ×10⁹ scale; scaled=1_000_000_000 <= 2_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, // StopLoss: no slippage; settled at spot, never constrains pool ceiling + ); + + let alice_id = order_id(&alice_order.order); + let bob_id = order_id(&bob_order.order); + let dave_id = order_id(&dave_stoploss.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![alice_order, bob_order, dave_stoploss]), + )); + + // All three fulfilled. + assert_eq!(Orders::::get(alice_id), Some(OrderStatus::Fulfilled)); + assert_eq!(Orders::::get(bob_id), Some(OrderStatus::Fulfilled)); + assert_eq!(Orders::::get(dave_id), Some(OrderStatus::Fulfilled)); + + // Pool buy called with min(1_020_000_000, 1_010_000_000) = 1_010_000_000. StopLoss's floor (0) is ignored on buy side. + assert_eq!(MockSwap::buy_alpha_limit_prices(), vec![1_010_000_000]); + }); +} + +/// StopLoss with a narrow slippage sets an effective floor above the current market price, +/// making the pool swap impossible and failing the entire batch. +/// +/// This demonstrates Issue 1 from the design: relayers should not apply max_slippage to +/// StopLoss orders. StopLoss triggers when price has already fallen; a floor derived from +/// the (higher) trigger threshold will almost always exceed the actual market price. +#[test] +fn execute_batched_orders_stoploss_narrow_slippage_breaks_batch() { + new_test_ext().execute_with(|| { + // StopLoss: limit=100_000_000_000 (100.0 in ×10⁹), triggers at price=50 (scaled=50_000_000_000 ≤ 100_000_000_000 ✓). + // 1% slippage → floor=99_000_000_000. Market is at 50 → pool cannot deliver ≥99_000_000_000. + MockTime::set(1_000_000); + MockSwap::set_price(50.0); + MockSwap::set_sell_tao_return(100); // non-zero so SwapReturnedZero is not the cause + MockSwap::set_enforce_price_limit(true); + MockSwap::set_alpha_balance(dave(), alice(), netuid(), 200); + + let stoploss = make_signed_order_with_slippage( + AccountKeyring::Dave, + alice(), + netuid(), + OrderType::StopLoss, + 200, + 100_000_000_000, // 100.0 in ×10⁹ scale; scaled=50_000_000_000 <= 100_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), // floor=99_000_000_000, but market=50 → pool rejects + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![stoploss]), + ), + DispatchError::Other("price limit exceeded") + ); + }); +} + +/// Same StopLoss scenario through execute_orders (best-effort): the order is silently +/// skipped rather than failing the whole call. +/// +/// Note: `DispatchError::Other` has `#[codec(skip)]` on its string field, so the reason +/// string is lost when stored in the event log. We verify the skip via storage absence +/// and by asserting the floor (99_000_000_000 = 100_000_000_000 - 1%) was actually passed +/// to the pool — which is what caused the rejection. The `execute_batched_orders` variant +/// below uses `assert_noop!` (checks the return value directly, no storage round-trip) and +/// can verify the string. +#[test] +fn execute_orders_stoploss_narrow_slippage_skips_order() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(50.0); + MockSwap::set_sell_tao_return(100); + MockSwap::set_enforce_price_limit(true); + + let stoploss = make_signed_order_with_slippage( + AccountKeyring::Dave, + alice(), + netuid(), + OrderType::StopLoss, + 200, + 100_000_000_000, // 100.0 in ×10⁹ scale; scaled=50_000_000_000 <= 100_000_000_000 ✓ + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_percent(1)), // floor=99_000_000_000, but market=50 → pool rejects + ); + let id = order_id(&stoploss.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![stoploss]), + false, + )); + + // Order not stored — pool rejected the floor. + assert!(Orders::::get(id).is_none()); + + // An OrderSkipped event must have been emitted for this order. + assert!( + System::events().iter().any(|r| matches!( + &r.event, + RuntimeEvent::LimitOrders(Event::OrderSkipped { order_id, .. }) + if *order_id == id + )), + "expected OrderSkipped event for this order" + ); + + // The sell was attempted with the correct floor (99_000_000_000 = 100_000_000_000 - 1%). + // This is the value that exceeded the market price and caused the rejection. + assert_eq!(MockSwap::sell_alpha_limit_prices(), vec![99_000_000_000]); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// relayer enforcement +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_orders_wrong_relayer_skipped() { + new_test_ext().execute_with(|| { + // Order locks execution to charlie(); submitting as bob() must be silently skipped. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(BoundedVec::truncate_from(vec![charlie()])), // only charlie may relay this order + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(bob()), // wrong relayer + bounded(vec![signed]), + false, + )); + + // Order not stored — it was skipped. + assert!(Orders::::get(id).is_none()); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::RelayerMissMatch.into(), + }); + }); +} + +#[test] +fn execute_orders_correct_relayer_executed() { + new_test_ext().execute_with(|| { + // Same order submitted by the designated relayer (charlie) — must succeed. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(BoundedVec::truncate_from(vec![charlie()])), // charlie is the designated relayer + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), // correct relayer + bounded(vec![signed]), + false, + )); + + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + assert_event(Event::OrderExecuted { + order_id: id, + signer: alice(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount_in: 1_000, + amount_out: 0, + }); + }); +} + +#[test] +fn execute_batched_orders_wrong_relayer_fails_entire_batch() { + new_test_ext().execute_with(|| { + // In execute_batched_orders a relayer mismatch is a hard failure — the + // whole call is reverted, unlike the best-effort skip in execute_orders. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(BoundedVec::truncate_from(vec![charlie()])), // only charlie may relay this order + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(bob()), // wrong relayer + netuid(), + bounded(vec![signed]) + ), + Error::::RelayerMissMatch + ); + }); +} + +#[test] +fn execute_batched_orders_correct_relayer_succeeds() { + new_test_ext().execute_with(|| { + // Same order submitted by the designated relayer — must execute and + // distribute alpha to the buyer. + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(1_000); + MockSwap::set_tao_balance(alice(), 1_000); + + let signed = make_signed_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(BoundedVec::truncate_from(vec![charlie()])), // charlie is the designated relayer + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), // correct relayer + netuid(), + bounded(vec![signed]) + )); + + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// Partial fills — execute_orders +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_orders_partial_fill_sets_partially_filled_status() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_tao_balance(alice(), 1_000); + + // Order for 1000 TAO; relayer is charlie (required for partial fills). + let signed = make_partial_fill_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + charlie(), + 400, // fill 400 out of 1000 + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(400)) + ); + }); +} + +#[test] +fn execute_orders_second_partial_fill_completes_order() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_tao_balance(alice(), 1_000); + + let signed_first = make_partial_fill_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + charlie(), + 600, + ); + let id = order_id(&signed_first.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed_first.clone()]), + false, + )); + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(600)) + ); + + // Re-submit the same signed order payload with a different partial_fill amount. + let mut signed_second = signed_first.clone(); + signed_second.partial_fill = Some(400); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed_second]), + false, + )); + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + }); +} + +#[test] +fn execute_orders_partial_fill_without_relayer_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_tao_balance(alice(), 1_000); + + // Build an order with partial_fills_enabled but no relayer set. + let inner = crate::Order { + signer: alice(), + hotkey: bob(), + netuid: netuid(), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: FAR_FUTURE, + fee_rate: Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: None, // <-- no relayer + max_slippage: None, + chain_id: 945, + partial_fills_enabled: true, + }; + let versioned = VersionedOrder::V1(inner); + let sig = AccountKeyring::Alice.pair().sign(&versioned.encode()); + let signed = crate::SignedOrder { + order: versioned, + signature: sp_runtime::MultiSignature::Sr25519(sig), + partial_fill: Some(400), + }; + let id = order_id(&signed.order); + + // The order is skipped (best-effort), not reverting the batch. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed]), + false, + )); + + // Nothing written to storage. + assert_eq!(Orders::::get(id), None); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::RelayerRequiredForPartialFill.into(), + }); + }); +} + +#[test] +fn execute_orders_partial_fill_exceeding_remaining_is_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_tao_balance(alice(), 1_000); + + // Pre-fill 700 of 1000. + let signed = make_partial_fill_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + charlie(), + 700, + ); + let id = order_id(&signed.order); + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed.clone()]), + false, + )); + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(700)) + ); + + // Try to fill 500 more, but only 300 remain → should be skipped. + let mut over_fill = signed.clone(); + over_fill.partial_fill = Some(500); + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![over_fill]), + false, + )); + + // Status unchanged. + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(700)) + ); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::IncorrectPartialFillAmount.into(), + }); + }); +} + +#[test] +fn execute_orders_partial_fill_none_on_partially_filled_is_skipped() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_tao_balance(alice(), 1_000); + + // Pre-fill 700 of 1000. + let signed = make_partial_fill_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + charlie(), + 700, + ); + let id = order_id(&signed.order); + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![signed.clone()]), + false, + )); + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(700)) + ); + + // Re-submit the same signed order with partial_fill = None against an + // order already PartiallyFilled. The one-shot full-execution path must + // not fire here: it would re-swap the full order.amount (over-debiting + // the signer) and mark the order Fulfilled, discarding the 700 already + // filled. The fix rejects this with IncorrectPartialFillAmount → skipped. + let mut none_fill = signed.clone(); + none_fill.partial_fill = None; + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![none_fill]), + false, + )); + + // Status unchanged — NOT over-filled and NOT marked Fulfilled. + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(700)) + ); + assert_event(Event::OrderSkipped { + order_id: id, + reason: Error::::IncorrectPartialFillAmount.into(), + }); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// Partial fills — execute_batched_orders +// ───────────────────────────────────────────────────────────────────────────── + +#[test] +fn execute_batched_orders_partial_fill_sets_partially_filled_status() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(400); + MockSwap::set_tao_balance(alice(), 1_000); + + let signed = make_partial_fill_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + charlie(), + 400, + ); + let id = order_id(&signed.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![signed]), + )); + + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(400)) + ); + }); +} + +#[test] +fn execute_batched_orders_second_partial_fill_completes_order() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(600); + MockSwap::set_tao_balance(alice(), 1_000); + + let signed_first = make_partial_fill_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + charlie(), + 600, + ); + let id = order_id(&signed_first.order); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![signed_first.clone()]), + )); + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(600)) + ); + + let mut signed_second = signed_first.clone(); + signed_second.partial_fill = Some(400); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![signed_second]), + )); + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// In-batch order_id deduplication — regression tests +// ───────────────────────────────────────────────────────────────────────────── + +/// Regression: the same fully-signed `LimitBuy` order appearing twice in one +/// batch must hard-fail with `DuplicateOrderInBatch` rather than debiting the +/// signer twice. Pre-fix, `validate_and_classify` validated each entry against +/// the same pre-batch `Orders::get(order_id)` snapshot with no in-batch tracking, +/// so the signer was charged N× their signed amount. +/// +/// `assert_noop!` also asserts the storage root is unchanged, proving the +/// all-or-nothing batch rolled back. (The mock's TAO/alpha ledgers are +/// thread-local RefCell maps, not substrate storage, so we do not assert on +/// them here — see `mock.rs`.) We additionally assert `Orders::get` was never +/// written. +#[test] +fn execute_batched_orders_full_fill_duplicate_rejected() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(500); + MockSwap::set_tao_balance(alice(), 1_000); + + // Open-relay (relayer: None) fully-signed LimitBuy. + let order = make_signed_order( + AccountKeyring::Alice, + dave(), + netuid(), + OrderType::LimitBuy, + 600, + u64::MAX, + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + None, + ); + let id = order_id(&order.order); + + // The same order twice in one batch. + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order.clone(), order]), + ), + Error::::DuplicateOrderInBatch + ); + + // The batch rolled back: no order status was recorded. + assert!(Orders::::get(id).is_none()); + }); +} + +/// Regression: two `SignedOrder`s that share the same inner `VersionedOrder` +/// (so the same `order_id`, since `order_id` excludes `partial_fill` and the +/// signature) but carry *different* `partial_fill` values must still collide +/// and be caught by the in-batch dedup. This exercises the partial-fill path +/// (partial_fills_enabled = true, relayer set). +#[test] +fn execute_batched_orders_partial_fill_duplicate_rejected() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_buy_alpha_return(400); + MockSwap::set_tao_balance(alice(), 1_000); + + // Same inner VersionedOrder; only the envelope `partial_fill` differs. + let first = make_partial_fill_order( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, + FAR_FUTURE, + charlie(), + 600, + ); + let mut second = first.clone(); + second.partial_fill = Some(400); + + // Same inner order ⇒ same order_id ⇒ caught by the dedup set. + assert_eq!(order_id(&first.order), order_id(&second.order)); + let id = order_id(&first.order); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![first, second]), + ), + Error::::DuplicateOrderInBatch + ); + + assert!(Orders::::get(id).is_none()); + }); +} + +/// Non-root origin cannot disable the pallet +#[test] +fn non_root_cannot_disable_the_pallet() { + new_test_ext().execute_with(|| { + // Try disabling the pallet with charlie + assert_noop!( + LimitOrders::set_pallet_status(RuntimeOrigin::signed(charlie()), false), + DispatchError::BadOrigin + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// MOCK_SIMULATE_PARTIAL_FILL — sim-swap detects partial fill before funds move +// ───────────────────────────────────────────────────────────────────────────── + +/// `execute_batched_orders` hard-fails the whole batch when the sim-swap for a +/// `LimitBuy` order detects a partial fill (price limit would stop the AMM +/// before consuming the full input). +#[test] +fn execute_batched_orders_buy_partial_fill_fails_batch() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_simulate_partial_fill(true); + + let order = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, // limit_price always passes for a buy + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_parts(1)), // slippage field set; mock ignores value + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order]), + ), + DispatchError::Other("slippage too high") + ); + }); +} + +/// `execute_orders` silently skips a `LimitBuy` order when the sim-swap detects +/// a partial fill: the order must not appear in storage and an `OrderSkipped` +/// event must be emitted. +#[test] +fn execute_orders_buy_partial_fill_skips_order() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_simulate_partial_fill(true); + + let order = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::LimitBuy, + 1_000, + u64::MAX, // limit_price always passes for a buy + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_parts(1)), // slippage field set; mock ignores value + ); + let id = order_id(&order.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![order]), + false, + )); + + // Order must not be stored — it was skipped, not fulfilled. + assert!(Orders::::get(id).is_none()); + + // An OrderSkipped event must have been emitted for this order. + assert!( + System::events().iter().any(|r| matches!( + &r.event, + RuntimeEvent::LimitOrders(Event::OrderSkipped { order_id, .. }) + if *order_id == id + )), + "expected OrderSkipped event for this order" + ); + }); +} + +/// `execute_batched_orders` hard-fails the whole batch when the sim-swap for a +/// `TakeProfit` (sell) order detects a partial fill. +#[test] +fn execute_batched_orders_sell_partial_fill_fails_batch() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_simulate_partial_fill(true); + // Seed alpha so the order passes the balance check before reaching the swap. + MockSwap::set_alpha_balance(alice(), bob(), netuid(), 1_000); + + let order = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, // limit_price = 0 → floor always passes for a TakeProfit + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_parts(1)), // slippage field set; mock ignores value + ); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie()), + netuid(), + bounded(vec![order]), + ), + DispatchError::Other("slippage too high") + ); + }); +} + +/// `execute_orders` silently skips a `TakeProfit` order when the sim-swap +/// detects a partial fill: the order must not appear in storage and an +/// `OrderSkipped` event must be emitted. +#[test] +fn execute_orders_sell_partial_fill_skips_order() { + new_test_ext().execute_with(|| { + MockTime::set(1_000_000); + MockSwap::set_price(1.0); + MockSwap::set_simulate_partial_fill(true); + // Seed alpha so the order passes the balance check before reaching the swap. + MockSwap::set_alpha_balance(alice(), bob(), netuid(), 1_000); + + let order = make_signed_order_with_slippage( + AccountKeyring::Alice, + bob(), + netuid(), + OrderType::TakeProfit, + 1_000, + 0, // limit_price = 0 → floor always passes for a TakeProfit + FAR_FUTURE, + Perbill::zero(), + fee_recipient(), + Some(Perbill::from_parts(1)), // slippage field set; mock ignores value + ); + let id = order_id(&order.order); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie()), + bounded(vec![order]), + false, + )); + + // Order must not be stored — it was skipped, not fulfilled. + assert!(Orders::::get(id).is_none()); + + // An OrderSkipped event must have been emitted for this order. + assert!( + System::events().iter().any(|r| matches!( + &r.event, + RuntimeEvent::LimitOrders(Event::OrderSkipped { order_id, .. }) + if *order_id == id + )), + "expected OrderSkipped event for this order" + ); + }); +} diff --git a/pallets/limit-orders/src/tests/migration.rs b/pallets/limit-orders/src/tests/migration.rs new file mode 100644 index 0000000000..d0302158d7 --- /dev/null +++ b/pallets/limit-orders/src/tests/migration.rs @@ -0,0 +1,120 @@ +#![allow(clippy::unwrap_used)] +//! Tests for the `migrate_register_pallet_hotkey` migration. + +use frame_support::{BoundedVec, traits::Hooks}; +use sp_runtime::{BuildStorage, traits::AccountIdConversion}; +use subtensor_swap_interface::OrderSwapInterface as _; + +use crate::{ + HasMigrationRun, LimitOrdersEnabled, MigrationKeyMaxLen, + migrations::migrate_register_pallet_hotkey, + tests::mock::{LimitOrdersPalletId, MockSwap, PalletHotkeyAccount, System, Test}, +}; + +fn migration_key() -> BoundedVec { + BoundedVec::truncate_from(b"migrate_register_pallet_hotkey".to_vec()) +} + +/// Minimal externalities: system genesis only, no pallet hotkey pre-registered, +/// `LimitOrdersEnabled` at its storage default (`false`). +fn migration_ext() -> sp_io::TestExternalities { + let storage = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + let mut ext = sp_io::TestExternalities::new(storage); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +#[test] +fn migration_registers_hotkey_and_marks_run_on_first_call() { + migration_ext().execute_with(|| { + let pallet_acct: crate::tests::mock::AccountId = + LimitOrdersPalletId::get().into_account_truncating(); + let pallet_hotkey = PalletHotkeyAccount::get(); + + assert!(!MockSwap::pallet_hotkey_registered( + &pallet_acct, + &pallet_hotkey + )); + assert!(!HasMigrationRun::::get(migration_key())); + + migrate_register_pallet_hotkey::(); + + assert!( + MockSwap::pallet_hotkey_registered(&pallet_acct, &pallet_hotkey), + "hotkey must be registered after migration" + ); + assert!( + HasMigrationRun::::get(migration_key()), + "migration must be marked as run" + ); + // Migration no longer touches LimitOrdersEnabled — value is unchanged. + assert!(!LimitOrdersEnabled::::get()); + }); +} + +#[test] +fn migration_does_not_touch_limit_orders_enabled() { + migration_ext().execute_with(|| { + // Enable the pallet before running the migration (simulates a chain + // that already had it enabled via genesis or admin action). + LimitOrdersEnabled::::set(true); + + migrate_register_pallet_hotkey::(); + + assert!( + LimitOrdersEnabled::::get(), + "migration must not change LimitOrdersEnabled" + ); + }); +} + +#[test] +fn migration_skips_hotkey_registration_when_already_registered() { + migration_ext().execute_with(|| { + let pallet_acct: crate::tests::mock::AccountId = + LimitOrdersPalletId::get().into_account_truncating(); + let pallet_hotkey = PalletHotkeyAccount::get(); + let _ = MockSwap::register_pallet_hotkey(&pallet_acct, &pallet_hotkey); + + // Must not panic on duplicate registration. + migrate_register_pallet_hotkey::(); + + assert!(HasMigrationRun::::get(migration_key())); + }); +} + +#[test] +fn migration_is_idempotent() { + migration_ext().execute_with(|| { + let pallet_acct: crate::tests::mock::AccountId = + LimitOrdersPalletId::get().into_account_truncating(); + let pallet_hotkey = PalletHotkeyAccount::get(); + + migrate_register_pallet_hotkey::(); + assert!(MockSwap::pallet_hotkey_registered( + &pallet_acct, + &pallet_hotkey + )); + + // Second run must be a no-op — hotkey stays registered, flag stays set. + migrate_register_pallet_hotkey::(); + assert!(MockSwap::pallet_hotkey_registered( + &pallet_acct, + &pallet_hotkey + )); + assert!(HasMigrationRun::::get(migration_key())); + }); +} + +#[test] +fn on_runtime_upgrade_delegates_to_migration() { + migration_ext().execute_with(|| { + assert!(!HasMigrationRun::::get(migration_key())); + + as Hooks>::on_runtime_upgrade(); + + assert!(HasMigrationRun::::get(migration_key())); + }); +} diff --git a/pallets/limit-orders/src/tests/mock.rs b/pallets/limit-orders/src/tests/mock.rs new file mode 100644 index 0000000000..2834c54afe --- /dev/null +++ b/pallets/limit-orders/src/tests/mock.rs @@ -0,0 +1,666 @@ +#![allow(clippy::unwrap_used)] +//! Minimal mock runtime for `pallet-limit-orders` unit tests. +//! +//! `AccountId` is `sp_runtime::AccountId32` so that `MultiSignature` works +//! out of the box; test keys come from `sp_keyring::AccountKeyring`. + +use std::cell::RefCell; +use std::collections::HashMap; + +use codec::Encode; +use frame_support::{ + BoundedVec, PalletId, construct_runtime, derive_impl, parameter_types, + traits::{ConstU32, ConstU64, Everything}, +}; +use frame_system as system; +use sp_core::{H256, Pair}; +use sp_keyring::Sr25519Keyring as AccountKeyring; +use sp_runtime::{ + AccountId32, BuildStorage, MultiSignature, + traits::{AccountIdConversion, BlakeTwo256, IdentityLookup}, +}; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; +use subtensor_swap_interface::OrderSwapInterface; + +use crate::{self as pallet_limit_orders, LimitOrdersEnabled}; + +// ── Runtime ────────────────────────────────────────────────────────────────── + +construct_runtime!( + pub enum Test { + System: system = 0, + LimitOrders: pallet_limit_orders = 1, + } +); + +pub type Block = frame_system::mocking::MockBlock; +pub type AccountId = AccountId32; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type MaxConsumers = ConstU32<16>; + type Nonce = u64; + type Block = Block; +} + +// ── MockSwap ───────────────────────────────────────────────────────────────── +// +// Records every call so tests can assert that the right transfers happened. + +#[derive(Debug, Clone, PartialEq)] +pub enum SwapCall { + BuyAlpha { + coldkey: AccountId, + hotkey: AccountId, + netuid: NetUid, + tao: u64, + limit_price: u64, + }, + SellAlpha { + coldkey: AccountId, + hotkey: AccountId, + netuid: NetUid, + alpha: u64, + limit_price: u64, + }, + TransferTao { + from: AccountId, + to: AccountId, + amount: u64, + }, + TransferStakedAlpha { + from_coldkey: AccountId, + from_hotkey: AccountId, + to_coldkey: AccountId, + to_hotkey: AccountId, + netuid: NetUid, + amount: u64, + }, +} + +thread_local! { + /// Log of every `OrderSwapInterface` call made during a test. + pub static SWAP_LOG: RefCell> = const { RefCell::new(Vec::new()) }; + /// Fixed price returned by `current_alpha_price` (default 1.0). + pub static MOCK_PRICE: RefCell = RefCell::new(U64F64::from_num(1u32)); + /// Fixed alpha returned by `buy_alpha` (default 0 — tests override as needed). + pub static MOCK_BUY_ALPHA_RETURN: RefCell = const { RefCell::new(0u64) }; + /// Fixed TAO returned by `sell_alpha` (default 0 — tests override as needed). + pub static MOCK_SELL_TAO_RETURN: RefCell = const { RefCell::new(0u64) }; + /// In-memory staked alpha ledger: (coldkey, hotkey, netuid) → balance. + /// `transfer_staked_alpha` debits/credits this map so tests can assert + /// on residual balances after distribution. + pub static ALPHA_BALANCES: RefCell> = + RefCell::new(HashMap::new()); + /// In-memory free TAO ledger: account → balance. + /// `transfer_tao` debits/credits this map so tests can assert + /// on residual balances after distribution. + pub static TAO_BALANCES: RefCell> = + RefCell::new(HashMap::new()); + /// When set to `true`, `transfer_tao` returns `Err(CannotLookup)` so + /// tests can exercise the fee-transfer-failure path. + pub static FAIL_FEE_TRANSFER: RefCell = const { RefCell::new(false) }; + /// When `true`, `buy_alpha` and `sell_alpha` return `DispatchError::Other("pool error")`. + pub static MOCK_SWAP_FAIL: RefCell = const { RefCell::new(false) }; + /// When `true`, swap calls enforce their `limit_price` argument against `MOCK_PRICE`: + /// `buy_alpha` fails if `market_price > limit_price` (ceiling exceeded); + /// `sell_alpha` fails if `market_price < limit_price` (floor not met). + pub static MOCK_ENFORCE_PRICE_LIMIT: RefCell = const { RefCell::new(false) }; + /// When `true`, `buy_alpha` and `sell_alpha` return a slippage error to simulate + /// the case where the AMM price limit stops the swap before the full amount is consumed. + pub static MOCK_SIMULATE_PARTIAL_FILL: RefCell = const { RefCell::new(false) }; + /// Rate-limit flags set by `transfer_staked_alpha` when `set_receiver_limit` is true. + /// Key: (hotkey, coldkey, netuid) — mirrors `StakingOperationRateLimiter` in subtensor. + pub static RATE_LIMITS: RefCell> = + RefCell::new(std::collections::HashSet::new()); + /// Registered (coldkey, hotkey) ownership pairs — mirrors `Owner` storage in subtensor. + pub static HOTKEY_REGISTRATIONS: RefCell> = + RefCell::new(std::collections::HashSet::new()); +} + +pub struct MockSwap; + +impl MockSwap { + pub fn set_price(price: f64) { + MOCK_PRICE.with(|p| *p.borrow_mut() = U64F64::from_num(price)); + } + pub fn set_buy_alpha_return(alpha: u64) { + MOCK_BUY_ALPHA_RETURN.with(|v| *v.borrow_mut() = alpha); + } + pub fn set_sell_tao_return(tao: u64) { + MOCK_SELL_TAO_RETURN.with(|v| *v.borrow_mut() = tao); + } + pub fn set_swap_fail(fail: bool) { + MOCK_SWAP_FAIL.with(|v| *v.borrow_mut() = fail); + } + pub fn set_enforce_price_limit(enforce: bool) { + MOCK_ENFORCE_PRICE_LIMIT.with(|v| *v.borrow_mut() = enforce); + } + pub fn set_simulate_partial_fill(val: bool) { + MOCK_SIMULATE_PARTIAL_FILL.with(|v| *v.borrow_mut() = val); + } + pub fn clear_log() { + SWAP_LOG.with(|l| l.borrow_mut().clear()); + ALPHA_BALANCES.with(|b| b.borrow_mut().clear()); + TAO_BALANCES.with(|b| b.borrow_mut().clear()); + RATE_LIMITS.with(|r| r.borrow_mut().clear()); + HOTKEY_REGISTRATIONS.with(|r| r.borrow_mut().clear()); + MOCK_ENFORCE_PRICE_LIMIT.with(|v| *v.borrow_mut() = false); + MOCK_SIMULATE_PARTIAL_FILL.with(|v| *v.borrow_mut() = false); + } + pub fn is_rate_limited(hotkey: &AccountId, coldkey: &AccountId, netuid: NetUid) -> bool { + RATE_LIMITS.with(|r| { + r.borrow() + .contains(&(hotkey.clone(), coldkey.clone(), netuid)) + }) + } + /// Seed a staked alpha balance for a (coldkey, hotkey, netuid) triple. + pub fn set_alpha_balance(coldkey: AccountId, hotkey: AccountId, netuid: NetUid, amount: u64) { + ALPHA_BALANCES.with(|b| { + b.borrow_mut().insert((coldkey, hotkey, netuid), amount); + }); + } + /// Query the current staked alpha balance for a (coldkey, hotkey, netuid) triple. + pub fn alpha_balance(coldkey: &AccountId, hotkey: &AccountId, netuid: NetUid) -> u64 { + ALPHA_BALANCES.with(|b| { + *b.borrow() + .get(&(coldkey.clone(), hotkey.clone(), netuid)) + .unwrap_or(&0) + }) + } + /// Seed a free TAO balance for an account. + pub fn set_tao_balance(account: AccountId, amount: u64) { + TAO_BALANCES.with(|b| { + b.borrow_mut().insert(account, amount); + }); + } + /// Query the current free TAO balance for an account. + pub fn tao_balance(account: &AccountId) -> u64 { + TAO_BALANCES.with(|b| *b.borrow().get(account).unwrap_or(&0)) + } + pub fn log() -> Vec { + SWAP_LOG.with(|l| l.borrow().clone()) + } + /// Returns the `limit_price` argument from every `buy_alpha` call, in order. + pub fn buy_alpha_limit_prices() -> Vec { + Self::log() + .into_iter() + .filter_map(|c| { + if let SwapCall::BuyAlpha { limit_price, .. } = c { + Some(limit_price) + } else { + None + } + }) + .collect() + } + /// Returns the `limit_price` argument from every `sell_alpha` call, in order. + pub fn sell_alpha_limit_prices() -> Vec { + Self::log() + .into_iter() + .filter_map(|c| { + if let SwapCall::SellAlpha { limit_price, .. } = c { + Some(limit_price) + } else { + None + } + }) + .collect() + } + + pub fn tao_transfers() -> Vec<(AccountId, AccountId, u64)> { + Self::log() + .into_iter() + .filter_map(|c| { + if let SwapCall::TransferTao { from, to, amount } = c { + Some((from, to, amount)) + } else { + None + } + }) + .collect() + } + pub fn alpha_transfers() -> Vec<(AccountId, AccountId, AccountId, AccountId, NetUid, u64)> { + Self::log() + .into_iter() + .filter_map(|c| { + if let SwapCall::TransferStakedAlpha { + from_coldkey, + from_hotkey, + to_coldkey, + to_hotkey, + netuid, + amount, + } = c + { + Some(( + from_coldkey, + from_hotkey, + to_coldkey, + to_hotkey, + netuid, + amount, + )) + } else { + None + } + }) + .collect() + } +} + +impl OrderSwapInterface for MockSwap { + fn buy_alpha( + coldkey: &AccountId, + hotkey: &AccountId, + netuid: NetUid, + tao_amount: TaoBalance, + limit_price: TaoBalance, + _apply_limits: bool, + ) -> Result { + if MOCK_SWAP_FAIL.with(|v| *v.borrow()) { + return Err(frame_support::pallet_prelude::DispatchError::Other( + "pool error", + )); + } + if MOCK_SIMULATE_PARTIAL_FILL.with(|v| *v.borrow()) { + return Err(frame_support::pallet_prelude::DispatchError::Other( + "slippage too high", + )); + } + let tao = tao_amount.to_u64(); + // Record the call (including rejected ones) so tests can verify the limit was passed. + SWAP_LOG.with(|l| { + l.borrow_mut().push(SwapCall::BuyAlpha { + coldkey: coldkey.clone(), + hotkey: hotkey.clone(), + netuid, + tao, + limit_price: limit_price.to_u64(), + }) + }); + if MOCK_ENFORCE_PRICE_LIMIT.with(|v| *v.borrow()) { + let price = MOCK_PRICE.with(|p| { + p.borrow() + .saturating_mul(U64F64::from_num(1_000_000_000u64)) + .saturating_to_num::() + }); + if price > limit_price.to_u64() { + return Err(frame_support::pallet_prelude::DispatchError::Other( + "price limit exceeded", + )); + } + } + let alpha_out = MOCK_BUY_ALPHA_RETURN.with(|v| *v.borrow()); + // Debit TAO from coldkey, credit alpha to (coldkey, hotkey, netuid). + TAO_BALANCES.with(|b| { + let mut map = b.borrow_mut(); + let bal = map.entry(coldkey.clone()).or_insert(0); + *bal = bal.saturating_sub(tao); + }); + ALPHA_BALANCES.with(|b| { + let mut map = b.borrow_mut(); + let bal = map + .entry((coldkey.clone(), hotkey.clone(), netuid)) + .or_insert(0); + *bal = bal.saturating_add(alpha_out); + }); + Ok(AlphaBalance::from(alpha_out)) + } + + fn sell_alpha( + coldkey: &AccountId, + hotkey: &AccountId, + netuid: NetUid, + alpha_amount: AlphaBalance, + limit_price: TaoBalance, + _apply_limits: bool, + ) -> Result { + if MOCK_SWAP_FAIL.with(|v| *v.borrow()) { + return Err(frame_support::pallet_prelude::DispatchError::Other( + "pool error", + )); + } + if MOCK_SIMULATE_PARTIAL_FILL.with(|v| *v.borrow()) { + return Err(frame_support::pallet_prelude::DispatchError::Other( + "slippage too high", + )); + } + let alpha = alpha_amount.to_u64(); + // Record the call (including rejected ones) so tests can verify the limit was passed. + SWAP_LOG.with(|l| { + l.borrow_mut().push(SwapCall::SellAlpha { + coldkey: coldkey.clone(), + hotkey: hotkey.clone(), + netuid, + alpha, + limit_price: limit_price.to_u64(), + }) + }); + // Only enforce if a non-zero floor was requested (0 means no constraint). + if MOCK_ENFORCE_PRICE_LIMIT.with(|v| *v.borrow()) && limit_price.to_u64() > 0 { + let price = MOCK_PRICE.with(|p| { + p.borrow() + .saturating_mul(U64F64::from_num(1_000_000_000u64)) + .saturating_to_num::() + }); + if price < limit_price.to_u64() { + return Err(frame_support::pallet_prelude::DispatchError::Other( + "price limit exceeded", + )); + } + } + let tao_out = MOCK_SELL_TAO_RETURN.with(|v| *v.borrow()); + // Debit alpha from (coldkey, hotkey, netuid), credit TAO to coldkey. + ALPHA_BALANCES.with(|b| { + let mut map = b.borrow_mut(); + let bal = map + .entry((coldkey.clone(), hotkey.clone(), netuid)) + .or_insert(0); + *bal = bal.saturating_sub(alpha); + }); + TAO_BALANCES.with(|b| { + let mut map = b.borrow_mut(); + let bal = map.entry(coldkey.clone()).or_insert(0); + *bal = bal.saturating_add(tao_out); + }); + Ok(TaoBalance::from(tao_out)) + } + + fn current_alpha_price(_netuid: NetUid) -> U64F64 { + MOCK_PRICE.with(|p| *p.borrow()) + } + + fn transfer_tao( + from: &AccountId, + to: &AccountId, + amount: TaoBalance, + ) -> frame_support::pallet_prelude::DispatchResult { + if FAIL_FEE_TRANSFER.with(|f| *f.borrow()) { + return Err(frame_support::pallet_prelude::DispatchError::CannotLookup); + } + let amt = amount.to_u64(); + TAO_BALANCES.with(|b| { + let mut map = b.borrow_mut(); + let from_bal = map.entry(from.clone()).or_insert(0); + *from_bal = from_bal.saturating_sub(amt); + let to_bal = map.entry(to.clone()).or_insert(0); + *to_bal = to_bal.saturating_add(amt); + }); + SWAP_LOG.with(|l| { + l.borrow_mut().push(SwapCall::TransferTao { + from: from.clone(), + to: to.clone(), + amount: amt, + }) + }); + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_up_netuid_for_benchmark(_netuid: NetUid) { + // Mock price is already set; no subnet state to initialise. + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_up_acc_for_benchmark(hotkey: &AccountId, coldkey: &AccountId) { + // Provide non-zero swap returns so batched-order benchmarks don't hit + // `SwapReturnedZero`. Also seed TAO and alpha balances so transfers + // succeed in the mock ledgers. + MockSwap::set_buy_alpha_return(1_000_000); + MockSwap::set_sell_tao_return(1_000_000); + MockSwap::set_tao_balance(coldkey.clone(), u64::MAX / 2); + MockSwap::set_alpha_balance( + coldkey.clone(), + hotkey.clone(), + NetUid::from(1u16), + u64::MAX / 2, + ); + } + + fn register_pallet_hotkey( + coldkey: &AccountId, + hotkey: &AccountId, + ) -> frame_support::pallet_prelude::DispatchResult { + HOTKEY_REGISTRATIONS.with(|r| { + r.borrow_mut().insert((coldkey.clone(), hotkey.clone())); + }); + Ok(()) + } + + fn pallet_hotkey_registered(coldkey: &AccountId, hotkey: &AccountId) -> bool { + HOTKEY_REGISTRATIONS.with(|r| r.borrow().contains(&(coldkey.clone(), hotkey.clone()))) + } + + fn transfer_staked_alpha( + from_coldkey: &AccountId, + from_hotkey: &AccountId, + to_coldkey: &AccountId, + to_hotkey: &AccountId, + netuid: NetUid, + amount: AlphaBalance, + validate_sender: bool, + set_receiver_limit: bool, + ) -> frame_support::pallet_prelude::DispatchResult { + if validate_sender { + let rate_limited = RATE_LIMITS.with(|r| { + r.borrow() + .contains(&(from_hotkey.clone(), from_coldkey.clone(), netuid)) + }); + if rate_limited { + return Err(frame_support::pallet_prelude::DispatchError::Other( + "StakingOperationRateLimitExceeded", + )); + } + } + let amt = amount.to_u64(); + ALPHA_BALANCES.with(|b| { + let mut map = b.borrow_mut(); + let from_bal = map + .entry((from_coldkey.clone(), from_hotkey.clone(), netuid)) + .or_insert(0); + *from_bal = from_bal.saturating_sub(amt); + let to_bal = map + .entry((to_coldkey.clone(), to_hotkey.clone(), netuid)) + .or_insert(0); + *to_bal = to_bal.saturating_add(amt); + }); + if set_receiver_limit { + RATE_LIMITS.with(|r| { + r.borrow_mut() + .insert((to_hotkey.clone(), to_coldkey.clone(), netuid)); + }); + } + SWAP_LOG.with(|l| { + l.borrow_mut().push(SwapCall::TransferStakedAlpha { + from_coldkey: from_coldkey.clone(), + from_hotkey: from_hotkey.clone(), + to_coldkey: to_coldkey.clone(), + to_hotkey: to_hotkey.clone(), + netuid, + amount: amt, + }) + }); + Ok(()) + } +} + +// ── MockTime ───────────────────────────────────────────────────────────────── + +thread_local! { + pub static MOCK_TIME_MS: RefCell = const { RefCell::new(1_000_000u64) }; +} + +pub struct MockTime; + +impl MockTime { + pub fn set(ms: u64) { + MOCK_TIME_MS.with(|t| *t.borrow_mut() = ms); + } +} + +impl frame_support::traits::UnixTime for MockTime { + fn now() -> core::time::Duration { + let ms = MOCK_TIME_MS.with(|t| *t.borrow()); + core::time::Duration::from_millis(ms) + } +} + +// ── Pallet config ───────────────────────────────────────────────────────────── + +parameter_types! { + pub const LimitOrdersPalletId: PalletId = PalletId(*b"lmt/ordr"); + pub const PalletHotkeyAccount: AccountId = AccountId::new([0xaa; 32]); +} + +/// A fixed account used in tests as the fee recipient when a concrete +/// recipient is needed but the test isn't specifically about fees. +pub fn fee_recipient() -> AccountId { + AccountId::new([0xfe; 32]) +} + +impl pallet_limit_orders::Config for Test { + type SwapInterface = MockSwap; + type TimeProvider = MockTime; + type MaxOrdersPerBatch = ConstU32<64>; + type PalletId = LimitOrdersPalletId; + type PalletHotkey = PalletHotkeyAccount; + type WeightInfo = (); + type ChainId = ConstU64<945>; +} + +// ── Shared test helpers ─────────────────────────────────────────────────────── + +pub fn alice() -> AccountId { + AccountKeyring::Alice.to_account_id() +} +pub fn bob() -> AccountId { + AccountKeyring::Bob.to_account_id() +} +pub fn charlie() -> AccountId { + AccountKeyring::Charlie.to_account_id() +} +pub fn dave() -> AccountId { + AccountKeyring::Dave.to_account_id() +} +pub fn netuid() -> NetUid { + NetUid::from(1u16) +} + +pub const FAR_FUTURE: u64 = u64::MAX; + +#[allow(clippy::too_many_arguments)] +pub fn make_signed_order( + keyring: AccountKeyring, + hotkey: AccountId, + netuid: NetUid, + order_type: crate::OrderType, + amount: u64, + limit_price: u64, + expiry: u64, + fee_rate: sp_runtime::Perbill, + fee_recipient: AccountId, + relayer: Option>>, +) -> crate::SignedOrder { + let signer = keyring.to_account_id(); + let order = crate::VersionedOrder::V1(crate::Order { + signer, + hotkey, + netuid, + order_type, + amount, + limit_price, + expiry, + fee_rate, + fee_recipient, + relayer, + max_slippage: None, + chain_id: 945, + partial_fills_enabled: false, + }); + let sig = keyring.pair().sign(&order.encode()); + crate::SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + } +} + +/// Build a signed order with partial fills enabled and a relayer set. +/// `partial_fill` is the fill amount to inject into the `SignedOrder` envelope. +#[allow(clippy::too_many_arguments)] +pub fn make_partial_fill_order( + keyring: AccountKeyring, + hotkey: AccountId, + netuid: NetUid, + order_type: crate::OrderType, + amount: u64, + limit_price: u64, + expiry: u64, + relayer: AccountId, + partial_fill: u64, +) -> crate::SignedOrder { + let signer = keyring.to_account_id(); + let order = crate::VersionedOrder::V1(crate::Order { + signer, + hotkey, + netuid, + order_type, + amount, + limit_price, + expiry, + fee_rate: sp_runtime::Perbill::zero(), + fee_recipient: fee_recipient(), + relayer: Some(BoundedVec::try_from(vec![relayer]).unwrap()), + max_slippage: None, + chain_id: 945, + partial_fills_enabled: true, + }); + let sig = keyring.pair().sign(&order.encode()); + crate::SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: Some(partial_fill), + } +} + +pub fn bounded( + v: Vec>, +) -> BoundedVec, ConstU32<64>> { + BoundedVec::try_from(v).unwrap() +} + +pub fn order_id(order: &crate::VersionedOrder) -> H256 { + crate::pallet::Pallet::::derive_order_id(order) +} + +// ── Test externalities ──────────────────────────────────────────────────────── + +pub fn new_test_ext() -> sp_io::TestExternalities { + let storage = system::GenesisConfig::::default() + .build_storage() + .unwrap(); + let mut ext = sp_io::TestExternalities::new(storage); + // Register a keystore so `sp_io::crypto` functions work in benchmark tests. + let keystore = sp_keystore::testing::MemoryKeystore::new(); + ext.register_extension(sp_keystore::KeystoreExt::new(keystore)); + ext.execute_with(|| { + System::set_block_number(1); + MockSwap::clear_log(); + // Simulate genesis_build: register the pallet hotkey and enable the pallet. + let pallet_acct: AccountId = LimitOrdersPalletId::get().into_account_truncating(); + let _ = MockSwap::register_pallet_hotkey(&pallet_acct, &PalletHotkeyAccount::get()); + LimitOrdersEnabled::::set(true); + }); + ext +} diff --git a/pallets/limit-orders/src/tests/mod.rs b/pallets/limit-orders/src/tests/mod.rs new file mode 100644 index 0000000000..95e0875b26 --- /dev/null +++ b/pallets/limit-orders/src/tests/mod.rs @@ -0,0 +1,4 @@ +pub mod auxiliary; +pub mod extrinsics; +pub mod migration; +pub mod mock; diff --git a/pallets/limit-orders/src/weights.rs b/pallets/limit-orders/src/weights.rs new file mode 100644 index 0000000000..e8c24d30ba --- /dev/null +++ b/pallets/limit-orders/src/weights.rs @@ -0,0 +1,362 @@ + +//! Autogenerated weights for `pallet_limit_orders` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 +//! DATE: 2026-06-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runnervm3jyl0`, CPU: `AMD EPYC 9V74 80-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` + +// Executed Command: +// /home/runner/work/subtensor/subtensor/target/production/node-subtensor +// benchmark +// pallet +// --runtime=/home/runner/work/subtensor/subtensor/target/production/wbuild/node-subtensor-runtime/node_subtensor_runtime.compact.compressed.wasm +// --genesis-builder=runtime +// --genesis-builder-preset=benchmark +// --wasm-execution=compiled +// --pallet=pallet_limit_orders +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --no-storage-info +// --no-min-squares +// --no-median-slopes +// --output=/tmp/tmp.h1ZElBJrCs +// --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] +#![allow(dead_code)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_limit_orders`. +pub trait WeightInfo { + fn cancel_order() -> Weight; + fn set_pallet_status() -> Weight; + fn execute_orders(n: u32, ) -> Weight; + fn execute_batched_orders(n: u32, ) -> Weight; +} + +/// Weights for `pallet_limit_orders` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `LimitOrders::Orders` (r:1 w:1) + /// Proof: `LimitOrders::Orders` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + fn cancel_order() -> Weight { + // Proof Size summary in bytes: + // Measured: `66` + // Estimated: `3522` + // Minimum execution time: 13_230_000 picoseconds. + Weight::from_parts(14_822_000, 3522) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `LimitOrders::LimitOrdersEnabled` (r:0 w:1) + /// Proof: `LimitOrders::LimitOrdersEnabled` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + fn set_pallet_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_006_000 picoseconds. + Weight::from_parts(4_266_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `LimitOrders::LimitOrdersEnabled` (r:1 w:0) + /// Proof: `LimitOrders::LimitOrdersEnabled` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `EVMChainId::ChainId` (r:1 w:0) + /// Proof: `EVMChainId::ChainId` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `LimitOrders::Orders` (r:100 w:100) + /// Proof: `LimitOrders::Orders` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:100 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:202 w:202) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::FeeRate` (r:1 w:0) + /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) + /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:100 w:100) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:100 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:100 w:100) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:100 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:100 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:100 w:100) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:100 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:100) + /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[1, 100]`. + fn execute_orders(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1134 + n * (283 ±0)` + // Estimated: `6148 + n * (5158 ±0)` + // Minimum execution time: 597_854_000 picoseconds. + Weight::from_parts(61_768_457, 6148) + // Standard Error: 136_266 + .saturating_add(Weight::from_parts(520_180_782, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(17_u64)) + .saturating_add(T::DbWeight::get().reads((11_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(10_u64)) + .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5158).saturating_mul(n.into())) + } + /// Storage: `LimitOrders::LimitOrdersEnabled` (r:1 w:0) + /// Proof: `LimitOrders::LimitOrdersEnabled` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `EVMChainId::ChainId` (r:1 w:0) + /// Proof: `EVMChainId::ChainId` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `LimitOrders::Orders` (r:100 w:100) + /// Proof: `LimitOrders::Orders` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:203 w:203) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::FeeRate` (r:1 w:0) + /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) + /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:101 w:101) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:101 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:101 w:101) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:101 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:101 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:101 w:101) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:100 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:101) + /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[1, 100]`. + fn execute_batched_orders(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1263 + n * (283 ±0)` + // Estimated: `8727 + n * (5158 ±0)` + // Minimum execution time: 747_334_000 picoseconds. + Weight::from_parts(499_718_496, 8727) + // Standard Error: 74_442 + .saturating_add(Weight::from_parts(259_134_004, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(25_u64)) + .saturating_add(T::DbWeight::get().reads((10_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(15_u64)) + .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5158).saturating_mul(n.into())) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `LimitOrders::Orders` (r:1 w:1) + /// Proof: `LimitOrders::Orders` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + fn cancel_order() -> Weight { + // Proof Size summary in bytes: + // Measured: `66` + // Estimated: `3522` + // Minimum execution time: 13_230_000 picoseconds. + Weight::from_parts(14_822_000, 3522) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `LimitOrders::LimitOrdersEnabled` (r:0 w:1) + /// Proof: `LimitOrders::LimitOrdersEnabled` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + fn set_pallet_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_006_000 picoseconds. + Weight::from_parts(4_266_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `LimitOrders::LimitOrdersEnabled` (r:1 w:0) + /// Proof: `LimitOrders::LimitOrdersEnabled` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `EVMChainId::ChainId` (r:1 w:0) + /// Proof: `EVMChainId::ChainId` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `LimitOrders::Orders` (r:100 w:100) + /// Proof: `LimitOrders::Orders` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:100 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:202 w:202) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::FeeRate` (r:1 w:0) + /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) + /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:100 w:100) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:100 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:100 w:100) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:100 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:100 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:100 w:100) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:100 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:100) + /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[1, 100]`. + fn execute_orders(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1134 + n * (283 ±0)` + // Estimated: `6148 + n * (5158 ±0)` + // Minimum execution time: 597_854_000 picoseconds. + Weight::from_parts(61_768_457, 6148) + // Standard Error: 136_266 + .saturating_add(Weight::from_parts(520_180_782, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(17_u64)) + .saturating_add(RocksDbWeight::get().reads((11_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5158).saturating_mul(n.into())) + } + /// Storage: `LimitOrders::LimitOrdersEnabled` (r:1 w:0) + /// Proof: `LimitOrders::LimitOrdersEnabled` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// Storage: `Timestamp::Now` (r:1 w:0) + /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) + /// Storage: `EVMChainId::ChainId` (r:1 w:0) + /// Proof: `EVMChainId::ChainId` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`) + /// Storage: `LimitOrders::Orders` (r:100 w:100) + /// Proof: `LimitOrders::Orders` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:203 w:203) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::FeeRate` (r:1 w:0) + /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) + /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetVolume` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:101 w:101) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyShares` (r:101 w:0) + /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:101 w:101) + /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingHotkeys` (r:101 w:0) + /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Alpha` (r:101 w:0) + /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AlphaV2` (r:101 w:101) + /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTaoFlow` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTaoFlow` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Lock` (r:1 w:0) + /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:100 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:101) + /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `n` is `[1, 100]`. + fn execute_batched_orders(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1263 + n * (283 ±0)` + // Estimated: `8727 + n * (5158 ±0)` + // Minimum execution time: 747_334_000 picoseconds. + Weight::from_parts(499_718_496, 8727) + // Standard Error: 74_442 + .saturating_add(Weight::from_parts(259_134_004, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(25_u64)) + .saturating_add(RocksDbWeight::get().reads((10_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(15_u64)) + .saturating_add(RocksDbWeight::get().writes((7_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 5158).saturating_mul(n.into())) + } +} diff --git a/pallets/proxy/src/weights.rs b/pallets/proxy/src/weights.rs index 38ae8c69ac..1885d8b3f6 100644 --- a/pallets/proxy/src/weights.rs +++ b/pallets/proxy/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_subtensor_proxy` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-06-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervm3jyl0`, CPU: `AMD EPYC 9V74 80-Core Processor` +//! HOSTNAME: `runnervm7b5n9`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.dW1NaIslV8 +// --output=/tmp/tmp.y6dm1DHMCB // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -66,10 +66,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `637 + p * (37 ±0)` // Estimated: `4254 + p * (37 ±0)` - // Minimum execution time: 23_374_000 picoseconds. - Weight::from_parts(24_151_161, 4254) - // Standard Error: 3_337 - .saturating_add(Weight::from_parts(96_756, 0).saturating_mul(p.into())) + // Minimum execution time: 26_629_000 picoseconds. + Weight::from_parts(28_026_265, 4254) + // Standard Error: 4_888 + .saturating_add(Weight::from_parts(53_205, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) @@ -92,12 +92,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `894 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615 + a * (68 ±0) + p * (37 ±0)` - // Minimum execution time: 47_420_000 picoseconds. - Weight::from_parts(48_607_796, 8615) - // Standard Error: 1_429 - .saturating_add(Weight::from_parts(261_812, 0).saturating_mul(a.into())) - // Standard Error: 5_727 - .saturating_add(Weight::from_parts(54_276, 0).saturating_mul(p.into())) + // Minimum execution time: 52_588_000 picoseconds. + Weight::from_parts(53_234_507, 8615) + // Standard Error: 1_933 + .saturating_add(Weight::from_parts(228_831, 0).saturating_mul(a.into())) + // Standard Error: 7_745 + .saturating_add(Weight::from_parts(54_037, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 68).saturating_mul(a.into())) @@ -113,12 +113,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 23_295_000 picoseconds. - Weight::from_parts(24_193_917, 8615) - // Standard Error: 886 - .saturating_add(Weight::from_parts(211_445, 0).saturating_mul(a.into())) - // Standard Error: 3_552 - .saturating_add(Weight::from_parts(20_637, 0).saturating_mul(p.into())) + // Minimum execution time: 26_049_000 picoseconds. + Weight::from_parts(25_595_226, 8615) + // Standard Error: 1_041 + .saturating_add(Weight::from_parts(205_163, 0).saturating_mul(a.into())) + // Standard Error: 4_172 + .saturating_add(Weight::from_parts(40_277, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -132,12 +132,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 23_334_000 picoseconds. - Weight::from_parts(24_226_028, 8615) - // Standard Error: 891 - .saturating_add(Weight::from_parts(211_311, 0).saturating_mul(a.into())) - // Standard Error: 3_572 - .saturating_add(Weight::from_parts(19_850, 0).saturating_mul(p.into())) + // Minimum execution time: 26_269_000 picoseconds. + Weight::from_parts(26_388_332, 8615) + // Standard Error: 1_181 + .saturating_add(Weight::from_parts(199_703, 0).saturating_mul(a.into())) + // Standard Error: 4_732 + .saturating_add(Weight::from_parts(17_846, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -153,12 +153,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `308 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615` - // Minimum execution time: 30_775_000 picoseconds. - Weight::from_parts(29_900_605, 8615) - // Standard Error: 2_656 - .saturating_add(Weight::from_parts(245_681, 0).saturating_mul(a.into())) - // Standard Error: 10_638 - .saturating_add(Weight::from_parts(108_442, 0).saturating_mul(p.into())) + // Minimum execution time: 33_873_000 picoseconds. + Weight::from_parts(34_286_251, 8615) + // Standard Error: 2_040 + .saturating_add(Weight::from_parts(194_092, 0).saturating_mul(a.into())) + // Standard Error: 8_174 + .saturating_add(Weight::from_parts(53_615, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -169,10 +169,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 22_393_000 picoseconds. - Weight::from_parts(23_099_598, 4254) - // Standard Error: 1_922 - .saturating_add(Weight::from_parts(76_038, 0).saturating_mul(p.into())) + // Minimum execution time: 24_907_000 picoseconds. + Weight::from_parts(25_770_804, 4254) + // Standard Error: 2_400 + .saturating_add(Weight::from_parts(80_892, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -185,10 +185,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 23_635_000 picoseconds. - Weight::from_parts(24_761_635, 4254) - // Standard Error: 2_310 - .saturating_add(Weight::from_parts(58_413, 0).saturating_mul(p.into())) + // Minimum execution time: 26_690_000 picoseconds. + Weight::from_parts(27_827_049, 4254) + // Standard Error: 2_885 + .saturating_add(Weight::from_parts(64_054, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -199,10 +199,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 23_715_000 picoseconds. - Weight::from_parts(24_632_652, 4254) - // Standard Error: 2_253 - .saturating_add(Weight::from_parts(48_858, 0).saturating_mul(p.into())) + // Minimum execution time: 26_159_000 picoseconds. + Weight::from_parts(27_407_028, 4254) + // Standard Error: 3_009 + .saturating_add(Weight::from_parts(55_808, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -213,10 +213,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `139` // Estimated: `4254` - // Minimum execution time: 23_865_000 picoseconds. - Weight::from_parts(24_892_331, 4254) - // Standard Error: 2_007 - .saturating_add(Weight::from_parts(21_649, 0).saturating_mul(p.into())) + // Minimum execution time: 26_699_000 picoseconds. + Weight::from_parts(27_853_436, 4254) + // Standard Error: 3_475 + .saturating_add(Weight::from_parts(20_160, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -227,10 +227,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `156 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 22_854_000 picoseconds. - Weight::from_parts(23_802_763, 4254) - // Standard Error: 2_166 - .saturating_add(Weight::from_parts(41_019, 0).saturating_mul(p.into())) + // Minimum execution time: 25_437_000 picoseconds. + Weight::from_parts(26_529_087, 4254) + // Standard Error: 2_476 + .saturating_add(Weight::from_parts(48_014, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -244,8 +244,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `412` // Estimated: `8615` - // Minimum execution time: 42_413_000 picoseconds. - Weight::from_parts(43_264_000, 8615) + // Minimum execution time: 46_066_000 picoseconds. + Weight::from_parts(46_757_000, 8615) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -258,10 +258,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 11_487_000 picoseconds. - Weight::from_parts(12_050_045, 4254) - // Standard Error: 1_620 - .saturating_add(Weight::from_parts(45_828, 0).saturating_mul(p.into())) + // Minimum execution time: 13_585_000 picoseconds. + Weight::from_parts(14_259_931, 4254) + // Standard Error: 1_733 + .saturating_add(Weight::from_parts(42_799, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -282,10 +282,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `637 + p * (37 ±0)` // Estimated: `4254 + p * (37 ±0)` - // Minimum execution time: 23_374_000 picoseconds. - Weight::from_parts(24_151_161, 4254) - // Standard Error: 3_337 - .saturating_add(Weight::from_parts(96_756, 0).saturating_mul(p.into())) + // Minimum execution time: 26_629_000 picoseconds. + Weight::from_parts(28_026_265, 4254) + // Standard Error: 4_888 + .saturating_add(Weight::from_parts(53_205, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) @@ -308,12 +308,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `894 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615 + a * (68 ±0) + p * (37 ±0)` - // Minimum execution time: 47_420_000 picoseconds. - Weight::from_parts(48_607_796, 8615) - // Standard Error: 1_429 - .saturating_add(Weight::from_parts(261_812, 0).saturating_mul(a.into())) - // Standard Error: 5_727 - .saturating_add(Weight::from_parts(54_276, 0).saturating_mul(p.into())) + // Minimum execution time: 52_588_000 picoseconds. + Weight::from_parts(53_234_507, 8615) + // Standard Error: 1_933 + .saturating_add(Weight::from_parts(228_831, 0).saturating_mul(a.into())) + // Standard Error: 7_745 + .saturating_add(Weight::from_parts(54_037, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 68).saturating_mul(a.into())) @@ -329,12 +329,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 23_295_000 picoseconds. - Weight::from_parts(24_193_917, 8615) - // Standard Error: 886 - .saturating_add(Weight::from_parts(211_445, 0).saturating_mul(a.into())) - // Standard Error: 3_552 - .saturating_add(Weight::from_parts(20_637, 0).saturating_mul(p.into())) + // Minimum execution time: 26_049_000 picoseconds. + Weight::from_parts(25_595_226, 8615) + // Standard Error: 1_041 + .saturating_add(Weight::from_parts(205_163, 0).saturating_mul(a.into())) + // Standard Error: 4_172 + .saturating_add(Weight::from_parts(40_277, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -348,12 +348,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `299 + a * (68 ±0)` // Estimated: `8615` - // Minimum execution time: 23_334_000 picoseconds. - Weight::from_parts(24_226_028, 8615) - // Standard Error: 891 - .saturating_add(Weight::from_parts(211_311, 0).saturating_mul(a.into())) - // Standard Error: 3_572 - .saturating_add(Weight::from_parts(19_850, 0).saturating_mul(p.into())) + // Minimum execution time: 26_269_000 picoseconds. + Weight::from_parts(26_388_332, 8615) + // Standard Error: 1_181 + .saturating_add(Weight::from_parts(199_703, 0).saturating_mul(a.into())) + // Standard Error: 4_732 + .saturating_add(Weight::from_parts(17_846, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -369,12 +369,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `308 + a * (68 ±0) + p * (37 ±0)` // Estimated: `8615` - // Minimum execution time: 30_775_000 picoseconds. - Weight::from_parts(29_900_605, 8615) - // Standard Error: 2_656 - .saturating_add(Weight::from_parts(245_681, 0).saturating_mul(a.into())) - // Standard Error: 10_638 - .saturating_add(Weight::from_parts(108_442, 0).saturating_mul(p.into())) + // Minimum execution time: 33_873_000 picoseconds. + Weight::from_parts(34_286_251, 8615) + // Standard Error: 2_040 + .saturating_add(Weight::from_parts(194_092, 0).saturating_mul(a.into())) + // Standard Error: 8_174 + .saturating_add(Weight::from_parts(53_615, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -385,10 +385,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 22_393_000 picoseconds. - Weight::from_parts(23_099_598, 4254) - // Standard Error: 1_922 - .saturating_add(Weight::from_parts(76_038, 0).saturating_mul(p.into())) + // Minimum execution time: 24_907_000 picoseconds. + Weight::from_parts(25_770_804, 4254) + // Standard Error: 2_400 + .saturating_add(Weight::from_parts(80_892, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -401,10 +401,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 23_635_000 picoseconds. - Weight::from_parts(24_761_635, 4254) - // Standard Error: 2_310 - .saturating_add(Weight::from_parts(58_413, 0).saturating_mul(p.into())) + // Minimum execution time: 26_690_000 picoseconds. + Weight::from_parts(27_827_049, 4254) + // Standard Error: 2_885 + .saturating_add(Weight::from_parts(64_054, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -415,10 +415,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 23_715_000 picoseconds. - Weight::from_parts(24_632_652, 4254) - // Standard Error: 2_253 - .saturating_add(Weight::from_parts(48_858, 0).saturating_mul(p.into())) + // Minimum execution time: 26_159_000 picoseconds. + Weight::from_parts(27_407_028, 4254) + // Standard Error: 3_009 + .saturating_add(Weight::from_parts(55_808, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -429,10 +429,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `139` // Estimated: `4254` - // Minimum execution time: 23_865_000 picoseconds. - Weight::from_parts(24_892_331, 4254) - // Standard Error: 2_007 - .saturating_add(Weight::from_parts(21_649, 0).saturating_mul(p.into())) + // Minimum execution time: 26_699_000 picoseconds. + Weight::from_parts(27_853_436, 4254) + // Standard Error: 3_475 + .saturating_add(Weight::from_parts(20_160, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -443,10 +443,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `156 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 22_854_000 picoseconds. - Weight::from_parts(23_802_763, 4254) - // Standard Error: 2_166 - .saturating_add(Weight::from_parts(41_019, 0).saturating_mul(p.into())) + // Minimum execution time: 25_437_000 picoseconds. + Weight::from_parts(26_529_087, 4254) + // Standard Error: 2_476 + .saturating_add(Weight::from_parts(48_014, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -460,8 +460,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `412` // Estimated: `8615` - // Minimum execution time: 42_413_000 picoseconds. - Weight::from_parts(43_264_000, 8615) + // Minimum execution time: 46_066_000 picoseconds. + Weight::from_parts(46_757_000, 8615) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -474,10 +474,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `119 + p * (37 ±0)` // Estimated: `4254` - // Minimum execution time: 11_487_000 picoseconds. - Weight::from_parts(12_050_045, 4254) - // Standard Error: 1_620 - .saturating_add(Weight::from_parts(45_828, 0).saturating_mul(p.into())) + // Minimum execution time: 13_585_000 picoseconds. + Weight::from_parts(14_259_931, 4254) + // Standard Error: 1_733 + .saturating_add(Weight::from_parts(42_799, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } diff --git a/pallets/registry/Cargo.toml b/pallets/registry/Cargo.toml deleted file mode 100644 index 08d774884a..0000000000 --- a/pallets/registry/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "pallet-registry" -version = "4.0.0-dev" -description = "Simplified identity system for network participants." -authors = ["Bittensor Nucleus Team"] -homepage = "https://bittensor.com" -edition.workspace = true -license = "Unlicense" -publish = false -repository = "https://github.com/opentensor/subtensor" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -subtensor-macros.workspace = true -codec = { workspace = true, features = ["derive", "max-encoded-len"] } -scale-info = { workspace = true, features = ["derive"] } -frame-benchmarking = { workspace = true, optional = true } -frame-support.workspace = true -frame-system.workspace = true -sp-runtime.workspace = true -sp-std.workspace = true -enumflags2.workspace = true -sp-core.workspace = true -sp-io.workspace = true -pallet-balances.workspace = true -subtensor-runtime-common.workspace = true - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "sp-std/std", - "sp-runtime/std", - "enumflags2/std", - "sp-io/std", - "pallet-balances/std", - "subtensor-runtime-common/std", - "sp-core/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "subtensor-runtime-common/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", - "pallet-balances/try-runtime", -] diff --git a/pallets/registry/src/benchmarking.rs b/pallets/registry/src/benchmarking.rs deleted file mode 100644 index 244ffe2599..0000000000 --- a/pallets/registry/src/benchmarking.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! Benchmarking setup -#![cfg(feature = "runtime-benchmarks")] -#![allow( - clippy::arithmetic_side_effects, - clippy::expect_used, - clippy::unwrap_used -)] -use super::*; - -#[allow(unused)] -use crate::Pallet as Registry; -use frame_benchmarking::v2::*; -use frame_support::traits::{Get, tokens::fungible::Mutate}; -use frame_system::RawOrigin; -use sp_std::vec; - -fn assert_last_event( - generic_event: ::RuntimeEvent, -) { - frame_system::Pallet::::assert_last_event(generic_event.into()); -} - -// This creates an `IdentityInfo` object with `num_fields` extra fields. -// All data is pre-populated with some arbitrary bytes. -fn create_identity_info(_num_fields: u32) -> IdentityInfo { - let data = Data::Raw( - vec![0; 32] - .try_into() - .expect("size does not exceed 64; qed"), - ); - - IdentityInfo { - additional: Default::default(), - display: data.clone(), - legal: data.clone(), - web: data.clone(), - riot: data.clone(), - email: data.clone(), - pgp_fingerprint: Some([0; 20]), - image: data.clone(), - twitter: data, - } -} - -#[benchmarks(where BalanceOf: From)] -mod benchmarks { - use super::*; - - #[benchmark] - fn set_identity() { - // The target user - let caller: T::AccountId = whitelisted_caller(); - let deposit = T::InitialDeposit::get() * 10u64.into(); - let _ = T::Currency::set_balance(&caller, deposit); - - #[extrinsic_call] - _( - RawOrigin::Signed(caller.clone()), - caller.clone(), - Box::new(create_identity_info::(0)), - ); - - assert_last_event::(Event::::IdentitySet { who: caller }.into()); - } - - #[benchmark] - fn clear_identity() { - // The target user - let caller: T::AccountId = whitelisted_caller(); - let _ = T::Currency::set_balance(&caller, T::InitialDeposit::get() * 10u64.into()); - - Registry::::set_identity( - RawOrigin::Signed(caller.clone()).into(), - caller.clone(), - Box::new(create_identity_info::(0)), - ) - .unwrap(); - - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), caller.clone()); - - assert_last_event::(Event::::IdentityDissolved { who: caller }.into()); - } - - impl_benchmark_test_suite!(Registry, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/registry/src/lib.rs b/pallets/registry/src/lib.rs deleted file mode 100644 index f3b76bc529..0000000000 --- a/pallets/registry/src/lib.rs +++ /dev/null @@ -1,213 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(test)] -pub mod mock; -#[cfg(test)] -mod tests; - -mod benchmarking; -pub mod types; -pub mod weights; - -pub use pallet::*; -pub use types::*; -pub use weights::WeightInfo; - -use frame_support::traits::tokens::{ - Precision, - fungible::{self, MutateHold as _}, -}; -use sp_runtime::{Saturating, traits::Zero}; -use sp_std::boxed::Box; - -type BalanceOf = - <::Currency as fungible::Inspect<::AccountId>>::Balance; - -#[deny(missing_docs)] -#[frame_support::pallet] -#[allow(clippy::expect_used)] -pub mod pallet { - use super::*; - use frame_support::{pallet_prelude::*, traits::tokens::fungible}; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(_); - - // Configure the pallet by specifying the parameters and types on which it depends. - #[pallet::config] - pub trait Config: frame_system::Config { - /// Currency type that will be used to place deposits on neurons - #[allow(deprecated)] - type Currency: fungible::Mutate - + fungible::MutateHold; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - - /// Interface to allow other pallets to control who can register identities - type CanRegister: crate::CanRegisterIdentity; - - /// Configuration fields - /// Maximum user-configured additional fields - #[pallet::constant] - type MaxAdditionalFields: Get; - - /// The amount held on deposit for a registered identity - #[pallet::constant] - type InitialDeposit: Get>; - - /// The amount held on deposit per additional field for a registered identity. - #[pallet::constant] - type FieldDeposit: Get>; - - /// Reasons for putting funds on hold. - type RuntimeHoldReason: From; - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Emitted when a user registers an identity - IdentitySet { - /// The account that registered the identity - who: T::AccountId, - }, - /// Emitted when a user dissolves an identity - IdentityDissolved { - /// The account that dissolved the identity - who: T::AccountId, - }, - } - - #[pallet::error] - pub enum Error { - /// Account attempted to register an identity but does not meet the requirements. - CannotRegister, - /// Account passed too many additional fields to their identity - TooManyFieldsInIdentityInfo, - /// Account doesn't have a registered identity - NotRegistered, - } - - /// Enum to hold reasons for putting funds on hold. - #[pallet::composite_enum] - pub enum HoldReason { - /// Funds are held for identity registration - RegistryIdentity, - } - - /// Identity data by account - #[pallet::storage] - #[pallet::getter(fn identity_of)] - pub(super) type IdentityOf = StorageMap< - _, - Twox64Concat, - T::AccountId, - Registration, T::MaxAdditionalFields>, - OptionQuery, - >; - - #[pallet::call] - impl Pallet { - #![deny(clippy::expect_used)] - - /// Register an identity for an account. This will overwrite any existing identity. - #[pallet::call_index(0)] - #[pallet::weight(( - T::WeightInfo::set_identity(), - DispatchClass::Normal - ))] - pub fn set_identity( - origin: OriginFor, - identified: T::AccountId, - info: Box>, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - ensure!( - T::CanRegister::can_register(&who, &identified), - Error::::CannotRegister - ); - - let extra_fields = info.additional.len() as u32; - ensure!( - extra_fields <= T::MaxAdditionalFields::get(), - Error::::TooManyFieldsInIdentityInfo - ); - - let fd = >::from(extra_fields).saturating_mul(T::FieldDeposit::get()); - let mut id = match >::get(&identified) { - Some(mut id) => { - id.info = *info; - id - } - None => Registration { - info: *info, - deposit: Zero::zero(), - }, - }; - - let old_deposit = id.deposit; - id.deposit = T::InitialDeposit::get().saturating_add(fd); - if id.deposit > old_deposit { - T::Currency::hold( - &HoldReason::RegistryIdentity.into(), - &who, - id.deposit.saturating_sub(old_deposit), - )?; - } - if old_deposit > id.deposit { - let release_res = T::Currency::release( - &HoldReason::RegistryIdentity.into(), - &who, - old_deposit.saturating_sub(id.deposit), - Precision::BestEffort, - ); - debug_assert!(release_res.is_ok_and( - |released_amount| released_amount == old_deposit.saturating_sub(id.deposit) - )); - } - - >::insert(&identified, id); - Self::deposit_event(Event::IdentitySet { who: identified }); - - Ok(()) - } - - /// Clear the identity of an account. - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::clear_identity())] - pub fn clear_identity( - origin: OriginFor, - identified: T::AccountId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - - let id = >::take(&identified).ok_or(Error::::NotRegistered)?; - let deposit = id.total_deposit(); - - let release_res = T::Currency::release( - &HoldReason::RegistryIdentity.into(), - &who, - deposit, - Precision::BestEffort, - ); - debug_assert!(release_res.is_ok_and(|released_amount| released_amount == deposit)); - - Self::deposit_event(Event::IdentityDissolved { who: identified }); - - Ok(().into()) - } - } -} -// Interfaces to interact with other pallets -pub trait CanRegisterIdentity { - fn can_register(who: &AccountId, identified: &AccountId) -> bool; -} - -impl CanRegisterIdentity for () { - fn can_register(_: &A, _: &A) -> bool { - false - } -} diff --git a/pallets/registry/src/mock.rs b/pallets/registry/src/mock.rs deleted file mode 100644 index 32957c40bb..0000000000 --- a/pallets/registry/src/mock.rs +++ /dev/null @@ -1,81 +0,0 @@ -#![allow(clippy::expect_used)] -use crate as pallet_registry; -use frame_support::{derive_impl, parameter_types}; -use sp_core::U256; -use sp_runtime::{BuildStorage, traits::IdentityLookup}; -use subtensor_runtime_common::TaoBalance; - -type Block = frame_system::mocking::MockBlock; - -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test - { - System: frame_system = 1, - Balances: pallet_balances = 2, - Registry: pallet_registry = 3, - } -); - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type Block = Block; - type AccountId = U256; - type AccountData = pallet_balances::AccountData; - type Lookup = IdentityLookup; -} - -parameter_types! { - pub const ExistentialDeposit: TaoBalance = TaoBalance::new(1); -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for Test { - type AccountStore = System; - type Balance = TaoBalance; - type ExistentialDeposit = ExistentialDeposit; -} - -parameter_types! { - pub const MaxAdditionalFields: u32 = 16; - pub const InitialDeposit: TaoBalance = TaoBalance::new(100); - pub const FieldDeposit: TaoBalance = TaoBalance::new(10); -} - -pub struct CanRegister; -impl pallet_registry::CanRegisterIdentity for CanRegister { - fn can_register(who: &U256, identified: &U256) -> bool { - who == identified - } -} - -impl pallet_registry::Config for Test { - type Currency = Balances; - type WeightInfo = (); - type MaxAdditionalFields = MaxAdditionalFields; - type CanRegister = CanRegister; - type InitialDeposit = InitialDeposit; - type FieldDeposit = FieldDeposit; - type RuntimeHoldReason = RuntimeHoldReason; -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .expect("system storage should build ok"); - pallet_balances::GenesisConfig:: { - balances: vec![ - (U256::from(1), 10.into()), - (U256::from(2), 10.into()), - (U256::from(3), 10.into()), - (U256::from(4), 10.into()), - (U256::from(5), 3.into()), - ], - dev_accounts: None, - } - .assimilate_storage(&mut t) - .expect("balances storage should build ok"); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext -} diff --git a/pallets/registry/src/tests.rs b/pallets/registry/src/tests.rs deleted file mode 100644 index d233fe0783..0000000000 --- a/pallets/registry/src/tests.rs +++ /dev/null @@ -1 +0,0 @@ -// Testing diff --git a/pallets/registry/src/types.rs b/pallets/registry/src/types.rs deleted file mode 100644 index 0e5cbe3332..0000000000 --- a/pallets/registry/src/types.rs +++ /dev/null @@ -1,483 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; -use enumflags2::{BitFlags, bitflags}; -use frame_support::{ - BoundedVec, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, - traits::{ConstU32, Get}, -}; -use scale_info::{ - Path, Type, TypeInfo, TypeParameter, - build::{Fields, Variants}, - meta_type, -}; -use sp_runtime::{ - RuntimeDebug, - traits::{AppendZerosInput, Zero}, -}; -use sp_std::{fmt::Debug, iter::once, ops::Add, prelude::*}; -use subtensor_macros::freeze_struct; - -/// Either underlying data blob if it is at most 32 bytes, or a hash of it. If the data is greater -/// than 32-bytes then it will be truncated when encoding. -/// -/// Can also be `None`. -#[derive(Clone, Eq, PartialEq, RuntimeDebug, DecodeWithMemTracking, MaxEncodedLen)] -pub enum Data { - /// No data here. - None, - /// The data is stored directly. - Raw(BoundedVec>), - /// Only the Blake2 hash of the data is stored. The preimage of the hash may be retrieved - /// through some hash-lookup service. - BlakeTwo256([u8; 32]), - /// Only the SHA2-256 hash of the data is stored. The preimage of the hash may be retrieved - /// through some hash-lookup service. - Sha256([u8; 32]), - /// Only the Keccak-256 hash of the data is stored. The preimage of the hash may be retrieved - /// through some hash-lookup service. - Keccak256([u8; 32]), - /// Only the SHA3-256 hash of the data is stored. The preimage of the hash may be retrieved - /// through some hash-lookup service. - ShaThree256([u8; 32]), -} - -impl Data { - pub fn is_none(&self) -> bool { - self == &Data::None - } -} - -impl Decode for Data { - fn decode(input: &mut I) -> sp_std::result::Result { - let b = input.read_byte()?; - Ok(match b { - 0 => Data::None, - n @ 1..=65 => { - let mut r: BoundedVec<_, _> = vec![0u8; (n as usize).saturating_sub(1)] - .try_into() - .map_err(|_| codec::Error::from("bounded vec length exceeds limit"))?; - input.read(&mut r[..])?; - Data::Raw(r) - } - 66 => Data::BlakeTwo256(<[u8; 32]>::decode(input)?), - 67 => Data::Sha256(<[u8; 32]>::decode(input)?), - 68 => Data::Keccak256(<[u8; 32]>::decode(input)?), - 69 => Data::ShaThree256(<[u8; 32]>::decode(input)?), - _ => return Err(codec::Error::from("invalid leading byte")), - }) - } -} - -impl Encode for Data { - fn encode(&self) -> Vec { - match self { - Data::None => vec![0u8; 1], - Data::Raw(x) => { - let l = x.len().min(64) as u8; - let mut r = vec![l.saturating_add(1)]; - r.extend_from_slice(&x[..]); - r - } - Data::BlakeTwo256(h) => once(66u8).chain(h.iter().cloned()).collect(), - Data::Sha256(h) => once(67u8).chain(h.iter().cloned()).collect(), - Data::Keccak256(h) => once(68u8).chain(h.iter().cloned()).collect(), - Data::ShaThree256(h) => once(69u8).chain(h.iter().cloned()).collect(), - } - } -} -impl codec::EncodeLike for Data {} - -/// Add a Raw variant with the given index and a fixed sized byte array -macro_rules! data_raw_variants { - ($variants:ident, $(($index:literal, $size:literal)),* ) => { - $variants - $( - .variant(concat!("Raw", stringify!($size)), |v| v - .index($index) - .fields(Fields::unnamed().field(|f| f.ty::<[u8; $size]>())) - ) - )* - } -} - -impl TypeInfo for Data { - type Identity = Self; - - fn type_info() -> Type { - let variants = Variants::new().variant("None", |v| v.index(0)); - - // create a variant for all sizes of Raw data from 0-32 - let variants = data_raw_variants!( - variants, - (1, 0), - (2, 1), - (3, 2), - (4, 3), - (5, 4), - (6, 5), - (7, 6), - (8, 7), - (9, 8), - (10, 9), - (11, 10), - (12, 11), - (13, 12), - (14, 13), - (15, 14), - (16, 15), - (17, 16), - (18, 17), - (19, 18), - (20, 19), - (21, 20), - (22, 21), - (23, 22), - (24, 23), - (25, 24), - (26, 25), - (27, 26), - (28, 27), - (29, 28), - (30, 29), - (31, 30), - (32, 31), - (33, 32), - (34, 33), - (35, 34), - (36, 35), - (37, 36), - (38, 37), - (39, 38), - (40, 39), - (41, 40), - (42, 41), - (43, 42), - (44, 43), - (45, 44), - (46, 45), - (47, 46), - (48, 47), - (49, 48), - (50, 49), - (51, 50), - (52, 51), - (53, 52), - (54, 53), - (55, 54), - (56, 55), - (57, 56), - (58, 57), - (59, 58), - (60, 59), - (61, 60), - (62, 61), - (63, 62), - (64, 63), - (65, 64) - ); - - let variants = variants - .variant("BlakeTwo256", |v| { - v.index(66) - .fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) - }) - .variant("Sha256", |v| { - v.index(67) - .fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) - }) - .variant("Keccak256", |v| { - v.index(68) - .fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) - }) - .variant("ShaThree256", |v| { - v.index(69) - .fields(Fields::unnamed().field(|f| f.ty::<[u8; 32]>())) - }); - - Type::builder() - .path(Path::new("Data", module_path!())) - .variant(variants) - } -} - -impl Default for Data { - fn default() -> Self { - Self::None - } -} - -/// The fields that we use to identify the owner of an account with. Each corresponds to a field -/// in the `IdentityInfo` struct. -#[bitflags] -#[repr(u64)] -#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub enum IdentityField { - Display = 0b0000000000000000000000000000000000000000000000000000000000000001, - Legal = 0b0000000000000000000000000000000000000000000000000000000000000010, - Web = 0b0000000000000000000000000000000000000000000000000000000000000100, - Riot = 0b0000000000000000000000000000000000000000000000000000000000001000, - Email = 0b0000000000000000000000000000000000000000000000000000000000010000, - PgpFingerprint = 0b0000000000000000000000000000000000000000000000000000000000100000, - Image = 0b0000000000000000000000000000000000000000000000000000000001000000, - Twitter = 0b0000000000000000000000000000000000000000000000000000000010000000, -} - -/// Wrapper type for `BitFlags` that implements `Codec`. -#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)] -pub struct IdentityFields(pub BitFlags); - -impl MaxEncodedLen for IdentityFields { - fn max_encoded_len() -> usize { - u64::max_encoded_len() - } -} - -impl Eq for IdentityFields {} -impl Encode for IdentityFields { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } -} -impl Decode for IdentityFields { - fn decode(input: &mut I) -> sp_std::result::Result { - let field = u64::decode(input)?; - Ok(Self( - >::from_bits(field).map_err(|_| "invalid value")?, - )) - } -} -impl TypeInfo for IdentityFields { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new( - "T", - Some(meta_type::()), - )]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("IdentityField"))) - } -} - -/// Information concerning the identity of the controller of an account. -/// -/// NOTE: This should be stored at the end of the storage item to facilitate the addition of extra -/// fields in a backwards compatible way through a specialized `Decode` impl. -#[freeze_struct("4015f12f49280ee")] -#[derive( - CloneNoBound, - Encode, - Decode, - DecodeWithMemTracking, - Eq, - MaxEncodedLen, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[codec(mel_bound())] -#[derive(frame_support::DefaultNoBound)] -#[scale_info(skip_type_params(FieldLimit))] -pub struct IdentityInfo> { - /// Additional fields of the identity that are not catered for with the struct's explicit - /// fields. - pub additional: BoundedVec<(Data, Data), FieldLimit>, - - /// A reasonable display name for the controller of the account. This should be whatever it is - /// that it is typically known as and should not be confusable with other entities, given - /// reasonable context. - /// - /// Stored as UTF-8. - pub display: Data, - - /// The full legal name in the local jurisdiction of the entity. This might be a bit - /// long-winded. - /// - /// Stored as UTF-8. - pub legal: Data, - - /// A representative website held by the controller of the account. - /// - /// NOTE: `https://` is automatically prepended. - /// - /// Stored as UTF-8. - pub web: Data, - - /// The Riot/Matrix handle held by the controller of the account. - /// - /// Stored as UTF-8. - pub riot: Data, - - /// The email address of the controller of the account. - /// - /// Stored as UTF-8. - pub email: Data, - - /// The PGP/GPG public key of the controller of the account. - pub pgp_fingerprint: Option<[u8; 20]>, - - /// A graphic image representing the controller of the account. Should be a company, - /// organization or project logo or a headshot in the case of a human. - pub image: Data, - - /// The Twitter identity. The leading `@` character may be elided. - pub twitter: Data, -} - -impl> IdentityInfo { - pub fn fields(&self) -> IdentityFields { - let mut res = >::empty(); - if !self.display.is_none() { - res.insert(IdentityField::Display); - } - if !self.legal.is_none() { - res.insert(IdentityField::Legal); - } - if !self.web.is_none() { - res.insert(IdentityField::Web); - } - if !self.riot.is_none() { - res.insert(IdentityField::Riot); - } - if !self.email.is_none() { - res.insert(IdentityField::Email); - } - if self.pgp_fingerprint.is_some() { - res.insert(IdentityField::PgpFingerprint); - } - if !self.image.is_none() { - res.insert(IdentityField::Image); - } - if !self.twitter.is_none() { - res.insert(IdentityField::Twitter); - } - IdentityFields(res) - } -} - -/// Information concerning the identity of the controller of an account. -/// -/// NOTE: This is stored separately primarily to facilitate the addition of extra fields in a -/// backwards compatible way through a specialized `Decode` impl. -#[freeze_struct("797b69e82710bb21")] -#[derive( - CloneNoBound, Encode, Eq, MaxEncodedLen, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, -)] -#[codec(mel_bound())] -#[scale_info(skip_type_params(MaxAdditionalFields))] -pub struct Registration< - Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq, - MaxAdditionalFields: Get, -> { - /// Amount held on deposit for this information. - pub deposit: Balance, - - /// Information on the identity. - pub info: IdentityInfo, -} - -impl< - Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq + Zero + Add, - MaxAdditionalFields: Get, -> Registration -{ - pub(crate) fn total_deposit(&self) -> Balance { - self.deposit - } -} - -impl< - Balance: Encode + Decode + MaxEncodedLen + Copy + Clone + Debug + Eq + PartialEq, - MaxAdditionalFields: Get, -> Decode for Registration -{ - fn decode(input: &mut I) -> sp_std::result::Result { - let (deposit, info) = Decode::decode(&mut AppendZerosInput::new(input))?; - Ok(Self { deposit, info }) - } -} - -#[cfg(test)] -#[allow(clippy::indexing_slicing, clippy::unwrap_used)] -mod tests { - use super::*; - - #[test] - fn manual_data_type_info() { - let mut registry = scale_info::Registry::new(); - let type_id = registry.register_type(&scale_info::meta_type::()); - let registry: scale_info::PortableRegistry = registry.into(); - let type_info = registry.resolve(type_id.id).unwrap(); - - let check_type_info = |data: &Data| { - let variant_name = match data { - Data::None => "None".to_string(), - Data::BlakeTwo256(_) => "BlakeTwo256".to_string(), - Data::Sha256(_) => "Sha256".to_string(), - Data::Keccak256(_) => "Keccak256".to_string(), - Data::ShaThree256(_) => "ShaThree256".to_string(), - Data::Raw(bytes) => format!("Raw{}", bytes.len()), - }; - if let scale_info::TypeDef::Variant(variant) = &type_info.type_def { - let variant = variant - .variants - .iter() - .find(|v| v.name == variant_name) - .unwrap_or_else(|| panic!("Expected to find variant {variant_name}")); - - let field_arr_len = variant - .fields - .first() - .and_then(|f| registry.resolve(f.ty.id)) - .map(|ty| { - if let scale_info::TypeDef::Array(arr) = &ty.type_def { - arr.len - } else { - panic!("Should be an array type") - } - }) - .unwrap_or(0); - - let encoded = data.encode(); - assert_eq!(encoded[0], variant.index); - assert_eq!(encoded.len() as u32 - 1, field_arr_len); - } else { - panic!("Should be a variant type") - }; - }; - - let mut data = vec![ - Data::None, - Data::BlakeTwo256(Default::default()), - Data::Sha256(Default::default()), - Data::Keccak256(Default::default()), - Data::ShaThree256(Default::default()), - ]; - - // A Raw instance for all possible sizes of the Raw data - for n in 0..64 { - data.push(Data::Raw(vec![0u8; n as usize].try_into().unwrap())) - } - - for d in data.iter() { - check_type_info(d); - } - } -} diff --git a/pallets/registry/src/weights.rs b/pallets/registry/src/weights.rs deleted file mode 100644 index b927be26ad..0000000000 --- a/pallets/registry/src/weights.rs +++ /dev/null @@ -1,107 +0,0 @@ - -//! Autogenerated weights for `pallet_registry` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervm46oaq`, CPU: `AMD EPYC 7763 64-Core Processor` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` - -// Executed Command: -// /home/runner/work/subtensor/subtensor/target/production/node-subtensor -// benchmark -// pallet -// --runtime -// /home/runner/work/subtensor/subtensor/target/production/wbuild/node-subtensor-runtime/node_subtensor_runtime.compact.compressed.wasm -// --genesis-builder=runtime -// --genesis-builder-preset=benchmark -// --wasm-execution=compiled -// --pallet -// pallet_registry -// --extrinsic -// * -// --steps -// 50 -// --repeat -// 20 -// --no-storage-info -// --no-min-squares -// --no-median-slopes -// --output=/tmp/tmp.SfIpjZbmqj -// --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] -#![allow(dead_code)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; - -/// Weight functions needed for `pallet_registry`. -pub trait WeightInfo { - fn set_identity() -> Weight; - fn clear_identity() -> Weight; -} - -/// Weights for `pallet_registry` using the Substrate node and recommended hardware. -pub struct SubstrateWeight(PhantomData); -impl WeightInfo for SubstrateWeight { - /// Storage: `Registry::IdentityOf` (r:1 w:1) - /// Proof: `Registry::IdentityOf` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(99), added: 2574, mode: `MaxEncodedLen`) - fn set_identity() -> Weight { - // Proof Size summary in bytes: - // Measured: `6` - // Estimated: `3564` - // Minimum execution time: 50_534_000 picoseconds. - Weight::from_parts(51_626_000, 3564) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Registry::IdentityOf` (r:1 w:1) - /// Proof: `Registry::IdentityOf` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(99), added: 2574, mode: `MaxEncodedLen`) - fn clear_identity() -> Weight { - // Proof Size summary in bytes: - // Measured: `382` - // Estimated: `3847` - // Minimum execution time: 42_379_000 picoseconds. - Weight::from_parts(43_501_000, 3847) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } -} - -// For backwards compatibility and tests. -impl WeightInfo for () { - /// Storage: `Registry::IdentityOf` (r:1 w:1) - /// Proof: `Registry::IdentityOf` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(99), added: 2574, mode: `MaxEncodedLen`) - fn set_identity() -> Weight { - // Proof Size summary in bytes: - // Measured: `6` - // Estimated: `3564` - // Minimum execution time: 50_534_000 picoseconds. - Weight::from_parts(51_626_000, 3564) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Registry::IdentityOf` (r:1 w:1) - /// Proof: `Registry::IdentityOf` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(99), added: 2574, mode: `MaxEncodedLen`) - fn clear_identity() -> Weight { - // Proof Size summary in bytes: - // Measured: `382` - // Estimated: `3847` - // Minimum execution time: 42_379_000 picoseconds. - Weight::from_parts(43_501_000, 3847) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } -} diff --git a/pallets/subtensor/Cargo.toml b/pallets/subtensor/Cargo.toml index 99ba71629f..27f86564d7 100644 --- a/pallets/subtensor/Cargo.toml +++ b/pallets/subtensor/Cargo.toml @@ -160,6 +160,9 @@ runtime-benchmarks = [ "pallet-subtensor-utility/runtime-benchmarks", "pallet-shield/runtime-benchmarks", "subtensor-runtime-common/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks", + "libsecp256k1/hmac", + "libsecp256k1/static-context", ] pow-faucet = [] fast-runtime = ["subtensor-runtime-common/fast-runtime"] diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index ced3956b53..2b95d59dc3 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +use alloc::collections::BTreeMap; use alloc::vec::Vec; use codec::Compact; use pallet_subtensor::rpc_info::{ @@ -8,7 +9,7 @@ use pallet_subtensor::rpc_info::{ metagraph::{Metagraph, SelectiveMetagraph}, neuron_info::{NeuronInfo, NeuronInfoLite}, show_subnet::SubnetState, - stake_info::StakeInfo, + stake_info::{StakeAvailability, StakeInfo}, subnet_info::{ SubnetHyperparams, SubnetHyperparamsV2, SubnetHyperparamsV3, SubnetInfo, SubnetInfov2, }, @@ -59,12 +60,14 @@ sp_api::decl_runtime_apis! { fn get_selective_mechagraph(netuid: NetUid, subid: MechId, metagraph_indexes: Vec) -> Option>; fn get_subnet_to_prune() -> Option; fn get_subnet_account_id(netuid: NetUid) -> Option; + fn get_next_epoch_start_block(netuid: NetUid) -> Option; } pub trait StakeInfoRuntimeApi { fn get_stake_info_for_coldkey( coldkey_account: AccountId32 ) -> Vec>; fn get_stake_info_for_coldkeys( coldkey_accounts: Vec ) -> Vec<(AccountId32, Vec>)>; fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account: AccountId32, coldkey_account: AccountId32, netuid: NetUid ) -> Option>; + fn get_stake_availability_for_coldkeys( coldkey_accounts: Vec, netuids: Option> ) -> BTreeMap>; fn get_stake_fee( origin: Option<(AccountId32, NetUid)>, origin_coldkey_account: AccountId32, destination: Option<(AccountId32, NetUid)>, destination_coldkey_account: AccountId32, amount: u64 ) -> u64; fn get_coldkey_lock(coldkey: AccountId32, netuid: NetUid) -> Option; fn get_hotkey_conviction(hotkey: AccountId32, netuid: NetUid) -> U64F64; diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 1dd62bab0b..809babaae9 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -4,27 +4,37 @@ use crate::Pallet as Subtensor; use crate::staking::lock::LockState; +use crate::subnets::mechanism::GLOBAL_MAX_SUBNET_COUNT; use crate::*; -use codec::Compact; +use codec::{Compact, Encode}; use frame_benchmarking::v2::*; -use frame_support::{StorageDoubleMap, assert_ok}; +use frame_support::{ + StorageDoubleMap, assert_ok, + dispatch::{DispatchInfo, PostDispatchInfo}, + traits::{Get, IsSubType, OriginTrait}, +}; use frame_system::{RawOrigin, pallet_prelude::BlockNumberFor}; pub use pallet::*; -use sp_core::H256; +use sp_core::{H160, H256, ecdsa}; use sp_runtime::{ BoundedVec, Percent, - traits::{BlakeTwo256, Hash}, + traits::{BlakeTwo256, Dispatchable, Hash}, }; -use sp_std::collections::btree_set::BTreeSet; +use sp_std::collections::{btree_set::BTreeSet, vec_deque::VecDeque}; use sp_std::vec; -use substrate_fixed::types::{U64F64, U96F32}; -use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance}; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::{AlphaBalance, NetUid, NetUidStorageIndex, TaoBalance}; use subtensor_swap_interface::SwapHandler; #[benchmarks( where - T: pallet_balances::Config, + T: pallet_balances::Config + pallet_shield::Config, ::ExistentialDeposit: Get, + ::RuntimeCall: + Dispatchable, Info = DispatchInfo, PostInfo = PostDispatchInfo> + + IsSubType> + + IsSubType>, + OriginFor: Clone + OriginTrait, )] mod pallet_benchmarks { use super::*; @@ -85,6 +95,86 @@ mod pallet_benchmarks { ); } + fn set_benchmark_block_number(block_number: u64) { + let block_number: BlockNumberFor = match block_number.try_into() { + Ok(block_number) => block_number, + Err(_) => panic!("benchmark block number must fit into BlockNumberFor"), + }; + + frame_system::Pallet::::set_block_number(block_number); + } + + fn runtime_call(call: Call) -> ::RuntimeCall { + ::RuntimeCall::from(call).into() + } + + fn setup_extension_neuron(netuid: NetUid, hotkey: &T::AccountId) { + Subtensor::::init_new_network(netuid, 0); + Subtensor::::set_max_allowed_uids(netuid, GLOBAL_MAX_SUBNET_COUNT); + Subtensor::::append_neuron(netuid, hotkey, 0); + } + + fn benchmark_evm_secret_key() -> libsecp256k1::SecretKey { + let seed = [42u8; 32]; + + match libsecp256k1::SecretKey::parse(&seed) { + Ok(secret_key) => secret_key, + Err(_) => panic!("benchmark EVM secret key must be valid"), + } + } + + fn evm_key_from_secret_key(secret_key: &libsecp256k1::SecretKey) -> H160 { + let public_key = libsecp256k1::PublicKey::from_secret_key(secret_key); + let uncompressed = public_key.serialize(); + + let public_key_without_prefix = match uncompressed.get(1..) { + Some(public_key_without_prefix) => public_key_without_prefix, + None => panic!("uncompressed secp256k1 public key must contain a prefix byte"), + }; + + let hashed_public_key = sp_io::hashing::keccak_256(public_key_without_prefix); + + let evm_key_bytes = match hashed_public_key.get(12..) { + Some(evm_key_bytes) => evm_key_bytes, + None => panic!("keccak256 hash must be 32 bytes"), + }; + + H160::from_slice(evm_key_bytes) + } + + fn signature_for_associate_evm_key( + hotkey: &T::AccountId, + block_number: u64, + secret_key: &libsecp256k1::SecretKey, + ) -> ecdsa::Signature { + let block_hash = sp_io::hashing::keccak_256(block_number.encode().as_ref()); + + let mut message = hotkey.encode(); + message.extend_from_slice(&block_hash); + + let message_hash = Subtensor::::hash_message_eip191(message); + let secp_message = libsecp256k1::Message::parse(&message_hash); + + let (secp_signature, recovery_id) = libsecp256k1::sign(&secp_message, secret_key); + + let mut signature = [0u8; 65]; + let serialized_signature = secp_signature.serialize(); + + let signature_bytes = match signature.get_mut(..64) { + Some(signature_bytes) => signature_bytes, + None => panic!("benchmark ECDSA signature buffer must contain 64 signature bytes"), + }; + signature_bytes.copy_from_slice(&serialized_signature); + + let recovery_id_byte = match signature.get_mut(64) { + Some(recovery_id_byte) => recovery_id_byte, + None => panic!("benchmark ECDSA signature buffer must contain a recovery id byte"), + }; + *recovery_id_byte = recovery_id.serialize(); + + ecdsa::Signature::from_raw(signature) + } + #[benchmark] fn register() { let netuid = NetUid::from(1); @@ -440,19 +530,15 @@ mod pallet_benchmarks { salt.clone(), version_key, )); - let commit_block = Subtensor::::get_current_block_as_u64(); assert_ok!(Subtensor::::commit_weights( RawOrigin::Signed(hotkey.clone()).into(), netuid, commit_hash, )); - let (first_reveal_block, _) = Subtensor::::get_reveal_blocks(netuid, commit_block); - let reveal_block: BlockNumberFor = first_reveal_block - .try_into() - .ok() - .expect("can't convert to block number"); - frame_system::Pallet::::set_block_number(reveal_block); + // Advance the epoch counter into the commit's reveal window. + let reveal_period = Subtensor::::get_reveal_period(netuid); + SubnetEpochIndex::::mutate(netuid, |e| *e = e.saturating_add(reveal_period)); #[extrinsic_call] _( @@ -676,7 +762,6 @@ mod pallet_benchmarks { let mut salts_list = Vec::new(); let mut version_keys = Vec::new(); - let commit_block = Subtensor::::get_current_block_as_u64(); for i in 0..num_commits { let uids = vec![0u16]; let values = vec![i as u16]; @@ -704,12 +789,9 @@ mod pallet_benchmarks { version_keys.push(version_key_i); } - let (first_reveal_block, _) = Subtensor::::get_reveal_blocks(netuid, commit_block); - let reveal_block: BlockNumberFor = first_reveal_block - .try_into() - .ok() - .expect("can't convert to block number"); - frame_system::Pallet::::set_block_number(reveal_block); + // Advance the epoch counter into the reveal window for these commits. + let reveal_period = Subtensor::::get_reveal_period(netuid); + SubnetEpochIndex::::mutate(netuid, |e| *e = e.saturating_add(reveal_period)); #[extrinsic_call] _( @@ -863,6 +945,7 @@ mod pallet_benchmarks { add_balance_to_coldkey_account::(&coldkey.clone(), initial_balance); add_lock::(&coldkey, netuid); + // Price = 0.01 let tao_reserve = TaoBalance::from(1_000_000_000_000_u64); let alpha_in = AlphaBalance::from(100_000_000_000_000_u64); set_reserves::(netuid, tao_reserve, alpha_in); @@ -877,7 +960,7 @@ mod pallet_benchmarks { // by swapping 100 TAO let current_price = T::SwapInterface::current_alpha_price(netuid); let limit = current_price - .saturating_mul(U96F32::saturating_from_num(1_001_000_000)) + .saturating_mul(U64F64::saturating_from_num(1_001_000_000)) .saturating_to_num::() .into(); let amount_to_be_staked = TaoBalance::from(100_000_000_000_u64); @@ -934,8 +1017,6 @@ mod pallet_benchmarks { let _ = Subtensor::::create_account_if_non_existent(&coldkey, &destination); - StakingOperationRateLimiter::::remove((origin.clone(), coldkey.clone(), netuid)); - #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), @@ -966,6 +1047,7 @@ mod pallet_benchmarks { let hotkey: T::AccountId = account("Alice", 0, seed); Subtensor::::set_burn(netuid, benchmark_registration_burn()); + // Price = 0.01 let tao_reserve = TaoBalance::from(1_000_000_000_000_u64); let alpha_in = AlphaBalance::from(100_000_000_000_000_u64); set_reserves::(netuid, tao_reserve, alpha_in); @@ -991,8 +1073,6 @@ mod pallet_benchmarks { let amount_unstaked = AlphaBalance::from(30_000_000_000_u64); - StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); - #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), @@ -1049,12 +1129,10 @@ mod pallet_benchmarks { let current_price = T::SwapInterface::current_alpha_price(netuid); let limit = current_price - .saturating_mul(U96F32::saturating_from_num(500_000_000)) + .saturating_mul(U64F64::saturating_from_num(999_900_000)) .saturating_to_num::() .into(); - StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); - #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), @@ -1118,8 +1196,6 @@ mod pallet_benchmarks { allow )); - StakingOperationRateLimiter::::remove((hot.clone(), coldkey.clone(), netuid1)); - #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), @@ -1172,8 +1248,6 @@ mod pallet_benchmarks { let _ = Subtensor::::create_account_if_non_existent(&dest, &hot); - StakingOperationRateLimiter::::remove((hot.clone(), coldkey.clone(), netuid)); - #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), @@ -1229,8 +1303,6 @@ mod pallet_benchmarks { let alpha_to_swap = Subtensor::::get_stake_for_hotkey_and_coldkey_on_subnet(&hot, &coldkey, netuid1); - StakingOperationRateLimiter::::remove((hot.clone(), coldkey.clone(), netuid1)); - #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), @@ -1584,8 +1656,6 @@ mod pallet_benchmarks { staked_amt )); - StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); - #[extrinsic_call] _(RawOrigin::Signed(coldkey), hotkey); } @@ -1627,7 +1697,7 @@ mod pallet_benchmarks { // by swapping 1 TAO let current_price = T::SwapInterface::current_alpha_price(netuid); let limit = current_price - .saturating_mul(U96F32::saturating_from_num(500_000_000)) + .saturating_mul(U64F64::saturating_from_num(500_000_000)) .saturating_to_num::() .into(); let staked_amt = TaoBalance::from(1_000_000_000_u64); @@ -1640,8 +1710,6 @@ mod pallet_benchmarks { staked_amt )); - StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); - #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), @@ -2148,6 +2216,268 @@ mod pallet_benchmarks { ); } + #[benchmark] + fn associate_evm_key() { + let netuid = NetUid::from(1); + let tempo: u16 = 1; + + let coldkey: T::AccountId = account("Test", 0, 1); + let hotkey: T::AccountId = account("Alice", 0, 1); + + Subtensor::::init_new_network(netuid, tempo); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_network_registration_allowed(netuid, true); + Subtensor::::set_max_allowed_uids(netuid, 4096); + Subtensor::::set_burn(netuid, benchmark_registration_burn()); + + seed_swap_reserves::(netuid); + fund_for_registration::(netuid, &coldkey); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), + netuid, + hotkey.clone() + )); + + let uid = match Subtensor::::get_uid_for_net_and_hotkey(netuid, &hotkey) { + Ok(uid) => uid, + Err(_) => panic!("registered benchmark hotkey must have a uid"), + }; + + // No existing association means `block_associated` is treated as 0. + // Move the block forward enough to satisfy: + // now - 0 >= T::EvmKeyAssociateRateLimit::get() + let benchmark_block_number = T::EvmKeyAssociateRateLimit::get().saturating_add(1); + + let benchmark_block: BlockNumberFor = match benchmark_block_number.try_into() { + Ok(benchmark_block) => benchmark_block, + Err(_) => panic!("benchmark block number must fit into BlockNumberFor"), + }; + + frame_system::Pallet::::set_block_number(benchmark_block); + + let block_number = Subtensor::::get_current_block_as_u64(); + + let evm_secret_key = benchmark_evm_secret_key(); + let evm_key = evm_key_from_secret_key(&evm_secret_key); + + let signature = + signature_for_associate_evm_key::(&hotkey, block_number, &evm_secret_key); + + #[extrinsic_call] + _( + RawOrigin::Signed(hotkey.clone()), + netuid, + evm_key, + block_number, + signature, + ); + + assert_eq!( + AssociatedEvmAddress::::get(netuid, uid), + Some((evm_key, block_number)) + ); + } + + #[benchmark] + fn set_tempo() { + let netuid = NetUid::from(1); + let coldkey: T::AccountId = account("Owner", 0, 1); + + Subtensor::::init_new_network(netuid, 1u16); + SubnetOwner::::insert(netuid, coldkey.clone()); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_commit_reveal_weights_enabled(netuid, false); + Subtensor::::set_admin_freeze_window(0); + + #[extrinsic_call] + _(RawOrigin::Signed(coldkey.clone()), netuid, MIN_TEMPO); + } + + #[benchmark] + fn set_activity_cutoff_factor() { + let netuid = NetUid::from(1); + let coldkey: T::AccountId = account("Owner", 0, 1); + + Subtensor::::init_new_network(netuid, 1u16); + SubnetOwner::::insert(netuid, coldkey.clone()); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_admin_freeze_window(0); + + #[extrinsic_call] + _( + RawOrigin::Signed(coldkey.clone()), + netuid, + INITIAL_ACTIVITY_CUTOFF_FACTOR_MILLI, + ); + } + + #[benchmark] + fn trigger_epoch() { + let netuid = NetUid::from(1); + let coldkey: T::AccountId = account("Owner", 0, 1); + + Subtensor::::init_new_network(netuid, 1u16); + SubnetOwner::::insert(netuid, coldkey.clone()); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_commit_reveal_weights_enabled(netuid, false); + Subtensor::::set_admin_freeze_window(0); + + #[extrinsic_call] + _(RawOrigin::Signed(coldkey.clone()), netuid); + } + + #[benchmark] + fn check_coldkey_swap_extension() { + let coldkey: T::AccountId = account("coldkey", 0, 1); + let new_coldkey: T::AccountId = account("new_coldkey", 0, 1); + let hotkey: T::AccountId = account("hotkey", 0, 1); + let new_coldkey_hash: T::Hash = ::Hashing::hash_of(&new_coldkey); + let now = frame_system::Pallet::::block_number(); + let call = runtime_call::(Call::::register_network { hotkey }); + + ColdkeySwapAnnouncements::::insert(&coldkey, (now, new_coldkey_hash)); + ColdkeySwapDisputes::::insert(&coldkey, now); + + #[block] + { + assert_eq!( + CheckColdkeySwap::::check(&coldkey, &call), + Err(Error::::ColdkeySwapDisputed) + ); + } + } + + #[benchmark] + fn check_weights_extension() { + let netuid = NetUid::from(1); + let hotkey: T::AccountId = account("hotkey", 0, 1); + let netuid_index = NetUidStorageIndex::from(netuid); + let uids: Vec = vec![0]; + let values: Vec = vec![10]; + let salt: Vec = vec![8]; + let version_key = 0_u64; + + setup_extension_neuron::(netuid, &hotkey); + Subtensor::::set_stake_threshold(0); + + let commit_hash = Subtensor::::get_commit_hash( + &hotkey, + netuid_index, + &uids, + &values, + &salt, + version_key, + ); + let mut commits = VecDeque::new(); + for i in 0..9 { + commits.push_back((H256::repeat_byte(i + 1), 0, 0, 0)); + } + commits.push_back((commit_hash, 0, 0, 0)); + WeightCommits::::insert(netuid_index, &hotkey, commits); + + let reveal_period = Subtensor::::get_reveal_period(netuid); + SubnetEpochIndex::::insert(netuid, reveal_period); + + let call = Call::::reveal_weights { + netuid, + uids, + values, + salt, + version_key, + }; + + #[block] + { + assert_ok!(CheckWeights::::check(&hotkey, &call)); + } + } + + #[benchmark] + fn check_rate_limits_extension() { + let netuid = NetUid::from(1); + let hotkey: T::AccountId = account("hotkey", 0, 1); + let netuid_index = NetUidStorageIndex::from(netuid); + let call = Call::::set_weights { + netuid, + dests: vec![0], + weights: vec![1], + version_key: 0, + }; + + setup_extension_neuron::(netuid, &hotkey); + Subtensor::::set_commit_reveal_weights_enabled(netuid, false); + Subtensor::::set_weights_set_rate_limit(netuid, 1); + Subtensor::::set_last_update_for_uid(netuid_index, 0, 1); + set_benchmark_block_number::(3); + + #[block] + { + assert_ok!(CheckRateLimits::::check(&hotkey, &call)); + } + } + + #[benchmark] + fn check_delegate_take_extension() { + let coldkey: T::AccountId = account("coldkey", 0, 1); + let hotkey: T::AccountId = account("hotkey", 0, 1); + let call = Call::::increase_take { + hotkey: hotkey.clone(), + take: Subtensor::::get_max_delegate_take(), + }; + + Owner::::insert(&hotkey, &coldkey); + + #[block] + { + assert_ok!(CheckDelegateTake::::check(&coldkey, &call)); + } + } + + #[benchmark] + fn check_serving_endpoints_extension() { + let hotkey: T::AccountId = account("hotkey", 0, 1); + let netuid = NetUid::from(1); + let call = Call::::serve_axon { + netuid, + version: 1, + ip: u128::from(u32::from_be_bytes([8, 8, 8, 8])), + port: 1, + ip_type: 4, + protocol: 0, + placeholder1: 0, + placeholder2: 0, + }; + + Uids::::insert(netuid, &hotkey, 0); + + #[block] + { + assert_ok!(CheckServingEndpoints::::check(&hotkey, &call)); + } + } + + #[benchmark] + fn check_evm_key_association_extension() { + let netuid = NetUid::from(1); + let hotkey: T::AccountId = account("hotkey", 0, 1); + let block_number = T::EvmKeyAssociateRateLimit::get().saturating_add(1); + let call = Call::::associate_evm_key { + netuid, + evm_key: H160::zero(), + block_number, + signature: ecdsa::Signature::from_raw([0_u8; 65]), + }; + + setup_extension_neuron::(netuid, &hotkey); + set_benchmark_block_number::(block_number); + + #[block] + { + assert_ok!(CheckEvmKeyAssociation::::check(&hotkey, &call)); + } + } + impl_benchmark_test_suite!( Subtensor, crate::tests::mock::new_test_ext(1), diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index fac924ccf4..00f1ac16a9 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -36,9 +36,11 @@ impl Pallet { } fn try_set_pending_children(block_number: u64) { + // Called *after* `run_coinbase` has advanced `LastEpochBlock` for any + // subnet whose epoch slot fired this block — `should_run_epoch` is no + // longer true. Detect "epoch just fired" by `LastEpochBlock == block`. for netuid in Self::get_all_subnet_netuids() { - if Self::should_run_epoch(netuid, block_number) { - // Set pending children on the epoch. + if LastEpochBlock::::get(netuid) == block_number { Self::do_set_pending_children(netuid); } } @@ -77,8 +79,18 @@ impl Pallet { } pub fn reveal_crv3_commits() { - let netuids: Vec = Self::get_all_subnet_netuids(); - for netuid in netuids.into_iter().filter(|netuid| *netuid != NetUid::ROOT) { + let current_block = Self::get_current_block_as_u64(); + let subnets: Vec = Self::get_all_subnet_netuids() + .into_iter() + .filter(|netuid| *netuid != NetUid::ROOT) + .collect(); + // Subnets whose epoch is due this block but deferred by the per-block cap. + let deferred = Self::epochs_deferred_this_block(&subnets, current_block); + + for netuid in subnets.into_iter() { + if deferred.contains(&netuid) { + continue; + } // Reveal matured weights. if let Err(e) = Self::reveal_crv3_commits_for_subnet(netuid) { log::warn!("Failed to reveal commits for subnet {netuid} due to error: {e:?}"); diff --git a/pallets/subtensor/src/coinbase/mod.rs b/pallets/subtensor/src/coinbase/mod.rs index c51bf58d1d..5184e2e3c0 100644 --- a/pallets/subtensor/src/coinbase/mod.rs +++ b/pallets/subtensor/src/coinbase/mod.rs @@ -7,3 +7,4 @@ pub mod root; pub mod run_coinbase; pub mod subnet_emissions; pub mod tao; +pub mod tempo_control; diff --git a/pallets/subtensor/src/coinbase/reveal_commits.rs b/pallets/subtensor/src/coinbase/reveal_commits.rs index 3d43cfba29..a5cddd6856 100644 --- a/pallets/subtensor/src/coinbase/reveal_commits.rs +++ b/pallets/subtensor/src/coinbase/reveal_commits.rs @@ -38,8 +38,9 @@ impl Pallet { /// The `reveal_crv3_commits` function is run at the very beginning of epoch `n`, pub fn reveal_crv3_commits_for_subnet(netuid: NetUid) -> dispatch::DispatchResult { let reveal_period = Self::get_reveal_period(netuid); - let cur_block = Self::get_current_block_as_u64(); - let cur_epoch = Self::get_epoch_index(netuid, cur_block); + // If the subnet is deferred past this block the + // commits are taken once here and the later block(s) become no-ops. + let cur_epoch = Self::current_epoch_with_lookahead(netuid); // Weights revealed must have been committed during epoch `cur_epoch - reveal_period`. let reveal_epoch = cur_epoch.saturating_sub(reveal_period); diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 35069ef6b5..b4db388621 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -18,7 +18,7 @@ use super::*; use crate::CommitmentsInterface; use safe_math::*; -use substrate_fixed::types::{I64F64, U96F32}; +use substrate_fixed::types::{I64F64, U64F64}; use subtensor_runtime_common::{AlphaBalance, NetUid, NetUidStorageIndex, TaoBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -213,8 +213,6 @@ impl Pallet { Self::finalize_all_subnet_root_dividends(netuid); // --- Perform the cleanup before removing the network. - // Will handle it in dissolve network PR. - T::SwapInterface::dissolve_all_liquidity_providers(netuid).map_err(|e| e.error)?; Self::destroy_alpha_in_out_stakes(netuid)?; T::SwapInterface::clear_protocol_liquidity(netuid)?; T::CommitmentsInterface::purge_netuid(netuid); @@ -284,6 +282,10 @@ impl Pallet { MaxAllowedUids::::remove(netuid); ImmunityPeriod::::remove(netuid); ActivityCutoff::::remove(netuid); + ActivityCutoffFactorMilli::::remove(netuid); + LastEpochBlock::::remove(netuid); + PendingEpochAt::::remove(netuid); + SubnetEpochIndex::::remove(netuid); MinAllowedWeights::::remove(netuid); RegistrationsThisInterval::::remove(netuid); POWRegistrationsThisInterval::::remove(netuid); @@ -302,7 +304,6 @@ impl Pallet { SubnetEmaProtocolFlow::::remove(netuid); SubnetExcessTao::::remove(netuid); SubnetRootSellTao::::remove(netuid); - SubnetTaoProvided::::remove(netuid); // --- 13. Token / mechanism / registration toggles. TokenSymbol::::remove(netuid); @@ -463,21 +464,6 @@ impl Pallet { TransactionKeyLastBlock::::remove((hot, netuid, name)); } } - // StakingOperationRateLimiter NMAP: (hot, cold, netuid) → bool - { - let to_rm: sp_std::vec::Vec<(T::AccountId, T::AccountId)> = - StakingOperationRateLimiter::::iter() - .filter_map( - |((hot, cold, n), _)| { - if n == netuid { Some((hot, cold)) } else { None } - }, - ) - .collect(); - for (hot, cold) in to_rm { - StakingOperationRateLimiter::::remove((hot, cold, netuid)); - } - } - // --- 22. Subnet leasing: remove mapping and any lease-scoped state linked to this netuid. if let Some(lease_id) = SubnetUidToLeaseId::::take(netuid) { SubnetLeases::::remove(lease_id); @@ -608,7 +594,7 @@ impl Pallet { let current_block: u64 = Self::get_current_block_as_u64(); let mut candidate_netuid: Option = None; - let mut candidate_price: U96F32 = U96F32::saturating_from_num(u128::MAX); + let mut candidate_price: U64F64 = U64F64::saturating_from_num(u128::MAX); let mut candidate_timestamp: u64 = u64::MAX; for (netuid, added) in NetworksAdded::::iter() { @@ -623,7 +609,7 @@ impl Pallet { continue; } - let price: U96F32 = Self::get_moving_alpha_price(netuid); + let price: U64F64 = Self::get_moving_alpha_price(netuid); // If tie on price, earliest registration wins. if price < candidate_price diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 60f6253643..d7c75964b2 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -1,9 +1,9 @@ use super::*; use crate::coinbase::tao::CreditOf; -use alloc::collections::BTreeMap; +use alloc::collections::{BTreeMap, BTreeSet}; use frame_support::traits::Imbalance; use safe_math::*; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::{U64F64, U96F32}; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -85,13 +85,16 @@ impl Pallet { let tao_to_swap_with: TaoBalance = tou64!(excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + // Inject tao and alpha into protocol liquidity. In theorry, it may not always + // be a success (returned values are 0s) in case of high liquidity disbalance + let (actual_injected_tao, actual_injected_alpha) = + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); + // Clear per-block pool-side emission counters up front so a subnet // disabled this block does not display stale values from an earlier block. SubnetExcessTao::::insert(*netuid_i, TaoBalance::ZERO); SubnetTaoInEmission::::insert(*netuid_i, TaoBalance::ZERO); - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); - if tao_to_swap_with > TaoBalance::ZERO { // Turn excess_tao portion of credit into TaoBalance on subnet account match Self::spend_tao(&subnet_account_id, remaining_credit, tao_to_swap_with) { @@ -128,37 +131,34 @@ impl Pallet { } // Inject Alpha in. - let alpha_in_i = - AlphaBalance::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); - SubnetAlphaInEmission::::insert(*netuid_i, alpha_in_i); + SubnetAlphaInEmission::::insert(*netuid_i, actual_injected_alpha); // Mint alpha and resolve to alpha reserve - Self::resolve_to_alpha_in(Self::mint_alpha(*netuid_i, alpha_in_i)); + Self::resolve_to_alpha_in(Self::mint_alpha(*netuid_i, actual_injected_alpha)); // Inject TAO in. - let injected_tao: TaoBalance = - tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - if !injected_tao.is_zero() { - match Self::spend_tao(&subnet_account_id, remaining_credit, injected_tao) { + if !actual_injected_tao.is_zero() { + match Self::spend_tao(&subnet_account_id, remaining_credit, actual_injected_tao) + { Ok(remainder) => { remaining_credit = remainder; - SubnetTaoInEmission::::insert(*netuid_i, injected_tao); + SubnetTaoInEmission::::insert(*netuid_i, actual_injected_tao); SubnetTAO::::mutate(*netuid_i, |total| { - *total = total.saturating_add(injected_tao); + *total = total.saturating_add(actual_injected_tao); }); TotalStake::::mutate(|total| { - *total = total.saturating_add(injected_tao); + *total = total.saturating_add(actual_injected_tao); }); // Record emission injection as protocol inflow. - Self::record_protocol_inflow(*netuid_i, injected_tao); + Self::record_protocol_inflow(*netuid_i, actual_injected_tao); } Err(remainder) => { remaining_credit = remainder; let remaining_balance = remaining_credit.peek(); log::error!( - "Failed to spend credit: injected_tao = {injected_tao:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" + "Failed to spend credit: injected_tao = {actual_injected_tao:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" ); } } @@ -198,7 +198,8 @@ impl Pallet { log::debug!("alpha_emission_i: {alpha_emission_i:?}"); // Get subnet price. - let price_i: U96F32 = T::SwapInterface::current_alpha_price(netuid_i.into()); + let price_i: U96F32 = + U96F32::saturating_from_num(T::SwapInterface::current_alpha_price(netuid_i.into())); log::debug!("price_i: {price_i:?}"); let mut tao_in_i: U96F32 = tao_emission_i; @@ -320,6 +321,29 @@ impl Pallet { } } + /// Subnets whose epoch slot is due *this* block but is deferred by the per-block + /// cap (`MaxEpochsPerBlock`). + pub fn epochs_deferred_this_block(subnets: &[NetUid], current_block: u64) -> BTreeSet { + let cap = Self::get_max_epochs_per_block() as u32; + let mut deferred: BTreeSet = BTreeSet::new(); + let mut epochs_run_this_block: u32 = 0; + + for &netuid in subnets.iter() { + if !Self::should_run_epoch(netuid, current_block) { + continue; + } + // Per-block cap — due subnets beyond the limit are deferred. + if epochs_run_this_block >= cap { + deferred.insert(netuid); + continue; + } + if Self::is_epoch_input_state_consistent(netuid) { + epochs_run_this_block = epochs_run_this_block.saturating_add(1); + } + } + deferred + } + pub fn drain_pending( subnets: &[NetUid], current_block: u64, @@ -329,19 +353,36 @@ impl Pallet { NetUid, (AlphaBalance, AlphaBalance, AlphaBalance, AlphaBalance), > = BTreeMap::new(); - // --- Drain pending emissions for all subnets hat are at their tempo. - // Run the epoch for *all* subnets, even if we don't emit anything. + // Per-block cap on number of epochs that may run; the rest are deferred 1 block forward + // by setting `PendingEpochAt`. + let max_epochs_per_block = Self::get_max_epochs_per_block() as u32; + let mut epochs_run_this_block: u32 = 0; + for &netuid in subnets.iter() { - // Increment blocks since last step. + // Increment blocks since last *successful* step (existing semantics). BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); - // Run the epoch if applicable. - if Self::should_run_epoch(netuid, current_block) - && Self::is_epoch_input_state_consistent(netuid) - { - // Restart counters. + if !Self::should_run_epoch(netuid, current_block) { + continue; + } + + // Per-block cap — defer if already at limit. + if epochs_run_this_block >= max_epochs_per_block { + let next_block = current_block.saturating_add(1); + PendingEpochAt::::insert(netuid, next_block); + Self::deposit_event(Event::EpochDeferred { + netuid, + from_block: current_block, + to_block: next_block, + }); + continue; + } + + if Self::is_epoch_input_state_consistent(netuid) { + // Reset blocks-since counter; LastMechansimStepBlock is written + // post-distribute (see the caller), so bonds masking can read the + // previous successful run. BlocksSinceLastStep::::insert(netuid, 0); - LastMechansimStepBlock::::insert(netuid, current_block); // Get and drain the subnet pending emission. let pending_server_alpha = PendingServerEmission::::get(netuid); @@ -368,11 +409,24 @@ impl Pallet { owner_cut, ), ); + epochs_run_this_block = epochs_run_this_block.saturating_add(1); // Reserved for potential future enhancements. // Ownership update logic based on conviction is currently inactive by design. // Self::change_subnet_owner_if_needed(netuid); + } else { + // Schedule advances below; execution skipped. Pending emissions accumulate + // and will be drained by the next successful epoch. + Self::deposit_event(Event::EpochSkipped { + netuid, + block: current_block, + }); } + + // Advance the schedule unconditionally — the slot is consumed. + LastEpochBlock::::insert(netuid, current_block); + PendingEpochAt::::insert(netuid, 0); + SubnetEpochIndex::::mutate(netuid, |idx| *idx = idx.saturating_add(1)); } emissions_to_distribute } @@ -383,6 +437,7 @@ impl Pallet { (AlphaBalance, AlphaBalance, AlphaBalance, AlphaBalance), >, ) { + let current_block = Self::get_current_block_as_u64(); for ( &netuid, &(pending_server_alpha, pending_validator_alpha, pending_root_alpha, pending_owner_cut), @@ -396,18 +451,19 @@ impl Pallet { pending_root_alpha, pending_owner_cut, ); + LastMechansimStepBlock::::insert(netuid, current_block); } } pub fn get_network_root_sell_flag(subnets_to_emit_to: &[NetUid]) -> bool { - let total_ema_price: U96F32 = subnets_to_emit_to + let total_ema_price: U64F64 = subnets_to_emit_to .iter() .map(|netuid| Self::get_moving_alpha_price(*netuid)) .sum(); // If the total EMA price is less than or equal to 1 // then we WILL NOT root sell. - total_ema_price > U96F32::saturating_from_num(1) + total_ema_price > U64F64::saturating_from_num(1) } pub fn calculate_dividends_and_incentives( @@ -1028,28 +1084,57 @@ impl Pallet { /// # Returns /// * `bool` - True if the epoch should run, false otherwise. pub fn should_run_epoch(netuid: NetUid, current_block: u64) -> bool { - Self::blocks_until_next_epoch(netuid, Self::get_tempo(netuid), current_block) == 0 + let tempo = Self::get_tempo(netuid); + if tempo == 0 { + return false; + } + let pending = PendingEpochAt::::get(netuid); + if pending > 0 && current_block >= pending { + return true; + } + if BlocksSinceLastStep::::get(netuid) > MAX_TEMPO as u64 { + return true; + } + let last = LastEpochBlock::::get(netuid); + let blocks_since = current_block.saturating_sub(last); + blocks_since >= tempo as u64 } - /// Helper function which returns the number of blocks remaining before we will run the epoch on this - /// network. Networks run their epoch when (block_number + netuid + 1 ) % (tempo + 1) = 0 - /// tempo | netuid | # first epoch block - /// 1 0 0 - /// 1 1 1 - /// 2 0 1 - /// 2 1 0 - /// 100 0 99 - /// 100 1 98 - /// Special case: tempo = 0, the network never runs. - /// - pub fn blocks_until_next_epoch(netuid: NetUid, tempo: u16, block_number: u64) -> u64 { + /// Returns the number of blocks remaining before the next automatic epoch under the + /// stateful scheduler (period `tempo`, anchored on `LastEpochBlock`). Does NOT account for: + /// - `PendingEpochAt` (owner-triggered manual fire — could happen sooner), + /// - `BlocksSinceLastStep > MAX_TEMPO` safety-net, + /// - per-block-cap defer (could push the actual fire one or more blocks later) + /// Used by the admin-freeze-window predicate and external tooling. Returns `u64::MAX` when + /// `tempo == 0` (legacy defensive short-circuit). + pub fn blocks_until_next_auto_epoch(netuid: NetUid, tempo: u16, block_number: u64) -> u64 { if tempo == 0 { return u64::MAX; } - let netuid_plus_one = (u16::from(netuid) as u64).saturating_add(1); - let tempo_plus_one = (tempo as u64).saturating_add(1); - let adjusted_block = block_number.wrapping_add(netuid_plus_one); - let remainder = adjusted_block.checked_rem(tempo_plus_one).unwrap_or(0); - (tempo as u64).saturating_sub(remainder) + let last = LastEpochBlock::::get(netuid); + // Period is `tempo`: next firing at `last + tempo`. + let next_auto = last.saturating_add(tempo as u64); + next_auto.saturating_sub(block_number) + } + + /// Returns the absolute block number at which the next epoch is expected to fire for the + /// given subnet, considering both the automatic schedule (`LastEpochBlock + tempo`) and + /// any owner-triggered `PendingEpochAt`. Returns `None` if `tempo == 0` (subnet does not run). + /// Does NOT account for the per-block cap deferral or the `BlocksSinceLastStep > MAX_TEMPO` + /// safety-net (which can fire earlier under extreme drift). + pub fn get_next_epoch_start_block(netuid: NetUid) -> Option { + let tempo = Self::get_tempo(netuid); + if tempo == 0 { + return None; + } + let last = LastEpochBlock::::get(netuid); + let auto_next = last.saturating_add(tempo as u64); + + let pending = PendingEpochAt::::get(netuid); + if pending > 0 { + Some(auto_next.min(pending)) + } else { + Some(auto_next) + } } } diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index d7b35166d2..f378e3e930 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -5,6 +5,19 @@ use substrate_fixed::transcendental::{exp, ln}; use substrate_fixed::types::{I32F32, I64F64, U64F64, U96F32}; impl Pallet { + /// Returns the subnets that are eligible to receive emissions. + /// + /// # Parameters + /// - `subnets`: Candidate subnet IDs to evaluate in order. + /// + /// # Returns + /// A vector containing the candidate subnet IDs that are non-root, have + /// started emissions, have subtokens enabled, and currently allow network + /// registration. + /// + /// AI-readable: This output is passed to `get_shares_flow`, so changing these + /// eligibility rules also changes which subnet user TAO flow EMAs and protocol + /// flow EMAs are advanced during emission sharing. pub fn get_subnets_to_emit_to(subnets: &[NetUid]) -> Vec { // Filter out root subnet. // Filter out subnets with no first emission block number. @@ -246,8 +259,13 @@ impl Pallet { let zero = I64F64::saturating_from_num(0); // Always update both EMAs (keeps protocol EMA warm for when toggled on). - // Fixes #2667: protocol EMA accumulator was only drained when enabled, - // causing a shock on toggle. + // Note: + // User TAO EMAs are updated every time this method runs because get_ema_flow() + // is called before the NetTaoFlowEnabled branch. Protocol EMAs are different: + // update_ema_protocol_flow() is only called while NetTaoFlowEnabled is true. + // If net flow is disabled, protocol flow keeps accumulating in SubnetProtocolFlow + // and SubnetEmaProtocolFlow is not advanced/reset, so toggling net flow back on + // applies stale accumulated protocol flow in the next EMA update. let subnet_emas: Vec<(NetUid, I64F64, I64F64)> = subnets_to_emit_to .iter() .map(|netuid| { diff --git a/pallets/subtensor/src/coinbase/tao.rs b/pallets/subtensor/src/coinbase/tao.rs index 33dbda57fb..0dee496c3b 100644 --- a/pallets/subtensor/src/coinbase/tao.rs +++ b/pallets/subtensor/src/coinbase/tao.rs @@ -273,6 +273,11 @@ impl Pallet { credit: CreditOf, part: BalanceOf, ) -> Result, CreditOf> { + // Reject overspending. + if credit.peek() < part { + return Err(credit); + } + let (to_spend, remainder) = credit.split(part); match ::Currency::resolve(coldkey, to_spend) { diff --git a/pallets/subtensor/src/coinbase/tempo_control.rs b/pallets/subtensor/src/coinbase/tempo_control.rs new file mode 100644 index 0000000000..c43c53019e --- /dev/null +++ b/pallets/subtensor/src/coinbase/tempo_control.rs @@ -0,0 +1,116 @@ +use super::*; +use crate::Error; +use frame_support::pallet_prelude::DispatchResult; +use sp_runtime::DispatchError; +use subtensor_runtime_common::NetUid; + +use crate::system::pallet_prelude::OriginFor; +use crate::utils::rate_limiting::{Hyperparameter, TransactionType}; + +impl Pallet { + /// Owner-side `set_tempo` implementation. + pub fn do_set_tempo(origin: OriginFor, netuid: NetUid, tempo: u16) -> DispatchResult { + let who = Self::ensure_subnet_owner(origin, netuid)?; + + ensure!( + (MIN_TEMPO..=MAX_TEMPO).contains(&tempo), + Error::::TempoOutOfBounds + ); + + Self::ensure_admin_window_open(netuid)?; + + let tx = TransactionType::TempoUpdate; + ensure!( + tx.passes_rate_limit_on_subnet::(&who, netuid), + Error::::TxRateLimitExceeded + ); + + let now = Self::get_current_block_as_u64(); + + Self::apply_tempo_with_cycle_reset(netuid, tempo); + + tx.set_last_block_on_subnet::(&who, netuid, now); + Ok(()) + } + + /// `set_activity_cutoff_factor` implementation. Callable by the subnet owner + /// (subject to admin freeze window + rate limit) or by root (bypasses both). + pub fn do_set_activity_cutoff_factor( + origin: OriginFor, + netuid: NetUid, + factor_milli: u32, + ) -> DispatchResult { + let maybe_who = Self::ensure_subnet_owner_or_root(origin, netuid)?; + + ensure!( + (MIN_ACTIVITY_CUTOFF_FACTOR_MILLI..=MAX_ACTIVITY_CUTOFF_FACTOR_MILLI) + .contains(&factor_milli), + Error::::ActivityCutoffFactorMilliOutOfBounds + ); + + let tx = TransactionType::OwnerHyperparamUpdate(Hyperparameter::ActivityCutoffFactorMilli); + + // Admin freeze window and per-owner rate limit apply only to the subnet + // owner. Root bypasses both as a governance override. + if let Some(who) = maybe_who.as_ref() { + Self::ensure_admin_window_open(netuid)?; + ensure!( + tx.passes_rate_limit_on_subnet::(who, netuid), + Error::::TxRateLimitExceeded + ); + } + + Self::set_activity_cutoff_factor_milli(netuid, factor_milli); + + if let Some(who) = maybe_who.as_ref() { + let now = Self::get_current_block_as_u64(); + tx.set_last_block_on_subnet::(who, netuid, now); + } + + Ok(()) + } + + /// Owner-side `trigger_epoch` implementation. + /// Schedules the triggered epoch to fire after `AdminFreezeWindow` blocks; that + /// countdown engages the freeze window for the subnet via `is_in_admin_freeze_window`. + pub fn do_trigger_epoch(origin: OriginFor, netuid: NetUid) -> Result<(), DispatchError> { + let who = Self::ensure_subnet_owner(origin, netuid)?; + + // Block triggering to avoid breaking CRv3 reveal + ensure!( + !Self::get_commit_reveal_weights_enabled(netuid), + Error::::DynamicTempoBlockedByCommitReveal + ); + + // No `ensure_admin_window_open` here: trigger *defines* the next epoch. + ensure!( + PendingEpochAt::::get(netuid) == 0, + Error::::EpochTriggerAlreadyPending + ); + + let now = Self::get_current_block_as_u64(); + let window = AdminFreezeWindow::::get() as u64; + + let tempo = Self::get_tempo(netuid); + let remaining = Self::blocks_until_next_auto_epoch(netuid, tempo, now); + ensure!(remaining >= window, Error::::AutoEpochAlreadyImminent); + + let tx = TransactionType::OwnerHyperparamUpdate(Hyperparameter::TriggerEpoch); + ensure!( + tx.passes_rate_limit_on_subnet::(&who, netuid), + Error::::TxRateLimitExceeded + ); + + let fires_at = now.saturating_add(window); + + PendingEpochAt::::insert(netuid, fires_at); + tx.set_last_block_on_subnet::(&who, netuid, now); + + Self::deposit_event(Event::EpochTriggered { + netuid, + by: who, + fires_at, + }); + Ok(()) + } +} diff --git a/pallets/subtensor/src/epoch/run_epoch.rs b/pallets/subtensor/src/epoch/run_epoch.rs index 962c5bbbb4..cbfdc5a0fd 100644 --- a/pallets/subtensor/src/epoch/run_epoch.rs +++ b/pallets/subtensor/src/epoch/run_epoch.rs @@ -169,7 +169,7 @@ impl Pallet { log::trace!("tempo: {tempo:?}"); // Get activity cutoff. - let activity_cutoff: u64 = Self::get_activity_cutoff(netuid) as u64; + let activity_cutoff: u64 = Self::get_activity_cutoff_blocks(netuid); log::trace!("activity_cutoff: {activity_cutoff:?}"); // Last update vector. @@ -205,7 +205,13 @@ impl Pallet { // Recently registered matrix, recently_ij=True if last_tempo was *before* j was last registered. // Mask if: the last tempo block happened *before* the registration block // ==> last_tempo <= registered - let last_tempo: u64 = current_block.saturating_sub(tempo); + // For dynamic tempo - we pick previous-successful-epoch block: `LastMechansimStepBlock + 1` + let lms = LastMechansimStepBlock::::get(netuid); + let last_tempo: u64 = if lms == 0 { + current_block.saturating_sub(tempo) + } else { + lms.saturating_add(1) + }; let recently_registered: Vec = block_at_registration .iter() .map(|registered| last_tempo <= *registered) @@ -595,7 +601,7 @@ impl Pallet { log::trace!("tempo:\n{tempo:?}\n"); // Get activity cutoff. - let activity_cutoff: u64 = Self::get_activity_cutoff(netuid) as u64; + let activity_cutoff: u64 = Self::get_activity_cutoff_blocks(netuid); log::trace!("activity_cutoff: {activity_cutoff:?}"); // Last update vector. @@ -729,24 +735,30 @@ impl Pallet { let uid_of = |acct: &T::AccountId| terms_map.get(acct).map(|t| t.uid); // ---------- v2 ------------------------------------------------------ + // `WeightCommits` tuple: (hash, commit_epoch, commit_block, _). + // Expiry keys off `commit_epoch`; the column mask compares the absolute + // `commit_block` against `block_at_registration` (both block numbers). for (who, q) in WeightCommits::::iter_prefix(netuid_index) { - for (_, cb, _, _) in q.iter() { - if !Self::is_commit_expired(netuid, *cb) { + for (_, commit_epoch, commit_block, _) in q.iter() { + if !Self::is_commit_expired(netuid, *commit_epoch) { if let Some(cell) = uid_of(&who).and_then(|i| commit_blocks.get_mut(i)) { - *cell = (*cell).min(*cb); + *cell = (*cell).min(*commit_block); } break; // earliest active found } } } - // ---------- v3 ------------------------------------------------------ - for (_epoch, q) in TimelockedWeightCommits::::iter_prefix(netuid_index) { - for (who, cb, ..) in q.iter() { - if !Self::is_commit_expired(netuid, *cb) - && let Some(cell) = uid_of(who).and_then(|i| commit_blocks.get_mut(i)) - { - *cell = (*cell).min(*cb); + // ---------- v4 ------------------------------------------------------ + // `TimelockedWeightCommits` is keyed by `commit_epoch`; the value tuple + // carries the absolute `commit_block` in field 1. + for (commit_epoch, q) in TimelockedWeightCommits::::iter_prefix(netuid_index) { + if Self::is_commit_expired(netuid, commit_epoch) { + continue; + } + for (who, commit_block, ..) in q.iter() { + if let Some(cell) = uid_of(who).and_then(|i| commit_blocks.get_mut(i)) { + *cell = (*cell).min(*commit_block); } } } @@ -819,7 +831,13 @@ impl Pallet { // Remove bonds referring to neurons that have registered since last tempo. // Mask if: the last tempo block happened *before* the registration block // ==> last_tempo <= registered - let last_tempo: u64 = current_block.saturating_sub(tempo); + // For dynamic tempo - we pick previous-successful-epoch block: `LastMechansimStepBlock + 1` + let lms = LastMechansimStepBlock::::get(netuid); + let last_tempo: u64 = if lms == 0 { + current_block.saturating_sub(tempo) + } else { + lms.saturating_add(1) + }; bonds = scalar_vec_mask_sparse_matrix( &bonds, last_tempo, @@ -859,7 +877,13 @@ impl Pallet { // Remove bonds referring to neurons that have registered since last tempo. // Mask if: the last tempo block happened *before* the registration block // ==> last_tempo <= registered - let last_tempo: u64 = current_block.saturating_sub(tempo); + // For dynamic tempo - we pick previous-successful-epoch block: `LastMechansimStepBlock + 1` + let lms = LastMechansimStepBlock::::get(netuid); + let last_tempo: u64 = if lms == 0 { + current_block.saturating_sub(tempo) + } else { + lms.saturating_add(1) + }; bonds = scalar_vec_mask_sparse_matrix( &bonds, last_tempo, diff --git a/pallets/subtensor/src/extensions/subtensor.rs b/pallets/subtensor/src/extensions/subtensor.rs index e44c750268..ea91c87c6e 100644 --- a/pallets/subtensor/src/extensions/subtensor.rs +++ b/pallets/subtensor/src/extensions/subtensor.rs @@ -1,24 +1,65 @@ -use crate::{BalancesCall, Call, Config, Error, Pallet, TransactionType}; +use crate::{ + Call, CheckColdkeySwap, CheckDelegateTake, CheckEvmKeyAssociation, CheckRateLimits, + CheckServingEndpoints, CheckWeights, Config, Error, +}; use codec::{Decode, DecodeWithMemTracking, Encode}; -use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; -use frame_support::traits::IsSubType; +use frame_support::{ + dispatch::{DispatchExtension, DispatchInfo, PostDispatchInfo}, + traits::{IsSubType, OriginTrait}, + weights::Weight, +}; use scale_info::TypeInfo; use sp_runtime::traits::{ - AsSystemOriginSigner, DispatchInfoOf, Dispatchable, Implication, TransactionExtension, - ValidateResult, + DispatchInfoOf, Dispatchable, Implication, TransactionExtension, ValidateResult, }; use sp_runtime::{ impl_tx_ext_default, - transaction_validity::{TransactionSource, TransactionValidity, ValidTransaction}, + transaction_validity::{TransactionSource, TransactionValidityError}, }; use sp_std::marker::PhantomData; -use sp_std::vec::Vec; use subtensor_macros::freeze_struct; -use subtensor_runtime_common::{CustomTransactionError, NetUid, NetUidStorageIndex}; +use subtensor_runtime_common::CustomTransactionError; type CallOf = ::RuntimeCall; type OriginOf = ::RuntimeOrigin; +#[allow(deprecated)] +impl From> for CustomTransactionError { + fn from(error: Error) -> Self { + match error { + Error::::AmountTooLow | Error::::NotEnoughStakeToSetWeights => { + Self::StakeAmountTooLow + } + Error::::SubnetNotExists => Self::SubnetNotExists, + Error::::NotEnoughBalanceToStake => Self::BalanceTooLow, + Error::::HotKeyAccountNotExists => Self::HotkeyAccountDoesntExist, + Error::::NotEnoughStakeToWithdraw => Self::NotEnoughStakeToWithdraw, + Error::::InsufficientLiquidity => Self::InsufficientLiquidity, + Error::::SlippageTooHigh => Self::SlippageTooHigh, + Error::::TransferDisallowed => Self::TransferDisallowed, + Error::::HotKeyNotRegisteredInNetwork => Self::HotKeyNotRegisteredInNetwork, + Error::::InvalidIpAddress => Self::InvalidIpAddress, + Error::::ServingRateLimitExceeded => Self::ServingRateLimitExceeded, + Error::::InvalidPort => Self::InvalidPort, + Error::::NonAssociatedColdKey => Self::NonAssociatedColdKey, + Error::::DelegateTakeTooLow => Self::DelegateTakeTooLow, + Error::::DelegateTakeTooHigh => Self::DelegateTakeTooHigh, + Error::::InputLengthsUnequal => Self::InputLengthsUnequal, + Error::::NoWeightsCommitFound => Self::CommitNotFound, + Error::::RevealTooEarly => Self::CommitBlockNotInRevealRange, + Error::::InvalidRevealRound => Self::InvalidRevealRound, + Error::::CommittingWeightsTooFast + | Error::::SettingWeightsTooFast + | Error::::NetworkTxRateLimitExceeded => Self::RateLimitExceeded, + Error::::HotKeyNotRegisteredInSubNet => Self::UidNotFound, + Error::::EvmKeyAssociateRateLimitExceeded => Self::EvmKeyAssociateRateLimitExceeded, + Error::::ColdkeySwapAnnounced => Self::ColdkeyInSwapSchedule, + Error::::ColdkeySwapDisputed => Self::ColdkeySwapDisputed, + _ => Self::BadRequest, + } + } +} + #[freeze_struct("2e02eb32e5cb25d3")] #[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)] pub struct SubtensorTransactionExtension(pub PhantomData); @@ -29,87 +70,44 @@ impl sp_std::fmt::Debug for SubtensorTransac } } -impl SubtensorTransactionExtension -where - CallOf: Dispatchable + IsSubType>, -{ +impl SubtensorTransactionExtension { pub fn new() -> Self { Self(Default::default()) } - pub fn validity_ok(priority: u64) -> ValidTransaction { - ValidTransaction { - priority, - ..Default::default() - } - } - pub fn check_weights_min_stake(who: &T::AccountId, netuid: NetUid) -> bool { - Pallet::::check_weights_min_stake(who, netuid) - } + fn check(origin: &OriginOf, call: &CallOf) -> Result<(), Error> + where + T: pallet_shield::Config, + CallOf: Dispatchable> + + IsSubType> + + IsSubType>, + OriginOf: OriginTrait, + { + let Some(who) = origin.as_signer() else { + return Ok(()); + }; - /// Mirror the per-neuron `WeightsSetRateLimit` throttle (otherwise only - /// enforced inside the dispatch body) into the transaction-validity gate so - /// over-rate `set_weights`/`commit_weights` transactions are rejected - /// pre-dispatch instead of being included for free (these calls are - /// `Pays::No`). Unregistered callers (whose UID cannot be resolved) pass - /// through here and are rejected by the dispatch body, preserving existing - /// error semantics. - pub fn check_weights_rate_limit( - who: &T::AccountId, - netuid: NetUid, - netuid_index: NetUidStorageIndex, - ) -> Result<(), CustomTransactionError> { - if let Ok(neuron_uid) = Pallet::::get_uid_for_net_and_hotkey(netuid, who) { - let current_block = Pallet::::get_current_block_as_u64(); - if !Pallet::::check_rate_limit(netuid_index, neuron_uid, current_block) { - return Err(CustomTransactionError::RateLimitExceeded); - } - } - Ok(()) - } + CheckColdkeySwap::::check(who, call)?; - pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { - match result { - Ok(()) => Ok(ValidTransaction { - priority, - ..Default::default() - }), - Err(err) => Err(match err { - Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow, - Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists, - Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow, - Error::::HotKeyAccountNotExists => { - CustomTransactionError::HotkeyAccountDoesntExist - } - Error::::NotEnoughStakeToWithdraw => { - CustomTransactionError::NotEnoughStakeToWithdraw - } - Error::::InsufficientLiquidity => CustomTransactionError::InsufficientLiquidity, - Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh, - Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed, - Error::::HotKeyNotRegisteredInNetwork => { - CustomTransactionError::HotKeyNotRegisteredInNetwork - } - Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress, - Error::::ServingRateLimitExceeded => { - CustomTransactionError::ServingRateLimitExceeded - } - Error::::InvalidPort => CustomTransactionError::InvalidPort, - Error::::NonAssociatedColdKey => CustomTransactionError::NonAssociatedColdKey, - _ => CustomTransactionError::BadRequest, - } - .into()), - } + let Some(call) = call.is_sub_type() else { + return Ok(()); + }; + + CheckWeights::::check(who, call)?; + CheckRateLimits::::check(who, call)?; + CheckDelegateTake::::check(who, call)?; + CheckServingEndpoints::::check(who, call)?; + CheckEvmKeyAssociation::::check(who, call) } } impl TransactionExtension> for SubtensorTransactionExtension where - T: Config + Send + Sync + TypeInfo + pallet_balances::Config, - CallOf: Dispatchable + T: Config + pallet_shield::Config + Send + Sync + TypeInfo, + CallOf: Dispatchable, Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType> - + IsSubType>, - OriginOf: AsSystemOriginSigner + Clone, + + IsSubType>, + OriginOf: Clone + OriginTrait, { const IDENTIFIER: &'static str = "SubtensorTransactionExtension"; @@ -117,6 +115,16 @@ where type Val = (); type Pre = (); + fn weight(&self, call: &CallOf) -> Weight { + use DispatchExtension as DE; + as DE>>::weight(call) + .saturating_add( as DE>>::weight(call)) + .saturating_add( as DE>>::weight(call)) + .saturating_add( as DE>>::weight(call)) + .saturating_add( as DE>>::weight(call)) + .saturating_add( as DE>>::weight(call)) + } + fn validate( &self, origin: OriginOf, @@ -127,331 +135,164 @@ where _inherited_implication: &impl Implication, _source: TransactionSource, ) -> ValidateResult> { - // Ensure the transaction is signed, else we just skip the extension. - let Some(who) = origin.as_system_origin_signer() else { - return Ok((Default::default(), (), origin)); - }; + Self::check(&origin, call) + .map(|()| (Default::default(), (), origin)) + .map_err(|error| TransactionValidityError::from(CustomTransactionError::from(error))) + } - match call.is_sub_type() { - Some(Call::commit_weights { netuid, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - // Mirror the in-dispatch commit rate limit - // (internal_commit_weights always enforces it). - Self::check_weights_rate_limit(who, *netuid, NetUidStorageIndex::from(*netuid))?; - Ok((Default::default(), (), origin)) - } - Some(Call::commit_mechanism_weights { netuid, mecid, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - // Mirror the in-dispatch commit rate limit - // (internal_commit_weights always enforces it). - Self::check_weights_rate_limit( - who, - *netuid, - Pallet::::get_mechanism_storage_index(*netuid, *mecid), - )?; - Ok((Default::default(), (), origin)) - } - Some(Call::batch_commit_weights { - netuids, - commit_hashes, - }) => { - if netuids.len() != commit_hashes.len() { - return Err(CustomTransactionError::InputLengthsUnequal.into()); - } - for netuid in netuids.iter() { - let netuid: NetUid = (*netuid).into(); - if !Self::check_weights_min_stake(who, netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - } - Ok((Default::default(), (), origin)) - } - Some(Call::reveal_weights { - netuid, - uids, - values, - salt, - version_key, - }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - let provided_hash = Pallet::::get_commit_hash( - who, - NetUidStorageIndex::from(*netuid), - uids, - values, - salt, - *version_key, - ); - match Pallet::::find_commit_block_via_hash(provided_hash) { - Some(commit_block) => { - if Pallet::::is_reveal_block_range(*netuid, commit_block) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) - } - } - None => Err(CustomTransactionError::CommitNotFound.into()), - } - } - Some(Call::reveal_mechanism_weights { - netuid, - mecid, - uids, - values, - salt, - version_key, - }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - let provided_hash = Pallet::::get_commit_hash( - who, - Pallet::::get_mechanism_storage_index(*netuid, *mecid), - uids, - values, - salt, - *version_key, - ); - match Pallet::::find_commit_block_via_hash(provided_hash) { - Some(commit_block) => { - if Pallet::::is_reveal_block_range(*netuid, commit_block) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) - } - } - None => Err(CustomTransactionError::CommitNotFound.into()), - } - } - Some(Call::batch_reveal_weights { - netuid, - uids_list, - values_list, - salts_list, - version_keys, - }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } + impl_tx_ext_default!(CallOf; prepare); +} - let num_reveals = uids_list.len(); - if num_reveals == values_list.len() - && num_reveals == salts_list.len() - && num_reveals == version_keys.len() - { - let provided_hashes = (0..num_reveals) - .map(|i| { - Pallet::::get_commit_hash( - who, - NetUidStorageIndex::from(*netuid), - uids_list.get(i).unwrap_or(&Vec::new()), - values_list.get(i).unwrap_or(&Vec::new()), - salts_list.get(i).unwrap_or(&Vec::new()), - *version_keys.get(i).unwrap_or(&0_u64), - ) - }) - .collect::>(); +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::SubtensorTransactionExtension; + use crate::{ + CheckColdkeySwap, CheckDelegateTake, CheckEvmKeyAssociation, CheckRateLimits, + CheckServingEndpoints, CheckWeights, ColdkeySwapAnnouncements, ColdkeySwapDisputes, + tests::mock::*, + }; + use frame_support::{ + assert_ok, + dispatch::{DispatchExtension, GetDispatchInfo, Pays}, + }; + use frame_system::RawOrigin; + use sp_core::U256; + use sp_runtime::{ + traits::{DispatchInfoOf, Hash, TransactionExtension, TxBaseImplication}, + transaction_validity::{TransactionSource, TransactionValidityError, ValidTransaction}, + }; + use subtensor_runtime_common::{CustomTransactionError, MechId, NetUid}; - let batch_reveal_block = provided_hashes - .iter() - .filter_map(|hash| Pallet::::find_commit_block_via_hash(*hash)) - .collect::>(); + fn dispatch_info() + -> sp_runtime::traits::DispatchInfoOf<::RuntimeCall> { + DispatchInfoOf::<::RuntimeCall>::default() + } - if provided_hashes.len() == batch_reveal_block.len() { - if Pallet::::is_batch_reveal_block_range(*netuid, batch_reveal_block) { - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) - } - } else { - Err(CustomTransactionError::CommitNotFound.into()) - } - } else { - Err(CustomTransactionError::InputLengthsUnequal.into()) - } - } - Some(Call::set_weights { netuid, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - // Mirror the in-dispatch rate limit (only enforced when - // commit-reveal is disabled, matching internal_set_weights). - if !Pallet::::get_commit_reveal_weights_enabled(*netuid) { - Self::check_weights_rate_limit( - who, - *netuid, - NetUidStorageIndex::from(*netuid), - )?; - } - Ok((Default::default(), (), origin)) - } - Some(Call::set_mechanism_weights { netuid, mecid, .. }) => { - if !Self::check_weights_min_stake(who, *netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - // Mirror the in-dispatch rate limit (only enforced when - // commit-reveal is disabled, matching internal_set_weights). - if !Pallet::::get_commit_reveal_weights_enabled(*netuid) { - Self::check_weights_rate_limit( - who, - *netuid, - Pallet::::get_mechanism_storage_index(*netuid, *mecid), - )?; - } - Ok((Default::default(), (), origin)) - } - Some(Call::batch_set_weights { - netuids, - weights, - version_keys, - }) => { - if netuids.len() != weights.len() || netuids.len() != version_keys.len() { - return Err(CustomTransactionError::InputLengthsUnequal.into()); - } - for netuid in netuids.iter() { - let netuid: NetUid = (*netuid).into(); - if !Self::check_weights_min_stake(who, netuid) { - return Err(CustomTransactionError::StakeAmountTooLow.into()); - } - } - Ok((Default::default(), (), origin)) - } - Some(Call::commit_timelocked_weights { - netuid, - reveal_round, - .. - }) => { - if Self::check_weights_min_stake(who, *netuid) { - if *reveal_round < pallet_drand::LastStoredRound::::get() { - return Err(CustomTransactionError::InvalidRevealRound.into()); - } - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::StakeAmountTooLow.into()) - } - } - Some(Call::commit_timelocked_mechanism_weights { - netuid, - mecid: _, - reveal_round, - .. - }) - | Some(Call::commit_crv3_mechanism_weights { - netuid, - mecid: _, - reveal_round, - .. - }) => { - if Self::check_weights_min_stake(who, *netuid) { - if *reveal_round < pallet_drand::LastStoredRound::::get() { - return Err(CustomTransactionError::InvalidRevealRound.into()); - } - Ok((Default::default(), (), origin)) - } else { - Err(CustomTransactionError::StakeAmountTooLow.into()) - } - } - Some(Call::increase_take { hotkey, take }) - | Some(Call::decrease_take { hotkey, take }) => { - if *take < Pallet::::get_min_delegate_take() { - return Err(CustomTransactionError::DelegateTakeTooLow.into()); - } - if *take > Pallet::::get_max_delegate_take() { - return Err(CustomTransactionError::DelegateTakeTooHigh.into()); - } - Self::result_to_validity(Pallet::::do_take_checks(who, hotkey), 0u64) - .map(|validity| (validity, (), origin.clone())) - } + fn validate_signed( + signer: U256, + call: &RuntimeCall, + ) -> Result { + SubtensorTransactionExtension::::new() + .validate( + RawOrigin::Signed(signer).into(), + call, + &dispatch_info(), + 0, + (), + &TxBaseImplication(()), + TransactionSource::External, + ) + .map(|(validity, _, _)| validity) + } - Some(Call::serve_axon { - netuid, - version, - ip, - port, - ip_type, - protocol, - placeholder1, - placeholder2, - }) => { - // Fully validate the user input - Self::result_to_validity( - Pallet::::validate_serve_axon( - who, - *netuid, - *version, - *ip, - *port, - *ip_type, - *protocol, - *placeholder1, - *placeholder2, - ), - 0u64, - ) - .map(|validity| (validity, (), origin.clone())) - } - Some(Call::serve_axon_tls { + fn expected_transaction_extension_weight(call: &RuntimeCall) -> frame_support::weights::Weight { + use DispatchExtension as DE; + as DE>::weight(call) + .saturating_add( as DE>::weight(call)) + .saturating_add( as DE>::weight(call)) + .saturating_add( as DE>::weight(call)) + .saturating_add( as DE>::weight( + call, + )) + .saturating_add( as DE>::weight( + call, + )) + } + + #[test] + fn validate_accepts_calls_allowed_by_dispatch_extensions() { + new_test_ext(1).execute_with(|| { + let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + + assert_ok!(validate_signed(U256::from(1), &call)); + }); + } + + #[test] + #[allow(deprecated)] + fn validate_maps_dispatch_extension_errors_to_transaction_errors() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); + let new_coldkey_hash = + ::Hashing::hash_of(&U256::from(99)); + + ColdkeySwapAnnouncements::::insert( + coldkey, + (System::block_number(), new_coldkey_hash), + ); + let err = validate_signed(coldkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::ColdkeyInSwapSchedule.into()); + + ColdkeySwapDisputes::::insert(coldkey, System::block_number()); + let err = validate_signed(coldkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::ColdkeySwapDisputed.into()); + }); + } + + #[test] + fn pays_no_set_weights_validate_rejects_rate_limited_call() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + + add_network_disable_commit_reveal(netuid, 1, 0); + setup_reserves( netuid, - version, - ip, - port, - ip_type, - protocol, - placeholder1, - placeholder2, - certificate: _, - }) => Self::result_to_validity( - Pallet::::validate_serve_axon( - who, - *netuid, - *version, - *ip, - *port, - *ip_type, - *protocol, - *placeholder1, - *placeholder2, - ), - 0u64, - ) - .map(|validity| (validity, (), origin.clone())), - Some(Call::serve_prometheus { + 1_000_000_000_000_u64.into(), + 1_000_000_000_000_u64.into(), + ); + register_ok_neuron(netuid, hotkey, coldkey, 0); + SubtensorModule::set_stake_threshold(0); + + SubtensorModule::set_weights_set_rate_limit(netuid, 100); + System::set_block_number(10_u64); + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + let netuid_index = SubtensorModule::get_mechanism_storage_index(netuid, MechId::MAIN); + SubtensorModule::set_last_update_for_uid( + netuid_index, + uid, + SubtensorModule::get_current_block_as_u64(), + ); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::set_weights { netuid, - version, - ip, - port, - ip_type, - }) => Self::result_to_validity( - Pallet::::validate_serve_prometheus( - who, *netuid, *version, *ip, *port, *ip_type, - ) - .map(|_| ()), - 0u64, - ) - .map(|validity| (validity, (), origin.clone())), - Some(Call::register_network { .. }) => { - if !TransactionType::RegisterNetwork.passes_rate_limit::(who) { - return Err(CustomTransactionError::RateLimitExceeded.into()); - } + dests: vec![uid], + weights: vec![1], + version_key: 0, + }); - Ok((Default::default(), (), origin)) - } - Some(Call::associate_evm_key { netuid, .. }) => { - let uid = Pallet::::get_uid_for_net_and_hotkey(*netuid, who) - .map_err(|_| CustomTransactionError::UidNotFound)?; - Pallet::::ensure_evm_key_associate_rate_limit(*netuid, uid) - .map_err(|_| CustomTransactionError::EvmKeyAssociateRateLimitExceeded)?; - Ok((Default::default(), (), origin)) - } - _ => Ok((Default::default(), (), origin)), - } + assert_eq!(call.get_dispatch_info().pays_fee, Pays::No); + let err = validate_signed(hotkey, &call).unwrap_err(); + assert_eq!(err, CustomTransactionError::RateLimitExceeded.into()); + }); } - impl_tx_ext_default!(CallOf; weight prepare); + #[test] + fn weight_matches_top_level_dispatch_extension_checks() { + new_test_ext(1).execute_with(|| { + let extension = SubtensorTransactionExtension::::new(); + let calls = [ + RuntimeCall::System(frame_system::Call::remark { remark: vec![] }), + RuntimeCall::SubtensorModule(SubtensorCall::set_weights { + netuid: NetUid::from(1), + dests: vec![0], + weights: vec![1], + version_key: 0, + }), + RuntimeCall::SubtensorModule(SubtensorCall::register_network { + hotkey: U256::from(9), + }), + ]; + + for call in calls { + assert_eq!( + TransactionExtension::weight(&extension, &call), + expected_transaction_extension_weight(&call) + ); + } + }); + } } diff --git a/pallets/subtensor/src/guards/check_coldkey_swap.rs b/pallets/subtensor/src/guards/check_coldkey_swap.rs index 14b1a25ac9..5f124be219 100644 --- a/pallets/subtensor/src/guards/check_coldkey_swap.rs +++ b/pallets/subtensor/src/guards/check_coldkey_swap.rs @@ -1,3 +1,4 @@ +use crate::weights::WeightInfo; use crate::{Call, ColdkeySwapAnnouncements, ColdkeySwapDisputes, Config, Error}; use frame_support::{ dispatch::{DispatchErrorWithPostInfo, DispatchExtension, DispatchInfo, PostDispatchInfo}, @@ -26,6 +27,43 @@ type DispatchableOriginOf = as Dispatchable>::RuntimeOrigin; /// resolved origin. pub struct CheckColdkeySwap(PhantomData); +impl CheckColdkeySwap +where + T: Config + pallet_shield::Config, + CallOf: IsSubType> + IsSubType>, +{ + pub fn check(who: &T::AccountId, call: &CallOf) -> Result<(), Error> { + if !ColdkeySwapAnnouncements::::contains_key(who) { + return Ok(()); + } + + if ColdkeySwapDisputes::::contains_key(who) { + return Err(Error::::ColdkeySwapDisputed); + } + + if Self::is_allowed_during_swap(call) { + Ok(()) + } else { + Err(Error::::ColdkeySwapAnnounced) + } + } + + fn is_allowed_during_swap(call: &CallOf) -> bool { + matches!( + call.is_sub_type(), + Some( + Call::announce_coldkey_swap { .. } + | Call::swap_coldkey_announced { .. } + | Call::dispute_coldkey_swap { .. } + | Call::clear_coldkey_swap_announcement { .. } + ) + ) || matches!( + IsSubType::>::is_sub_type(call), + Some(pallet_shield::Call::submit_encrypted { .. }) + ) + } +} + impl DispatchExtension<::RuntimeCall> for CheckColdkeySwap where T: Config + pallet_shield::Config, @@ -37,7 +75,7 @@ where type Pre = (); fn weight(_call: &CallOf) -> Weight { - T::DbWeight::get().reads(2) + ::WeightInfo::check_coldkey_swap_extension() } fn pre_dispatch( @@ -50,44 +88,22 @@ where return Ok(()); }; - if ColdkeySwapAnnouncements::::contains_key(who) { - if ColdkeySwapDisputes::::contains_key(who) { - return Err(Error::::ColdkeySwapDisputed.into()); - } - - let is_allowed_direct = matches!( - call.is_sub_type(), - Some( - Call::announce_coldkey_swap { .. } - | Call::swap_coldkey_announced { .. } - | Call::dispute_coldkey_swap { .. } - | Call::clear_coldkey_swap_announcement { .. } - ) - ); - - let is_mev_protected = matches!( - IsSubType::>::is_sub_type(call), - Some(pallet_shield::Call::submit_encrypted { .. }) - ); - - if !is_allowed_direct && !is_mev_protected { - return Err(Error::::ColdkeySwapAnnounced.into()); - } - } - - Ok(()) + Self::check(who, call).map_err(Into::into) } } #[cfg(test)] #[allow(clippy::expect_used, clippy::unwrap_used)] mod tests { + use super::CheckColdkeySwap; use crate::{ColdkeySwapAnnouncements, ColdkeySwapDisputes, Error, tests::mock::*}; - use frame_support::{BoundedVec, assert_ok}; + use frame_support::{ + BoundedVec, assert_ok, dispatch::DispatchResultWithPostInfo, traits::ExtendedDispatchable, + }; use frame_system::Call as SystemCall; use pallet_subtensor_proxy::Call as ProxyCall; use sp_core::U256; - use sp_runtime::traits::{Dispatchable, Hash}; + use sp_runtime::traits::Hash; use subtensor_runtime_common::{ProxyType, TaoBalance}; type HashingOf = ::Hashing; @@ -154,11 +170,17 @@ mod tests { let _ = SubtensorModule::spend_tao(coldkey, credit, tao).unwrap(); } + fn dispatch_with_ext(call: RuntimeCall, origin: RuntimeOrigin) -> DispatchResultWithPostInfo { + as ExtendedDispatchable>::dispatch_with_extension( + origin, call, + ) + } + #[test] fn no_active_swap_allows_calls() { new_test_ext(1).execute_with(|| { let who = U256::from(1); - assert_ok!(remark_call().dispatch(RuntimeOrigin::signed(who))); + assert_ok!(dispatch_with_ext(remark_call(), RuntimeOrigin::signed(who))); }); } @@ -167,8 +189,7 @@ mod tests { new_test_ext(1).execute_with(|| { let who = U256::from(1); setup_swap_disputed(&who); - - assert_ok!(remark_call().dispatch(RuntimeOrigin::none())); + assert_ok!(dispatch_with_ext(remark_call(), RuntimeOrigin::none())); }); } @@ -177,8 +198,7 @@ mod tests { new_test_ext(1).execute_with(|| { let who = U256::from(1); setup_swap_disputed(&who); - - assert_ok!(remark_call().dispatch(RuntimeOrigin::root())); + assert_ok!(dispatch_with_ext(remark_call(), RuntimeOrigin::root())); }); } @@ -190,7 +210,9 @@ mod tests { for call in forbidden_calls() { assert_eq!( - call.dispatch(RuntimeOrigin::signed(who)).unwrap_err().error, + dispatch_with_ext(call, RuntimeOrigin::signed(who)) + .unwrap_err() + .error, Error::::ColdkeySwapAnnounced.into() ); } @@ -204,7 +226,7 @@ mod tests { setup_swap_announced(&who); for call in authorized_calls() { - if let Err(err) = call.dispatch(RuntimeOrigin::signed(who)) { + if let Err(err) = dispatch_with_ext(call, RuntimeOrigin::signed(who)) { assert_ne!( err.error, Error::::ColdkeySwapAnnounced.into(), @@ -229,7 +251,9 @@ mod tests { for call in all_calls { assert_eq!( - call.dispatch(RuntimeOrigin::signed(who)).unwrap_err().error, + dispatch_with_ext(call, RuntimeOrigin::signed(who)) + .unwrap_err() + .error, Error::::ColdkeySwapDisputed.into() ); } @@ -265,7 +289,10 @@ mod tests { }); // The outer proxy call itself succeeds - assert_ok!(proxy_call.dispatch(RuntimeOrigin::signed(delegate))); + assert_ok!(dispatch_with_ext( + proxy_call, + RuntimeOrigin::signed(delegate) + )); // The inner call was blocked — check via LastCallResult storage. assert_eq!( @@ -315,7 +342,10 @@ mod tests { call: Box::new(inner_proxy), }); - assert_ok!(outer_proxy.dispatch(RuntimeOrigin::signed(delegate2))); + assert_ok!(dispatch_with_ext( + outer_proxy, + RuntimeOrigin::signed(delegate2) + )); // The innermost call (remark as real) was blocked. assert_eq!( diff --git a/pallets/subtensor/src/guards/check_delegate_take.rs b/pallets/subtensor/src/guards/check_delegate_take.rs new file mode 100644 index 0000000000..c9f54d4cb5 --- /dev/null +++ b/pallets/subtensor/src/guards/check_delegate_take.rs @@ -0,0 +1,192 @@ +use crate::weights::WeightInfo; +use crate::{Call, Config, Error, Pallet}; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchExtension, DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::{IsSubType, OriginTrait}, +}; +use sp_runtime::traits::Dispatchable; +use sp_std::marker::PhantomData; + +type CallOf = ::RuntimeCall; +type DispatchableOriginOf = as Dispatchable>::RuntimeOrigin; + +/// Dispatch extension for delegate-take bounds and ownership preconditions. +/// +/// Signed increase/decrease take calls are checked before dispatch; unrelated +/// calls and non-signed origins pass through. +pub struct CheckDelegateTake(PhantomData); + +impl CheckDelegateTake { + pub fn check(who: &T::AccountId, call: &Call) -> Result<(), Error> { + match call { + Call::increase_take { hotkey, take } | Call::decrease_take { hotkey, take } => { + if *take < Pallet::::get_min_delegate_take() { + return Err(Error::::DelegateTakeTooLow); + } + if *take > Pallet::::get_max_delegate_take() { + return Err(Error::::DelegateTakeTooHigh); + } + Pallet::::do_take_checks(who, hotkey) + } + _ => Ok(()), + } + } +} + +impl DispatchExtension> for CheckDelegateTake +where + T: Config, + CallOf: Dispatchable + IsSubType>, + DispatchableOriginOf: OriginTrait, +{ + type Pre = (); + + fn weight(_call: &CallOf) -> Weight { + ::WeightInfo::check_delegate_take_extension() + } + + fn pre_dispatch( + origin: &DispatchableOriginOf, + call: &CallOf, + ) -> Result { + let Some(who) = origin.as_signer() else { + return Ok(()); + }; + + let Some(call) = call.is_sub_type() else { + return Ok(()); + }; + + Self::check(who, call).map_err(Into::into) + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::*; + use crate::{Error, tests::mock::*}; + use frame_support::{ + assert_ok, dispatch::DispatchResultWithPostInfo, traits::ExtendedDispatchable, + }; + use sp_core::U256; + use sp_runtime::DispatchError; + + fn increase_take_call(hotkey: U256, take: u16) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::increase_take { hotkey, take }) + } + + fn decrease_take_call(hotkey: U256, take: u16) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::decrease_take { hotkey, take }) + } + + fn dispatch_with_ext(call: RuntimeCall, origin: RuntimeOrigin) -> DispatchResultWithPostInfo { + as ExtendedDispatchable>::dispatch_with_extension( + origin, call, + ) + } + + fn err(result: DispatchResultWithPostInfo) -> DispatchError { + result.unwrap_err().error + } + + #[test] + fn accepts_owner_with_valid_take() { + new_test_ext(0).execute_with(|| { + let owner = U256::from(1); + let hotkey = U256::from(2); + crate::Owner::::insert(hotkey, owner); + + for call in [ + increase_take_call(hotkey, SubtensorModule::get_max_delegate_take()), + decrease_take_call(hotkey, SubtensorModule::get_min_delegate_take()), + ] { + assert_ok!(dispatch_with_ext(call, RuntimeOrigin::signed(owner))); + } + }); + } + + #[test] + fn rejects_take_too_low() { + new_test_ext(0).execute_with(|| { + let owner = U256::from(1); + let hotkey = U256::from(2); + crate::Owner::::insert(hotkey, owner); + + let take = SubtensorModule::get_min_delegate_take() - 1; + + for call in [ + increase_take_call(hotkey, take), + decrease_take_call(hotkey, take), + ] { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(owner))), + Error::::DelegateTakeTooLow.into() + ); + } + }); + } + + #[test] + fn rejects_take_too_high() { + new_test_ext(0).execute_with(|| { + let owner = U256::from(1); + let hotkey = U256::from(2); + crate::Owner::::insert(hotkey, owner); + + let take = SubtensorModule::get_max_delegate_take() + 1; + + for call in [ + increase_take_call(hotkey, take), + decrease_take_call(hotkey, take), + ] { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(owner))), + Error::::DelegateTakeTooHigh.into() + ); + } + }); + } + + #[test] + fn rejects_non_owner() { + new_test_ext(0).execute_with(|| { + let owner = U256::from(1); + let other = U256::from(2); + let hotkey = U256::from(3); + crate::Owner::::insert(hotkey, owner); + + let take = SubtensorModule::get_max_delegate_take(); + + for call in [ + increase_take_call(hotkey, take), + decrease_take_call(hotkey, take), + ] { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(other))), + Error::::NonAssociatedColdKey.into() + ); + } + }); + } + + #[test] + fn rejects_missing_hotkey_owner() { + new_test_ext(0).execute_with(|| { + let owner = U256::from(1); + let hotkey = U256::from(99); + let take = SubtensorModule::get_max_delegate_take(); + + for call in [ + increase_take_call(hotkey, take), + decrease_take_call(hotkey, take), + ] { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(owner))), + Error::::HotKeyAccountNotExists.into() + ); + } + }); + } +} diff --git a/pallets/subtensor/src/guards/check_evm_key_association.rs b/pallets/subtensor/src/guards/check_evm_key_association.rs new file mode 100644 index 0000000000..d7e2847e99 --- /dev/null +++ b/pallets/subtensor/src/guards/check_evm_key_association.rs @@ -0,0 +1,215 @@ +use crate::weights::WeightInfo; +use crate::{Call, Config, Error, Pallet}; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchExtension, DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::{IsSubType, OriginTrait}, +}; +use sp_runtime::traits::Dispatchable; +use sp_std::marker::PhantomData; + +type CallOf = ::RuntimeCall; +type DispatchableOriginOf = as Dispatchable>::RuntimeOrigin; + +/// Dispatch extension for EVM-key association preconditions. +/// +/// Signed EVM-key association calls are checked for subnet registration and +/// cooldown before dispatch; unrelated calls and non-signed origins pass through. +pub struct CheckEvmKeyAssociation(PhantomData); + +impl CheckEvmKeyAssociation { + pub fn check(who: &T::AccountId, call: &Call) -> Result<(), Error> { + match call { + Call::associate_evm_key { netuid, .. } => { + let uid = Pallet::::get_uid_for_net_and_hotkey(*netuid, who) + .map_err(|_| Error::::HotKeyNotRegisteredInSubNet)?; + Pallet::::ensure_evm_key_associate_rate_limit(*netuid, uid) + .map_err(|_| Error::::EvmKeyAssociateRateLimitExceeded)?; + Ok(()) + } + _ => Ok(()), + } + } +} + +impl DispatchExtension> for CheckEvmKeyAssociation +where + T: Config, + CallOf: Dispatchable + IsSubType>, + DispatchableOriginOf: OriginTrait, +{ + type Pre = (); + + fn weight(_call: &CallOf) -> Weight { + ::WeightInfo::check_evm_key_association_extension() + } + + fn pre_dispatch( + origin: &DispatchableOriginOf, + call: &CallOf, + ) -> Result { + let Some(who) = origin.as_signer() else { + return Ok(()); + }; + + let Some(call) = call.is_sub_type() else { + return Ok(()); + }; + + Self::check(who, call).map_err(Into::into) + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used, clippy::arithmetic_side_effects)] +mod tests { + use super::CheckEvmKeyAssociation; + use crate::{AssociatedEvmAddress, Error, tests::mock::*}; + use codec::Encode; + use frame_support::{ + assert_ok, dispatch::DispatchResultWithPostInfo, traits::ExtendedDispatchable, + }; + use frame_system::Call as SystemCall; + use sp_core::{H160, Pair, U256, ecdsa, keccak_256}; + use sp_runtime::DispatchError; + use subtensor_runtime_common::NetUid; + + fn dispatch_with_ext(call: RuntimeCall, origin: RuntimeOrigin) -> DispatchResultWithPostInfo { + as ExtendedDispatchable>::dispatch_with_extension( + origin, call, + ) + } + + fn err(result: DispatchResultWithPostInfo) -> DispatchError { + result.err().unwrap().error + } + + fn public_to_evm_key(pubkey: &ecdsa::Public) -> H160 { + let secp_pub = libsecp256k1::PublicKey::parse_compressed(&pubkey.0).unwrap(); + let uncompressed = secp_pub.serialize(); + let hash = keccak_256(&uncompressed[1..]); + H160::from_slice(&hash[12..]) + } + + fn sign_evm_message>(pair: &ecdsa::Pair, message: M) -> ecdsa::Signature { + let hash = SubtensorModule::hash_message_eip191(message); + let mut signature = pair.sign_prehashed(&hash); + signature.0[64] += 27; + signature + } + + fn associate_call( + netuid: NetUid, + evm_key: H160, + block_number: u64, + signature: ecdsa::Signature, + ) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::associate_evm_key { + netuid, + evm_key, + block_number, + signature, + }) + } + + fn dummy_associate_call(netuid: NetUid) -> RuntimeCall { + associate_call( + netuid, + H160::zero(), + 0, + ecdsa::Signature::from_raw([0_u8; 65]), + ) + } + + fn valid_associate_call(netuid: NetUid, hotkey: U256) -> (RuntimeCall, H160) { + let pair = ecdsa::Pair::generate().0; + let evm_key = public_to_evm_key(&pair.public()); + let block_number = System::block_number(); + let block_hash = keccak_256(block_number.encode().as_ref()); + let message = [ + hotkey.encode().as_ref(), + <[u8; 32] as AsRef<[u8]>>::as_ref(&block_hash), + ] + .concat(); + let signature = sign_evm_message(&pair, message); + + ( + associate_call(netuid, evm_key, block_number, signature), + evm_key, + ) + } + + #[test] + fn unrelated_calls_pass_through() { + new_test_ext(0).execute_with(|| { + let hotkey = U256::from(1); + let call = RuntimeCall::System(SystemCall::remark { remark: vec![] }); + + assert_ok!(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))); + }); + } + + #[test] + fn registered_hotkey_allows_call() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + System::set_block_number(EvmKeyAssociateRateLimit::get()); + + let (call, evm_key) = valid_associate_call(netuid, hotkey); + assert_ok!(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))); + + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + assert_eq!( + AssociatedEvmAddress::::get(netuid, uid), + Some((evm_key, SubtensorModule::get_current_block_as_u64())) + ); + }); + } + + #[test] + fn unregistered_hotkey_blocks_call() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + add_network(netuid, 1, 0); + + assert_eq!( + err(dispatch_with_ext( + dummy_associate_call(netuid), + RuntimeOrigin::signed(hotkey) + )), + Error::::HotKeyNotRegisteredInSubNet.into() + ); + }); + } + + #[test] + fn recent_association_blocks_call() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network(netuid, 1, 0); + register_ok_neuron(netuid, hotkey, coldkey, 0); + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + System::set_block_number(300_u64); + AssociatedEvmAddress::::insert( + netuid, + uid, + (H160::zero(), SubtensorModule::get_current_block_as_u64()), + ); + + assert_eq!( + err(dispatch_with_ext( + dummy_associate_call(netuid), + RuntimeOrigin::signed(hotkey) + )), + Error::::EvmKeyAssociateRateLimitExceeded.into() + ); + }); + } +} diff --git a/pallets/subtensor/src/guards/check_rate_limits.rs b/pallets/subtensor/src/guards/check_rate_limits.rs new file mode 100644 index 0000000000..d2c021dd5d --- /dev/null +++ b/pallets/subtensor/src/guards/check_rate_limits.rs @@ -0,0 +1,251 @@ +use crate::weights::WeightInfo; +use crate::{Call, Config, Error, Pallet, TransactionType}; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchExtension, DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::{IsSubType, OriginTrait}, +}; +use sp_runtime::traits::Dispatchable; +use sp_std::marker::PhantomData; +use subtensor_runtime_common::{NetUid, NetUidStorageIndex}; + +type CallOf = ::RuntimeCall; +type DispatchableOriginOf = as Dispatchable>::RuntimeOrigin; + +/// Dispatch extension for rate-limit checks that are safe to reject before dispatch. +/// +/// Signed weight and network-registration calls are checked before dispatch; +/// unrelated calls and non-signed origins pass through. +pub struct CheckRateLimits(PhantomData); + +impl CheckRateLimits { + fn check_weights_rate_limit( + who: &T::AccountId, + netuid: NetUid, + netuid_index: NetUidStorageIndex, + error: Error, + ) -> Result<(), Error> { + let Ok(neuron_uid) = Pallet::::get_uid_for_net_and_hotkey(netuid, who) else { + return Ok(()); + }; + + let current_block = Pallet::::get_current_block_as_u64(); + if Pallet::::check_rate_limit(netuid_index, neuron_uid, current_block) { + Ok(()) + } else { + Err(error) + } + } + + pub fn check(who: &T::AccountId, call: &Call) -> Result<(), Error> { + match call { + Call::commit_weights { netuid, .. } => Self::check_weights_rate_limit( + who, + *netuid, + NetUidStorageIndex::from(*netuid), + Error::::CommittingWeightsTooFast, + ), + Call::commit_mechanism_weights { netuid, mecid, .. } => Self::check_weights_rate_limit( + who, + *netuid, + Pallet::::get_mechanism_storage_index(*netuid, *mecid), + Error::::CommittingWeightsTooFast, + ), + Call::set_weights { netuid, .. } + if !Pallet::::get_commit_reveal_weights_enabled(*netuid) => + { + Self::check_weights_rate_limit( + who, + *netuid, + NetUidStorageIndex::from(*netuid), + Error::::SettingWeightsTooFast, + ) + } + Call::set_mechanism_weights { netuid, mecid, .. } + if !Pallet::::get_commit_reveal_weights_enabled(*netuid) => + { + Self::check_weights_rate_limit( + who, + *netuid, + Pallet::::get_mechanism_storage_index(*netuid, *mecid), + Error::::SettingWeightsTooFast, + ) + } + Call::register_network { .. } + if !TransactionType::RegisterNetwork.passes_rate_limit::(who) => + { + Err(Error::::NetworkTxRateLimitExceeded) + } + _ => Ok(()), + } + } +} + +impl DispatchExtension> for CheckRateLimits +where + T: Config, + CallOf: Dispatchable + IsSubType>, + DispatchableOriginOf: OriginTrait, +{ + type Pre = (); + + fn weight(_call: &CallOf) -> Weight { + ::WeightInfo::check_rate_limits_extension() + } + + fn pre_dispatch( + origin: &DispatchableOriginOf, + call: &CallOf, + ) -> Result { + let Some(who) = origin.as_signer() else { + return Ok(()); + }; + + let Some(call) = call.is_sub_type() else { + return Ok(()); + }; + + Self::check(who, call).map_err(Into::into) + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::CheckRateLimits; + use crate::{Error, tests::mock::*}; + use frame_support::{ + assert_ok, dispatch::DispatchResultWithPostInfo, traits::ExtendedDispatchable, + }; + use frame_system::Call as SystemCall; + use sp_core::U256; + use sp_runtime::DispatchError; + use subtensor_runtime_common::{MechId, NetUid, TaoBalance}; + + fn dispatch_with_ext(call: RuntimeCall, origin: RuntimeOrigin) -> DispatchResultWithPostInfo { + as ExtendedDispatchable>::dispatch_with_extension( + origin, call, + ) + } + + fn err(result: DispatchResultWithPostInfo) -> DispatchError { + result.err().unwrap().error + } + + fn register_neuron(netuid: NetUid, hotkey: U256, coldkey: U256) { + add_network(netuid, 1, 0); + setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + register_ok_neuron(netuid, hotkey, coldkey, 0); + } + + fn set_weights_call(netuid: NetUid, uid: u16) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::set_weights { + netuid, + dests: vec![uid], + weights: vec![1], + version_key: 0, + }) + } + + fn register_network_call(hotkey: U256) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::register_network { hotkey }) + } + + fn fund(coldkey: U256, amount: TaoBalance) { + add_balance_to_coldkey_account(&coldkey, amount); + } + + #[test] + fn unrelated_calls_pass_through() { + new_test_ext(0).execute_with(|| { + let call = RuntimeCall::System(SystemCall::remark { remark: vec![] }); + + assert_ok!(dispatch_with_ext( + call, + RuntimeOrigin::signed(U256::from(1)) + )); + }); + } + + #[test] + fn over_rate_set_weights_is_blocked() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + register_neuron(netuid, hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, false); + SubtensorModule::set_weights_set_rate_limit(netuid, 100); + System::set_block_number(10_u64); + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + let netuid_index = SubtensorModule::get_mechanism_storage_index(netuid, MechId::MAIN); + SubtensorModule::set_last_update_for_uid( + netuid_index, + uid, + SubtensorModule::get_current_block_as_u64(), + ); + + assert_eq!( + err(dispatch_with_ext( + set_weights_call(netuid, uid), + RuntimeOrigin::signed(hotkey) + )), + Error::::SettingWeightsTooFast.into() + ); + }); + } + + #[test] + fn set_weights_within_rate_limit_dispatches() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + register_neuron(netuid, hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, false); + SubtensorModule::set_weights_set_rate_limit(netuid, 100); + SubtensorModule::set_stake_threshold(0); + System::set_block_number(200_u64); + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + + assert_ok!(dispatch_with_ext( + set_weights_call(netuid, uid), + RuntimeOrigin::signed(hotkey) + )); + }); + } + + #[test] + fn over_rate_network_registration_is_blocked() { + new_test_ext(0).execute_with(|| { + crate::NetworkRateLimit::::put(50_u64); + System::set_block_number(200_u64); + SubtensorModule::set_network_last_lock_block(170); + let coldkey = U256::from(70); + + assert_eq!( + err(dispatch_with_ext( + register_network_call(U256::from(71)), + RuntimeOrigin::signed(coldkey) + )), + Error::::NetworkTxRateLimitExceeded.into() + ); + }); + } + + #[test] + fn network_registration_after_rate_limit_dispatches() { + new_test_ext(0).execute_with(|| { + crate::NetworkRateLimit::::put(50_u64); + System::set_block_number(200_u64); + SubtensorModule::set_network_last_lock_block(100); + let coldkey = U256::from(70); + fund(coldkey, SubtensorModule::get_network_lock_cost().into()); + + assert_ok!(dispatch_with_ext( + register_network_call(U256::from(71)), + RuntimeOrigin::signed(coldkey) + )); + }); + } +} diff --git a/pallets/subtensor/src/guards/check_serving_endpoints.rs b/pallets/subtensor/src/guards/check_serving_endpoints.rs new file mode 100644 index 0000000000..46304d337f --- /dev/null +++ b/pallets/subtensor/src/guards/check_serving_endpoints.rs @@ -0,0 +1,218 @@ +use crate::weights::WeightInfo; +use crate::{Call, Config, Error, Pallet}; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchExtension, DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::{IsSubType, OriginTrait}, +}; +use sp_runtime::traits::Dispatchable; +use sp_std::marker::PhantomData; + +type CallOf = ::RuntimeCall; +type DispatchableOriginOf = as Dispatchable>::RuntimeOrigin; + +/// Dispatch extension for axon/prometheus endpoint validation. +/// +/// Signed serving calls are checked before dispatch; unrelated calls and +/// non-signed origins pass through. +pub struct CheckServingEndpoints(PhantomData); + +impl CheckServingEndpoints { + pub fn check(who: &T::AccountId, call: &Call) -> Result<(), Error> { + match call { + Call::serve_axon { + netuid, + version, + ip, + port, + ip_type, + protocol, + placeholder1, + placeholder2, + } + | Call::serve_axon_tls { + netuid, + version, + ip, + port, + ip_type, + protocol, + placeholder1, + placeholder2, + .. + } => Pallet::::validate_serve_axon( + who, + *netuid, + *version, + *ip, + *port, + *ip_type, + *protocol, + *placeholder1, + *placeholder2, + ), + Call::serve_prometheus { + netuid, + version, + ip, + port, + ip_type, + } => { + Pallet::::validate_serve_prometheus(who, *netuid, *version, *ip, *port, *ip_type) + .map(|_| ()) + } + _ => Ok(()), + } + } +} + +impl DispatchExtension> for CheckServingEndpoints +where + T: Config, + CallOf: Dispatchable + IsSubType>, + DispatchableOriginOf: OriginTrait, +{ + type Pre = (); + + fn weight(_call: &CallOf) -> Weight { + ::WeightInfo::check_serving_endpoints_extension() + } + + fn pre_dispatch( + origin: &DispatchableOriginOf, + call: &CallOf, + ) -> Result { + let Some(who) = origin.as_signer() else { + return Ok(()); + }; + + let Some(call) = call.is_sub_type() else { + return Ok(()); + }; + + Self::check(who, call).map_err(Into::into) + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::CheckServingEndpoints; + use crate::{Error, tests::mock::*}; + use frame_support::{ + assert_ok, dispatch::DispatchResultWithPostInfo, traits::ExtendedDispatchable, + }; + use frame_system::Call as SystemCall; + use sp_core::U256; + use sp_runtime::DispatchError; + use subtensor_runtime_common::NetUid; + + fn dispatch_with_ext(call: RuntimeCall, origin: RuntimeOrigin) -> DispatchResultWithPostInfo { + as ExtendedDispatchable>::dispatch_with_extension( + origin, call, + ) + } + + fn err(result: DispatchResultWithPostInfo) -> DispatchError { + result.err().unwrap().error + } + + fn serve_axon_call(netuid: NetUid) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::serve_axon { + netuid, + version: 1, + ip: u128::from(u32::from_be_bytes([8, 8, 8, 8])), + port: 1, + ip_type: 4, + protocol: 0, + placeholder1: 0, + placeholder2: 0, + }) + } + + fn serve_axon_tls_call(netuid: NetUid) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::serve_axon_tls { + netuid, + version: 1, + ip: u128::from(u32::from_be_bytes([8, 8, 8, 8])), + port: 1, + ip_type: 4, + protocol: 0, + placeholder1: 0, + placeholder2: 0, + certificate: vec![], + }) + } + + fn serve_prometheus_call(netuid: NetUid) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::serve_prometheus { + netuid, + version: 1, + ip: u128::from(u32::from_be_bytes([8, 8, 4, 4])), + port: 1, + ip_type: 4, + }) + } + + fn register_hotkey(netuid: NetUid, hotkey: U256, coldkey: U256) { + add_network(netuid, 1, 0); + setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + register_ok_neuron(netuid, hotkey, coldkey, 0); + } + + #[test] + fn unrelated_calls_pass_through() { + new_test_ext(0).execute_with(|| { + let call = RuntimeCall::System(SystemCall::remark { remark: vec![] }); + + assert_ok!(dispatch_with_ext( + call, + RuntimeOrigin::signed(U256::from(1)) + )); + }); + } + + #[test] + fn unregistered_hotkey_blocks_axon() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let calls = [serve_axon_call(netuid), serve_axon_tls_call(netuid)]; + + for call in calls { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))), + Error::::HotKeyNotRegisteredInNetwork.into() + ); + } + }); + } + + #[test] + fn registered_hotkey_allows_axon() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + register_hotkey(netuid, hotkey, U256::from(2)); + let calls = [serve_axon_call(netuid), serve_axon_tls_call(netuid)]; + + for call in calls { + assert_ok!(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))); + } + }); + } + + #[test] + fn registered_hotkey_allows_prometheus() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + register_hotkey(netuid, hotkey, U256::from(2)); + + assert_ok!(dispatch_with_ext( + serve_prometheus_call(netuid), + RuntimeOrigin::signed(hotkey) + )); + }); + } +} diff --git a/pallets/subtensor/src/guards/check_weights.rs b/pallets/subtensor/src/guards/check_weights.rs new file mode 100644 index 0000000000..d10e7b8b8c --- /dev/null +++ b/pallets/subtensor/src/guards/check_weights.rs @@ -0,0 +1,613 @@ +use crate::weights::WeightInfo; +use crate::{Call, Config, Error, Pallet, WeightCommits}; +use frame_support::{ + dispatch::{DispatchErrorWithPostInfo, DispatchExtension, DispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + traits::{IsSubType, OriginTrait}, +}; +use sp_core::H256; +use sp_runtime::traits::Dispatchable; +use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, vec::Vec}; +use subtensor_runtime_common::{NetUid, NetUidStorageIndex}; + +type CallOf = ::RuntimeCall; +type DispatchableOriginOf = as Dispatchable>::RuntimeOrigin; +type WeightCommitQueue = VecDeque<(H256, u64, u64, u64)>; + +/// Dispatch extension for weight-setting preconditions. +/// +/// Signed weight calls are checked for batch shape, min stake, and commit/reveal +/// prerequisites before dispatch; unrelated calls and non-signed origins pass through. +pub struct CheckWeights(PhantomData); + +impl CheckWeights { + pub fn check(who: &T::AccountId, call: &Call) -> Result<(), Error> { + Self::check_input_lengths(call)?; + Self::check_min_stake(who, call)?; + Self::check_commit_reveal(who, call) + } + + fn check_input_lengths(call: &Call) -> Result<(), Error> { + let lengths_match = match call { + Call::batch_commit_weights { + netuids, + commit_hashes, + } => netuids.len() == commit_hashes.len(), + Call::batch_reveal_weights { + uids_list, + values_list, + salts_list, + version_keys, + .. + } => { + uids_list.len() == values_list.len() + && uids_list.len() == salts_list.len() + && uids_list.len() == version_keys.len() + } + Call::batch_set_weights { + netuids, + weights, + version_keys, + } => netuids.len() == weights.len() && netuids.len() == version_keys.len(), + _ => true, + }; + + if lengths_match { + Ok(()) + } else { + Err(Error::::InputLengthsUnequal) + } + } + + fn ensure_min_stake(who: &T::AccountId, netuid: NetUid) -> Result<(), Error> { + if Pallet::::check_weights_min_stake(who, netuid) { + Ok(()) + } else { + Err(Error::::NotEnoughStakeToSetWeights) + } + } + + fn check_min_stake(who: &T::AccountId, call: &Call) -> Result<(), Error> { + match call { + Call::commit_weights { netuid, .. } + | Call::commit_mechanism_weights { netuid, .. } + | Call::reveal_weights { netuid, .. } + | Call::reveal_mechanism_weights { netuid, .. } + | Call::batch_reveal_weights { netuid, .. } + | Call::set_weights { netuid, .. } + | Call::set_mechanism_weights { netuid, .. } + | Call::commit_timelocked_weights { netuid, .. } + | Call::commit_timelocked_mechanism_weights { netuid, .. } + | Call::commit_crv3_mechanism_weights { netuid, .. } => { + Self::ensure_min_stake(who, *netuid) + } + Call::batch_commit_weights { netuids, .. } + | Call::batch_set_weights { netuids, .. } => { + for netuid in netuids.iter() { + Self::ensure_min_stake(who, (*netuid).into())?; + } + Ok(()) + } + _ => Ok(()), + } + } + + fn find_commit_epoch(commits: &WeightCommitQueue, hash: H256) -> Option { + commits + .iter() + .find_map(|(commit_hash, commit_epoch, _, _)| { + (*commit_hash == hash).then_some(*commit_epoch) + }) + } + + fn check_reveal( + who: &T::AccountId, + netuid: NetUid, + netuid_index: NetUidStorageIndex, + uids: &[u16], + values: &[u16], + salt: &[u16], + version_key: u64, + ) -> Result<(), Error> { + let commits = + WeightCommits::::get(netuid_index, who).ok_or(Error::::NoWeightsCommitFound)?; + let hash = Pallet::::get_commit_hash(who, netuid_index, uids, values, salt, version_key); + let commit_epoch = + Self::find_commit_epoch(&commits, hash).ok_or(Error::::NoWeightsCommitFound)?; + + if Pallet::::is_reveal_block_range(netuid, commit_epoch) { + Ok(()) + } else { + Err(Error::::RevealTooEarly) + } + } + + fn check_batch_reveal( + who: &T::AccountId, + netuid: NetUid, + uids_list: &[Vec], + values_list: &[Vec], + salts_list: &[Vec], + version_keys: &[u64], + ) -> Result<(), Error> { + if uids_list.len() != values_list.len() + || uids_list.len() != salts_list.len() + || uids_list.len() != version_keys.len() + { + return Err(Error::::InputLengthsUnequal); + } + + let netuid_index = NetUidStorageIndex::from(netuid); + let commits = + WeightCommits::::get(netuid_index, who).ok_or(Error::::NoWeightsCommitFound)?; + + for (((uids, values), salt), version_key) in uids_list + .iter() + .zip(values_list) + .zip(salts_list) + .zip(version_keys) + { + let hash = + Pallet::::get_commit_hash(who, netuid_index, uids, values, salt, *version_key); + let commit_epoch = + Self::find_commit_epoch(&commits, hash).ok_or(Error::::NoWeightsCommitFound)?; + + if !Pallet::::is_reveal_block_range(netuid, commit_epoch) { + return Err(Error::::RevealTooEarly); + } + } + + Ok(()) + } + + fn check_commit_reveal(who: &T::AccountId, call: &Call) -> Result<(), Error> { + match call { + Call::reveal_weights { + netuid, + uids, + values, + salt, + version_key, + } => Self::check_reveal( + who, + *netuid, + NetUidStorageIndex::from(*netuid), + uids, + values, + salt, + *version_key, + ), + Call::reveal_mechanism_weights { + netuid, + mecid, + uids, + values, + salt, + version_key, + } => Self::check_reveal( + who, + *netuid, + Pallet::::get_mechanism_storage_index(*netuid, *mecid), + uids, + values, + salt, + *version_key, + ), + Call::batch_reveal_weights { + netuid, + uids_list, + values_list, + salts_list, + version_keys, + } => Self::check_batch_reveal( + who, + *netuid, + uids_list, + values_list, + salts_list, + version_keys, + ), + Call::commit_timelocked_weights { reveal_round, .. } + | Call::commit_timelocked_mechanism_weights { reveal_round, .. } + | Call::commit_crv3_mechanism_weights { reveal_round, .. } + if *reveal_round < pallet_drand::LastStoredRound::::get() => + { + Err(Error::::InvalidRevealRound) + } + _ => Ok(()), + } + } +} + +impl DispatchExtension> for CheckWeights +where + T: Config, + CallOf: Dispatchable + IsSubType>, + DispatchableOriginOf: OriginTrait, +{ + type Pre = (); + + fn weight(_call: &CallOf) -> Weight { + ::WeightInfo::check_weights_extension() + } + + fn pre_dispatch( + origin: &DispatchableOriginOf, + call: &CallOf, + ) -> Result { + let Some(who) = origin.as_signer() else { + return Ok(()); + }; + + let Some(call) = call.is_sub_type() else { + return Ok(()); + }; + + Self::check(who, call).map_err(Into::into) + } +} + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +mod tests { + use super::CheckWeights; + use crate::{Error, MAX_CRV3_COMMIT_SIZE_BYTES, tests::mock::*}; + use codec::Compact; + use frame_support::{ + BoundedVec, assert_ok, dispatch::DispatchResultWithPostInfo, traits::ConstU32, + traits::ExtendedDispatchable, + }; + use frame_system::Call as SystemCall; + use pallet_drand::LastStoredRound; + use sp_core::{H256, U256}; + use sp_runtime::DispatchError; + use subtensor_runtime_common::{MechId, NetUid}; + + fn dispatch_with_ext(call: RuntimeCall, origin: RuntimeOrigin) -> DispatchResultWithPostInfo { + as ExtendedDispatchable>::dispatch_with_extension( + origin, call, + ) + } + + fn err(result: DispatchResultWithPostInfo) -> DispatchError { + result.err().unwrap().error + } + + fn register_neuron(netuid: NetUid, hotkey: U256, coldkey: U256) { + add_network(netuid, 1, 0); + setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + register_ok_neuron(netuid, hotkey, coldkey, 0); + } + + fn set_weights_call(netuid: NetUid, uid: u16) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::set_weights { + netuid, + dests: vec![uid], + weights: vec![1], + version_key: 0, + }) + } + + fn reveal_weights_call(netuid: NetUid) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::reveal_weights { + netuid, + uids: vec![0], + values: vec![1], + salt: vec![1], + version_key: 0, + }) + } + + fn reveal_mechanism_weights_call(netuid: NetUid, mecid: MechId) -> RuntimeCall { + RuntimeCall::SubtensorModule(SubtensorCall::reveal_mechanism_weights { + netuid, + mecid, + uids: vec![0], + values: vec![1], + salt: vec![1], + version_key: 0, + }) + } + + #[test] + fn unrelated_calls_pass_through() { + new_test_ext(0).execute_with(|| { + let call = RuntimeCall::System(SystemCall::remark { remark: vec![] }); + + assert_ok!(dispatch_with_ext( + call, + RuntimeOrigin::signed(U256::from(1)) + )); + }); + } + + #[test] + fn mismatched_batch_lengths_are_blocked() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let calls = [ + RuntimeCall::SubtensorModule(SubtensorCall::batch_commit_weights { + netuids: vec![Compact(netuid)], + commit_hashes: vec![], + }), + RuntimeCall::SubtensorModule(SubtensorCall::batch_reveal_weights { + netuid, + uids_list: vec![vec![0]], + values_list: vec![], + salts_list: vec![vec![1]], + version_keys: vec![0], + }), + RuntimeCall::SubtensorModule(SubtensorCall::batch_set_weights { + netuids: vec![Compact(netuid)], + weights: vec![], + version_keys: vec![Compact(0_u64)], + }), + ]; + + for call in calls { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))), + Error::::InputLengthsUnequal.into() + ); + } + }); + } + + #[test] + fn low_stake_weight_calls_are_blocked() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + let bounded_commit = + BoundedVec::>::try_from(vec![0]).unwrap(); + add_network_disable_commit_reveal(netuid, 1, 0); + setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + SubtensorModule::set_stake_threshold(1_000_000_000_000_u64); + + let calls = [ + set_weights_call(netuid, 0), + RuntimeCall::SubtensorModule(SubtensorCall::set_mechanism_weights { + netuid, + mecid: MechId::MAIN, + dests: vec![0], + weights: vec![1], + version_key: 0, + }), + RuntimeCall::SubtensorModule(SubtensorCall::batch_set_weights { + netuids: vec![Compact(netuid)], + weights: vec![vec![(Compact(0_u16), Compact(1_u16))]], + version_keys: vec![Compact(0_u64)], + }), + RuntimeCall::SubtensorModule(SubtensorCall::commit_weights { + netuid, + commit_hash: H256::zero(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::commit_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit_hash: H256::zero(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::batch_commit_weights { + netuids: vec![Compact(netuid)], + commit_hashes: vec![H256::zero()], + }), + reveal_weights_call(netuid), + reveal_mechanism_weights_call(netuid, MechId::MAIN), + RuntimeCall::SubtensorModule(SubtensorCall::batch_reveal_weights { + netuid, + uids_list: vec![vec![0]], + values_list: vec![vec![1]], + salts_list: vec![vec![1]], + version_keys: vec![0], + }), + RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_weights { + netuid, + commit: bounded_commit.clone(), + reveal_round: 0, + commit_reveal_version: 0, + }), + RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit: bounded_commit.clone(), + reveal_round: 0, + commit_reveal_version: 0, + }), + RuntimeCall::SubtensorModule(SubtensorCall::commit_crv3_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit: bounded_commit, + reveal_round: 0, + }), + ]; + + for call in calls { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))), + Error::::NotEnoughStakeToSetWeights.into() + ); + } + }); + } + + #[test] + fn valid_set_weights_call_dispatches() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + add_network_disable_commit_reveal(netuid, 1, 0); + setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + register_ok_neuron(netuid, hotkey, coldkey, 0); + SubtensorModule::set_stake_threshold(0); + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + + assert_ok!(dispatch_with_ext( + set_weights_call(netuid, uid), + RuntimeOrigin::signed(hotkey) + )); + }); + } + + #[test] + fn missing_commit_is_blocked() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + register_neuron(netuid, hotkey, coldkey); + SubtensorModule::set_stake_threshold(0); + let calls = [ + reveal_weights_call(netuid), + reveal_mechanism_weights_call(netuid, MechId::MAIN), + RuntimeCall::SubtensorModule(SubtensorCall::batch_reveal_weights { + netuid, + uids_list: vec![vec![0]], + values_list: vec![vec![1]], + salts_list: vec![vec![1]], + version_keys: vec![0], + }), + ]; + + for call in calls { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))), + Error::::NoWeightsCommitFound.into() + ); + } + }); + } + + #[test] + fn reveal_before_window_is_blocked() { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + let uids = vec![0]; + let values = vec![1]; + let salt = vec![1]; + let version_key = 0; + register_neuron(netuid, hotkey, coldkey); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(0); + let commit_hash = SubtensorModule::get_commit_hash( + &hotkey, + netuid.into(), + &uids, + &values, + &salt, + version_key, + ); + assert_ok!(SubtensorModule::commit_weights( + RuntimeOrigin::signed(hotkey), + netuid, + commit_hash, + )); + + assert_eq!( + err(dispatch_with_ext( + reveal_weights_call(netuid), + RuntimeOrigin::signed(hotkey) + )), + Error::::RevealTooEarly.into() + ); + }); + } + + #[test] + fn valid_mechanism_reveal_dispatches() { + for (mecid, mechanism_count) in [ + (MechId::MAIN, None), + (MechId::from(1_u8), Some(MechId::from(2_u8))), + ] { + new_test_ext(0).execute_with(|| { + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + let uids = vec![0]; + let values = vec![1]; + let salt = vec![1]; + let version_key = 0; + register_neuron(netuid, hotkey, coldkey); + if let Some(mechanism_count) = mechanism_count { + crate::MechanismCountCurrent::::insert(netuid, mechanism_count); + } + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_stake_threshold(0); + + let commit_hash = SubtensorModule::get_commit_hash( + &hotkey, + SubtensorModule::get_mechanism_storage_index(netuid, mecid), + &uids, + &values, + &salt, + version_key, + ); + assert_ok!(SubtensorModule::commit_mechanism_weights( + RuntimeOrigin::signed(hotkey), + netuid, + mecid, + commit_hash, + )); + step_epochs(1, netuid); + + let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_mechanism_weights { + netuid, + mecid, + uids, + values, + salt, + version_key, + }); + assert_ok!(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))); + }); + } + } + + #[test] + fn invalid_reveal_round_is_blocked() { + new_test_ext(0).execute_with(|| { + LastStoredRound::::put(1_000_u64); + let netuid = NetUid::from(1); + let hotkey = U256::from(1); + let coldkey = U256::from(2); + register_neuron(netuid, hotkey, coldkey); + SubtensorModule::set_stake_threshold(0); + let commit = + BoundedVec::>::try_from(vec![0]).unwrap(); + let calls = [ + RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_weights { + netuid, + commit: commit.clone(), + reveal_round: 999, + commit_reveal_version: 0, + }), + RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit: commit.clone(), + reveal_round: 999, + commit_reveal_version: 0, + }), + RuntimeCall::SubtensorModule(SubtensorCall::commit_crv3_mechanism_weights { + netuid, + mecid: MechId::MAIN, + commit, + reveal_round: 999, + }), + ]; + + for call in calls { + assert_eq!( + err(dispatch_with_ext(call, RuntimeOrigin::signed(hotkey))), + Error::::InvalidRevealRound.into() + ); + } + }); + } +} diff --git a/pallets/subtensor/src/guards/mod.rs b/pallets/subtensor/src/guards/mod.rs index 44fba0c50f..485fc65a04 100644 --- a/pallets/subtensor/src/guards/mod.rs +++ b/pallets/subtensor/src/guards/mod.rs @@ -1,3 +1,13 @@ mod check_coldkey_swap; +mod check_delegate_take; +mod check_evm_key_association; +mod check_rate_limits; +mod check_serving_endpoints; +mod check_weights; pub use check_coldkey_swap::*; +pub use check_delegate_take::*; +pub use check_evm_key_association::*; +pub use check_rate_limits::*; +pub use check_serving_endpoints::*; +pub use check_weights::*; diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 828b8e6fb8..b1afef3401 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -17,8 +17,6 @@ use frame_support::{ pallet_prelude::*, traits::tokens::fungible, }; -use pallet_balances::Call as BalancesCall; -// use pallet_scheduler as Scheduler; use scale_info::TypeInfo; use sp_core::Get; use sp_runtime::DispatchError; @@ -926,6 +924,12 @@ pub mod pallet { // T::InitialHotkeyEmissionTempo::get() // } (DEPRECATED) + /// Default per-block epoch cap, seeded from the runtime-configured initial value. + #[pallet::type_value] + pub fn DefaultMaxEpochsPerBlock() -> u8 { + T::InitialMaxEpochsPerBlock::get() + } + /// Default value for rate limiting #[pallet::type_value] pub fn DefaultTxRateLimit() -> u64 { @@ -1343,11 +1347,6 @@ pub mod pallet { pub type SubnetTAO = StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao>; - /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. - #[pallet::storage] - pub type SubnetTaoProvided = - StorageMap<_, Identity, NetUid, TaoBalance, ValueQuery, DefaultZeroTao>; - /// --- MAP ( netuid ) --> alpha_in_emission | Returns the amount of alph in emission into the pool per block. #[pallet::storage] pub type SubnetAlphaInEmission = @@ -1390,11 +1389,6 @@ pub mod pallet { pub type SubnetAlphaIn = StorageMap<_, Identity, NetUid, AlphaBalance, ValueQuery, DefaultZeroAlpha>; - /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. - #[pallet::storage] - pub type SubnetAlphaInProvided = - StorageMap<_, Identity, NetUid, AlphaBalance, ValueQuery, DefaultZeroAlpha>; - /// --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. #[pallet::storage] pub type SubnetAlphaOut = @@ -1550,6 +1544,19 @@ pub mod pallet { OptionQuery, >; + /// --- NMAP ( netuid, hotkey, coldkey ) --> () | Reverse index for non-zero locks targeting this hotkey on this subnet. + #[pallet::storage] + pub type LockingColdkeys = StorageNMap< + _, + ( + NMapKey, // subnet + NMapKey, // hotkey + NMapKey, // coldkey + ), + (), + OptionQuery, + >; + /// --- DMAP ( netuid, hotkey ) --> LockState | Total lock per hotkey per subnet. #[pallet::storage] pub type HotkeyLock = StorageDoubleMap< @@ -1801,6 +1808,50 @@ pub mod pallet { #[pallet::storage] pub type Tempo = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultTempo>; + /// Lower bound for owner-set tempo. Also the fixed cooldown for `set_tempo`. + pub const MIN_TEMPO: u16 = 360; + /// Upper bound for owner-set tempo (≈ 7 days at 12 s/block). + pub const MAX_TEMPO: u16 = 50_400; + /// Lower bound for activity-cutoff factor (per-mille). 1_000 = one full tempo. + pub const MIN_ACTIVITY_CUTOFF_FACTOR_MILLI: u32 = 1_000; + /// Upper bound for activity-cutoff factor (per-mille). 50_000 = 50 tempos. + pub const MAX_ACTIVITY_CUTOFF_FACTOR_MILLI: u32 = 50_000; + /// Default activity-cutoff factor (per-mille). 13_889 ≈ legacy 5000-block cutoff + /// at default tempo 360 (`13_889 * 360 / 1000 = 5_000`, exact via ceiling rounding). + pub const INITIAL_ACTIVITY_CUTOFF_FACTOR_MILLI: u32 = 13_889; + + /// Default value for activity-cutoff factor (per-mille). + #[pallet::type_value] + pub fn DefaultActivityCutoffFactorMilli() -> u32 { + INITIAL_ACTIVITY_CUTOFF_FACTOR_MILLI + } + + /// --- MAP ( netuid ) --> last epoch attempt block (consumed slot). + /// Drives normal-cadence scheduling and the admin freeze window. + /// Advances on every `should_run_epoch == true` slot — including consistency-skipped slots — + /// and on a successful `set_tempo` (cycle reset). + #[pallet::storage] + pub type LastEpochBlock = + StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; + + /// --- MAP ( netuid ) --> block at which a manually triggered epoch should fire. + /// `0` means no trigger pending. Cleared after the triggered epoch runs. + #[pallet::storage] + pub type PendingEpochAt = + StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; + + /// --- MAP ( netuid ) --> monotonic epoch counter. + /// Incremented by exactly one each time the subnet's epoch slot is consumed in `run_coinbase` + #[pallet::storage] + pub type SubnetEpochIndex = + StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; + + /// --- MAP ( netuid ) --> activity-cutoff factor in per-mille epochs (1/1000 granularity). + /// Effective cutoff in blocks = `(factor × tempo) / 1000`, clamped to ≥ 1. + #[pallet::storage] + pub type ActivityCutoffFactorMilli = + StorageMap<_, Identity, NetUid, u32, ValueQuery, DefaultActivityCutoffFactorMilli>; + /// ============================ /// ==== Subnet Parameters ===== /// ============================ @@ -1971,6 +2022,7 @@ pub mod pallet { StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmunityPeriod>; /// --- MAP ( netuid ) --> activity_cutoff + // #[deprecated(note = "Replaced by `ActivityCutoffFactorMilli` (per-mille of `Tempo`).")] #[pallet::storage] pub type ActivityCutoff = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultActivityCutoff>; @@ -2104,6 +2156,10 @@ pub mod pallet { DefaultRAORecycledForRegistration, >; + /// --- ITEM ( max_epochs_per_block ) + #[pallet::storage] + pub type MaxEpochsPerBlock = StorageValue<_, u8, ValueQuery, DefaultMaxEpochsPerBlock>; + /// --- ITEM ( tx_rate_limit ) #[pallet::storage] pub type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; @@ -2399,7 +2455,8 @@ pub mod pallet { #[pallet::storage] pub type StakeThreshold = StorageValue<_, u64, ValueQuery, DefaultStakeThreshold>; - /// --- MAP (netuid, who) --> VecDeque<(hash, commit_block, first_reveal_block, last_reveal_block)> | Stores a queue of commits for an account on a given netuid. + /// --- MAP (netuid, who) --> VecDeque<(hash, commit_epoch, commit_block, _unused)> + /// Stores a queue of commit-reveal-v2 commits for an account on a given netuid. #[pallet::storage] pub type WeightCommits = StorageDoubleMap< _, @@ -2481,20 +2538,6 @@ pub mod pallet { OptionQuery, >; - /// DMAP ( hot, cold, netuid ) --> rate limits for staking operations - /// Value contains just a marker: we use this map as a set. - #[pallet::storage] - pub type StakingOperationRateLimiter = StorageNMap< - _, - ( - NMapKey, // hot - NMapKey, // cold - NMapKey, // subnet - ), - bool, - ValueQuery, - >; - #[pallet::storage] // --- MAP(netuid ) --> Root claim threshold pub type RootClaimableThreshold = StorageMap<_, Blake2_128Concat, NetUid, I96F32, ValueQuery, DefaultMinRootClaimAmount>; @@ -2767,7 +2810,7 @@ pub struct TaoBalanceReserve(PhantomData); impl TokenReserve for TaoBalanceReserve { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> TaoBalance { - SubnetTAO::::get(netuid).saturating_add(SubnetTaoProvided::::get(netuid)) + SubnetTAO::::get(netuid) } fn increase_provided(netuid: NetUid, tao: TaoBalance) { @@ -2785,7 +2828,7 @@ pub struct AlphaBalanceReserve(PhantomData); impl TokenReserve for AlphaBalanceReserve { #![deny(clippy::expect_used)] fn reserve(netuid: NetUid) -> AlphaBalance { - SubnetAlphaIn::::get(netuid).saturating_add(SubnetAlphaInProvided::::get(netuid)) + SubnetAlphaIn::::get(netuid) } fn increase_provided(netuid: NetUid, alpha: AlphaBalance) { diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 7b94866b52..ad372d1e0e 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -120,6 +120,18 @@ mod config { /// Max burn lower bound. #[pallet::constant] type MaxBurnLowerBound: Get; + /// Lower bound for owner-set tempo. + #[pallet::constant] + type MinTempo: Get; + /// Upper bound for owner-set tempo. + #[pallet::constant] + type MaxTempo: Get; + /// Lower bound for the activity-cutoff factor (per-mille). + #[pallet::constant] + type MinActivityCutoffFactorMilli: Get; + /// Upper bound for the activity-cutoff factor (per-mille). + #[pallet::constant] + type MaxActivityCutoffFactorMilli: Get; /// Initial adjustment interval. #[pallet::constant] type InitialAdjustmentInterval: Get; @@ -270,5 +282,10 @@ mod config { /// Burn account ID #[pallet::constant] type BurnAccountId: Get; + /// Initial default per-block cap on number of subnet epochs that may + /// execute in a single `block_step`; the rest are deferred 1 block forward via + /// `PendingEpochAt`. + #[pallet::constant] + type InitialMaxEpochsPerBlock: Get; } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 0e5a386529..08e5bb8fdf 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1823,7 +1823,7 @@ mod dispatches { /// May emit a `EvmKeyAssociated` event on success #[pallet::call_index(93)] #[pallet::weight(( - Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().reads_writes(2, 1)), + ::WeightInfo::associate_evm_key(), DispatchClass::Normal, Pays::No ))] @@ -2593,12 +2593,45 @@ mod dispatches { let coldkey = ensure_signed(origin)?; Self::do_set_perpetual_lock(&coldkey, netuid, enabled) } + + /// Owner-side `set_tempo`. Validates `[MinTempo, MaxTempo]`, applies a fixed + /// `MinTempo`-block cooldown via `TransactionType::TempoUpdate`, respects the admin + /// freeze window, and resets the cycle (`LastEpochBlock = current_block`) on success. + #[pallet::call_index(139)] + #[pallet::weight(::WeightInfo::set_tempo())] + pub fn set_tempo(origin: OriginFor, netuid: NetUid, tempo: u16) -> DispatchResult { + Self::do_set_tempo(origin, netuid, tempo) + } + + /// `set_activity_cutoff_factor`. Per-mille (1/1000) units; `cutoff_blocks + /// = (factor × tempo) / 1000`. Validates `[MinActivityCutoffFactorMilli, + /// MaxActivityCutoffFactorMilli]`. Callable by the subnet owner (rate-limited + /// via `OwnerHyperparamUpdate`, respects the admin freeze window) or by root + /// (bypasses both). + #[pallet::call_index(140)] + #[pallet::weight(::WeightInfo::set_activity_cutoff_factor())] + pub fn set_activity_cutoff_factor( + origin: OriginFor, + netuid: NetUid, + factor_milli: u32, + ) -> DispatchResult { + Self::do_set_activity_cutoff_factor(origin, netuid, factor_milli) + } + + /// Owner-side `trigger_epoch`. Schedules an epoch to fire after `AdminFreezeWindow` + /// blocks. Rate-limited via the existing `OwnerHyperparamUpdate` pattern. + #[pallet::call_index(141)] + #[pallet::weight(::WeightInfo::trigger_epoch())] + pub fn trigger_epoch(origin: OriginFor, netuid: NetUid) -> DispatchResult { + Self::do_trigger_epoch(origin, netuid) + } + /// Sets or clears whether the caller rejects incoming locked alpha. /// /// Coldkeys reject locked alpha by default. Passing `false` opts the /// caller into receiving locked alpha from stake transfers or coldkey /// swaps. - #[pallet::call_index(139)] + #[pallet::call_index(142)] #[pallet::weight(( ::DbWeight::get().reads_writes(1, 1), DispatchClass::Normal, diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 8b454d609c..6401b5846d 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -213,14 +213,10 @@ mod errors { SubtokenDisabled, /// Too frequent hotkey swap on subnet HotKeySwapOnSubnetIntervalNotPassed, - /// Zero max stake amount - ZeroMaxStakeAmount, /// Invalid netuid duplication SameNetuid, /// The caller does not have enough balance for the operation. InsufficientBalance, - /// Too frequent staking operations - StakingOperationRateLimitExceeded, /// Invalid lease beneficiary to register the leased network. InvalidLeaseBeneficiary, /// Lease cannot end in the past. @@ -247,6 +243,8 @@ mod errors { SymbolAlreadyInUse, /// Incorrect commit-reveal version. IncorrectCommitRevealVersion, + /// Reveal round is older than the most recently stored DRAND round. + InvalidRevealRound, /// Reveal period is too large. RevealPeriodTooLarge, /// Reveal period is too small. @@ -305,6 +303,20 @@ mod errors { CannotUseSystemAccount, /// Trying to unlock more than locked UnlockAmountTooHigh, + /// The supplied tempo is outside the allowed range. + TempoOutOfBounds, + /// The supplied activity-cutoff factor is outside the allowed range. + ActivityCutoffFactorMilliOutOfBounds, + /// An epoch trigger is already pending for this subnet; wait for it to fire + /// before triggering again. + EpochTriggerAlreadyPending, + /// The next automatic epoch is already imminent; a manual trigger would have + /// no effect. + AutoEpochAlreadyImminent, + /// `trigger_epoch` is blocked because commit-reveal is enabled for this subnet: + /// an out-of-band epoch would desync the CRv3 reveal window from the wall-clock + /// Drand schedule and silently drop committed weights. + DynamicTempoBlockedByCommitReveal, /// The destination coldkey rejects incoming locked alpha. AccountRejectsLockedAlpha, } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 787b5b5503..7bc9bf450a 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -109,6 +109,8 @@ mod events { MaxBurnSet(NetUid, TaoBalance), /// setting min burn on a network. MinBurnSet(NetUid, TaoBalance), + /// setting the per-block epoch cap (dynamic tempo throttle). + MaxEpochsPerBlockSet(u8), /// setting the transaction rate limit. TxRateLimitSet(u64), /// setting the delegate take transaction rate limit. @@ -612,6 +614,42 @@ mod events { netuid: NetUid, }, + /// Activity-cutoff factor (per-mille) set on a subnet by its owner. + ActivityCutoffFactorMilliSet { + /// The subnet identifier. + netuid: NetUid, + /// Factor (per-mille). + factor_milli: u32, + }, + + /// Owner manually triggered an epoch for their subnet. + EpochTriggered { + /// The subnet identifier. + netuid: NetUid, + /// The account that triggered the epoch. + by: T::AccountId, + /// The earliest block at which the triggered epoch may execute. + fires_at: u64, + }, + + /// An epoch slot was deferred to the next block due to the per-block epoch cap. + EpochDeferred { + /// The subnet identifier. + netuid: NetUid, + /// Block at which the epoch was originally scheduled. + from_block: u64, + /// Block to which the epoch was deferred. + to_block: u64, + }, + + /// Epoch execution skipped by `is_epoch_input_state_consistent` returned false or other errors. + EpochSkipped { + /// The subnet identifier. + netuid: NetUid, + /// The block at which the slot was consumed. + block: u64, + }, + /// Subnet ownership was reassigned by lock conviction. SubnetOwnerChanged { /// The subnet whose owner changed. diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index a950c6c97a..6371f30e46 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -38,17 +38,6 @@ mod hooks { } } - // ---- Called on the finalization of this pallet. The code weight must be taken into account prior to the execution of this macro. - // - // # Args: - // * 'n': (BlockNumberFor): - // - The number of the block we are finalizing. - fn on_finalize(_block_number: BlockNumberFor) { - for _ in StakingOperationRateLimiter::::drain() { - // Clear all entries each block - } - } - fn on_runtime_upgrade() -> frame_support::weights::Weight { // --- Migrate storage let mut weight = frame_support::weights::Weight::from_parts(0, 0); @@ -181,6 +170,10 @@ mod hooks { .saturating_add(migrations::migrate_remove_deprecated_conviction_maps::migrate_remove_deprecated_conviction_maps::()) // Reset testnet conviction lock storage before deploying the current design. .saturating_add(migrations::migrate_reset_tnet_conviction_locks::migrate_reset_tnet_conviction_locks::()) + // Seed LastEpochBlock for dynamic-tempo / owner-triggered-epochs feature + .saturating_add(migrations::migrate_dynamic_tempo::migrate_dynamic_tempo::()) + // Populate locking reverse map. + .saturating_add(migrations::migrate_populate_locking_coldkeys::migrate_populate_locking_coldkeys::()) // Capture the runtime-upgrade block for TAO-in refund cutover. .saturating_add(migrations::migrate_tao_in_refund_deployment_block::migrate_tao_in_refund_deployment_block::()) // Fix lock state left behind by subnet-scoped hotkey swaps. diff --git a/pallets/subtensor/src/migrations/migrate_cleanup_swap_v3.rs b/pallets/subtensor/src/migrations/migrate_cleanup_swap_v3.rs new file mode 100644 index 0000000000..cebbf373ec --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_cleanup_swap_v3.rs @@ -0,0 +1,70 @@ +use super::*; +use crate::HasMigrationRun; +use frame_support::{storage_alias, traits::Get, weights::Weight}; +use scale_info::prelude::string::String; + +pub mod deprecated_swap_maps { + use super::*; + + /// --- MAP ( netuid ) --> tao_in_user_subnet | Returns the amount of TAO in the subnet reserve provided by users as liquidity. + #[storage_alias] + pub type SubnetTaoProvided = + StorageMap, Identity, NetUid, TaoBalance, ValueQuery>; + + /// --- MAP ( netuid ) --> alpha_supply_user_in_pool | Returns the amount of alpha in the pool provided by users as liquidity. + #[storage_alias] + pub type SubnetAlphaInProvided = + StorageMap, Identity, NetUid, AlphaBalance, ValueQuery>; +} + +pub fn migrate_cleanup_swap_v3() -> Weight { + let migration_name = b"migrate_cleanup_swap_v3".to_vec(); + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name), + ); + + // ------------------------------ + // Step 1: Move provided to reserves + // ------------------------------ + for (netuid, tao_provided) in deprecated_swap_maps::SubnetTaoProvided::::iter() { + SubnetTAO::::mutate(netuid, |total| { + *total = total.saturating_add(tao_provided); + }); + } + for (netuid, alpha_provided) in deprecated_swap_maps::SubnetAlphaInProvided::::iter() { + SubnetAlphaIn::::mutate(netuid, |total| { + *total = total.saturating_add(alpha_provided); + }); + } + + // ------------------------------ + // Step 2: Remove Map entries + // ------------------------------ + remove_prefix::("SubtensorModule", "SubnetTaoProvided", &mut weight); + remove_prefix::("SubtensorModule", "SubnetAlphaInProvided", &mut weight); + + // ------------------------------ + // Step 3: Mark Migration as Completed + // ------------------------------ + + HasMigrationRun::::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/migrate_dynamic_tempo.rs b/pallets/subtensor/src/migrations/migrate_dynamic_tempo.rs new file mode 100644 index 0000000000..c359b96c2f --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_dynamic_tempo.rs @@ -0,0 +1,163 @@ +use super::*; +use frame_support::{traits::Get, weights::Weight}; +use log; +use scale_info::prelude::string::String; +use sp_core::H256; +use sp_std::collections::vec_deque::VecDeque; + +/// One-shot migration for the dynamic-tempo / owner-triggered-epochs feature. +/// +/// 1. Back-fills `LastEpochBlock[netuid]` for every existing subnet so the first +/// post-upgrade epoch lands on the same block as the legacy modulo formula +/// `(block + netuid + 1) % (tempo + 1) == 0`. The new scheduler period is +/// `tempo` (next firing at `LastEpochBlock + tempo`). +/// Existing `Tempo[netuid]` values are preserved as-is regardless of whether +/// they fall inside `[MIN_TEMPO, MAX_TEMPO]`. Owner-side `set_tempo` enforces +/// the bounds for new updates; root-side `sudo_set_tempo` can still write any +/// `u16`. Subnets with `Tempo == 0` are left as-is — the legacy short-circuit +/// keeps them dormant and matches their pre-upgrade behaviour. +/// 2. Converts each subnet's existing `ActivityCutoff[netuid]` (absolute block count) +/// into `ActivityCutoffFactorMilli[netuid]` (per-mille of `tempo`) so that +/// `factor * tempo / 1000 ≈ old_cutoff` post-upgrade. Production defaults +/// (`tempo=360`, `cutoff=5000`) round-trip to 5000 blocks exactly via ceiling +/// division. Out-of-range factors are clamped to +/// `[MIN_ACTIVITY_CUTOFF_FACTOR_MILLI, MAX_ACTIVITY_CUTOFF_FACTOR_MILLI]` — +/// extreme historical cutoffs may shift to the nearest representable factor. +/// 3. Seeds `SubnetEpochIndex[netuid]` (the new stateful epoch counter) with the +/// legacy modulo epoch index `(block + netuid + 1) / (tempo + 1)` so that +/// existing commit-reveal commit keys — `TimelockedWeightCommits` (CR-v4) keyed +/// by epoch, and `WeightCommits` (CR-v2) tagged with `commit_epoch` — stay +/// valid and continuous across the upgrade. +/// 4. Rewrites every CR-v2 `WeightCommits` entry to `(hash, commit_epoch, +/// commit_block, _)`: field 1 (previously the absolute `commit_block`) becomes +/// `commit_epoch` under the legacy modulo formula; field 2 keeps the absolute +/// `commit_block` (used by the epoch's commit-reveal weight column-mask). +pub fn migrate_dynamic_tempo() -> Weight { + let mig_name: Vec = b"dynamic_tempo_v1".to_vec(); + let mig_name_str = String::from_utf8_lossy(&mig_name); + + let mut total_weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(&mig_name) { + log::info!("Migration '{mig_name_str}' already executed - skipping"); + return total_weight; + } + + log::info!("Running migration '{mig_name_str}'"); + + let current_block = Pallet::::get_current_block_as_u64(); + let mut visited: u64 = 0; + let mut last_epoch_seeded: u64 = 0; + let mut epoch_index_seeded: u64 = 0; + let mut activity_factor_seeded: u64 = 0; + let mut activity_factor_clamped: u64 = 0; + let mut crv2_commits_converted: u64 = 0; + let mut reads: u64 = 0; + let mut writes: u64 = 0; + + let netuids: Vec = Tempo::::iter_keys().collect(); + reads = reads.saturating_add(netuids.len() as u64); + + for netuid in netuids.into_iter() { + visited = visited.saturating_add(1); + let tempo = Tempo::::get(netuid); + reads = reads.saturating_add(1); + + if tempo == 0 { + // Legacy `tempo == 0` short-circuit preserved; do not seed `LastEpochBlock`. + continue; + } + + // Compute next-epoch block under the *legacy* modulo formula and back-fill + // `LastEpochBlock` so the *new* scheduler fires its first epoch on the same + // block the legacy chain would have. + // Legacy `blocks_until_next_epoch` (pre-upgrade behaviour, period `tempo + 1`): + // adjusted = current_block + netuid + 1 + // remainder = adjusted % (tempo + 1) + // blocks_until_next = tempo - remainder + // New scheduler period is `tempo`, next firing at `LastEpochBlock + tempo`. + // Solve for `LastEpochBlock`: + // LastEpochBlock = current_block + blocks_until_next - tempo + // = current_block - (tempo - blocks_until_next) + let netuid_plus_one = (u16::from(netuid) as u64).saturating_add(1); + let tempo_plus_one = (tempo as u64).saturating_add(1); + let adjusted = current_block.wrapping_add(netuid_plus_one); + let remainder = adjusted.checked_rem(tempo_plus_one).unwrap_or(0); + let blocks_until_next = (tempo as u64).saturating_sub(remainder); + let offset = (tempo as u64).saturating_sub(blocks_until_next); + let last_epoch = current_block.saturating_sub(offset); + + LastEpochBlock::::insert(netuid, last_epoch); + last_epoch_seeded = last_epoch_seeded.saturating_add(1); + writes = writes.saturating_add(1); + + // Seed the stateful epoch counter with the legacy modulo epoch index + // `(current_block + netuid + 1) / (tempo + 1)` so CR commit keys + // (TimelockedWeightCommits epoch keys, WeightCommits commit_epoch) stay + // continuous across the upgrade. + let legacy_epoch = adjusted.checked_div(tempo_plus_one).unwrap_or(0); + SubnetEpochIndex::::insert(netuid, legacy_epoch); + epoch_index_seeded = epoch_index_seeded.saturating_add(1); + writes = writes.saturating_add(1); + + // Convert legacy absolute `ActivityCutoff` into per-mille `ActivityCutoffFactorMilli` + let old_cutoff = ActivityCutoff::::get(netuid) as u64; + reads = reads.saturating_add(1); + let tempo_u64 = tempo as u64; + let raw_factor = old_cutoff + .saturating_mul(1_000) + .saturating_add(tempo_u64.saturating_sub(1)) + .checked_div(tempo_u64) + .unwrap_or(INITIAL_ACTIVITY_CUTOFF_FACTOR_MILLI as u64); + let clamped = raw_factor + .max(MIN_ACTIVITY_CUTOFF_FACTOR_MILLI as u64) + .min(MAX_ACTIVITY_CUTOFF_FACTOR_MILLI as u64) as u32; + if clamped as u64 != raw_factor { + activity_factor_clamped = activity_factor_clamped.saturating_add(1); + } + ActivityCutoffFactorMilli::::insert(netuid, clamped); + activity_factor_seeded = activity_factor_seeded.saturating_add(1); + writes = writes.saturating_add(1); + } + + // --- CR-v2: rewrite every `WeightCommits` entry to the + // `(hash, commit_epoch, commit_block, _)` layout. Field 1 was the absolute + // `commit_block`; it becomes `commit_epoch` (legacy modulo epoch). Field 2 + // keeps the absolute `commit_block` (used by the epoch column-mask). + let crv2_entries: Vec<_> = WeightCommits::::iter().collect(); + reads = reads.saturating_add(crv2_entries.len() as u64); + for (netuid_index, account, commits) in crv2_entries.into_iter() { + let (netuid, _) = Pallet::::get_netuid_and_subid(netuid_index).unwrap_or_default(); + let tempo = Tempo::::get(netuid); + reads = reads.saturating_add(1); + let tempo_plus_one = (tempo as u64).saturating_add(1); + let netuid_plus_one = (u16::from(netuid) as u64).saturating_add(1); + + let converted: VecDeque<(H256, u64, u64, u64)> = commits + .into_iter() + .map(|(hash, commit_block, _, _)| { + let commit_epoch = commit_block + .saturating_add(netuid_plus_one) + .checked_div(tempo_plus_one) + .unwrap_or(0); + (hash, commit_epoch, commit_block, 0u64) + }) + .collect(); + WeightCommits::::insert(netuid_index, account, converted); + crv2_commits_converted = crv2_commits_converted.saturating_add(1); + writes = writes.saturating_add(1); + } + + total_weight = total_weight.saturating_add(T::DbWeight::get().reads_writes(reads, writes)); + + log::info!( + "Dynamic tempo migration: visited={visited}, last_epoch_seeded={last_epoch_seeded}, epoch_index_seeded={epoch_index_seeded}, activity_factor_seeded={activity_factor_seeded}, activity_factor_clamped={activity_factor_clamped}, crv2_commits_converted={crv2_commits_converted}" + ); + + HasMigrationRun::::insert(&mig_name, true); + total_weight = total_weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!("Migration '{mig_name_str}' completed"); + + total_weight +} diff --git a/pallets/subtensor/src/migrations/migrate_populate_locking_coldkeys.rs b/pallets/subtensor/src/migrations/migrate_populate_locking_coldkeys.rs new file mode 100644 index 0000000000..c1220c2077 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_populate_locking_coldkeys.rs @@ -0,0 +1,71 @@ +use alloc::string::String; +use frame_support::{traits::Get, weights::Weight}; + +use crate::{Config, HasMigrationRun, Lock, Pallet as Subtensor}; + +const MIGRATION_NAME: &[u8] = b"migrate_populate_locking_coldkeys"; + +pub fn migrate_populate_locking_coldkeys() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(MIGRATION_NAME) { + log::info!( + "Migration '{}' already executed - skipping", + String::from_utf8_lossy(MIGRATION_NAME) + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(MIGRATION_NAME) + ); + + let now = Subtensor::::get_current_block_as_u64(); + let unlock_rate = crate::UnlockRate::::get(); + let maturity_rate = crate::MaturityRate::::get(); + let mut scanned_count = 0u64; + let mut indexed_count = 0u64; + let mut removed_count = 0u64; + let lock_keys = Lock::::iter_keys().collect::>(); + + // Note for AI reviewer: Lock has very few records at the time of this migration, + // so iterating it once here is safe and keeps the migration simple. + for (coldkey, netuid, hotkey) in lock_keys { + scanned_count = scanned_count.saturating_add(1); + + let mut model = + Subtensor::::read_conviction_model_for_hotkey(&coldkey, netuid, &hotkey, now); + model.roll_forward(now, unlock_rate, maturity_rate); + + if model.individual_lock().is_zero() { + removed_count = removed_count.saturating_add(1); + } else { + indexed_count = indexed_count.saturating_add(1); + } + + Subtensor::::save_conviction_model(&coldkey, netuid, &hotkey, model); + } + + weight = weight.saturating_add(T::DbWeight::get().reads(scanned_count)); + weight = weight.saturating_add( + T::DbWeight::get().writes( + indexed_count + .saturating_mul(2) + .saturating_add(removed_count.saturating_mul(3)), + ), + ); + + HasMigrationRun::::insert(MIGRATION_NAME, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{}' completed. scanned_entries={}, indexed_entries={}, removed_zero_entries={}", + String::from_utf8_lossy(MIGRATION_NAME), + scanned_count, + indexed_count, + removed_count + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index b775cfaa49..e846d325dc 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -5,6 +5,7 @@ use sp_io::KillStorageResult; use sp_io::hashing::twox_128; use sp_io::storage::clear_prefix; pub mod migrate_auto_stake_destination; +pub mod migrate_cleanup_swap_v3; pub mod migrate_clear_deprecated_registration_maps; pub mod migrate_coldkey_swap_scheduled; pub mod migrate_coldkey_swap_scheduled_to_announcements; @@ -16,6 +17,7 @@ pub mod migrate_crv3_v2_to_timelocked; pub mod migrate_delete_subnet_21; pub mod migrate_delete_subnet_3; pub mod migrate_disable_commit_reveal; +pub mod migrate_dynamic_tempo; pub mod migrate_fix_bad_hk_swap; pub mod migrate_fix_childkeys; pub mod migrate_fix_is_network_member; @@ -33,6 +35,7 @@ pub mod migrate_network_lock_cost_2500; pub mod migrate_network_lock_reduction_interval; pub mod migrate_orphaned_storage_items; pub mod migrate_pending_emissions; +pub mod migrate_populate_locking_coldkeys; pub mod migrate_populate_owned_hotkeys; pub mod migrate_rao; pub mod migrate_rate_limit_keys; diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index ec61f2e596..2dbaa883d9 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -10,7 +10,7 @@ use substrate_fixed::types::I96F32; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{AlphaBalance, MechId, NetUid, NetUidStorageIndex, TaoBalance}; -#[freeze_struct("fbab6d1e7f3c69ae")] +#[freeze_struct("54520f5534d7e59e")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct Metagraph { // Subnet index @@ -54,7 +54,7 @@ pub struct Metagraph { max_weights_limit: Compact, // max allowed weights per val weights_version: Compact, // allowed weights version weights_rate_limit: Compact, // rate limit on weights - activity_cutoff: Compact, // validator weights cut off period in blocks + activity_cutoff: Compact, // validator weights cut off period in blocks max_validators: Compact, // max allowed validators // Registration @@ -110,7 +110,7 @@ pub struct Metagraph { alpha_dividends_per_hotkey: Vec<(AccountId, Compact)>, // List of dividend payout in alpha via subnet. } -#[freeze_struct("3ff2befdb7b393ea")] +#[freeze_struct("5f9c8beab622882c")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SelectiveMetagraph { // Subnet index @@ -154,8 +154,8 @@ pub struct SelectiveMetagraph { max_weights_limit: Option>, // max allowed weights per val weights_version: Option>, // allowed weights version weights_rate_limit: Option>, // rate limit on weights - activity_cutoff: Option>, // validator weights cut off period in blocks - max_validators: Option>, // max allowed validators + activity_cutoff: Option>, // validator weights cut off period in blocks (effective = factor × tempo / 1000) + max_validators: Option>, // max allowed validators // Registration num_uids: Option>, @@ -710,7 +710,7 @@ impl Pallet { max_weights_limit: Self::get_max_weight_limit(netuid).into(), // max allowed weight weights_version: Self::get_weights_version_key(netuid).into(), // allowed weights version weights_rate_limit: Self::get_weights_set_rate_limit(netuid).into(), // rate limit on weights. - activity_cutoff: Self::get_activity_cutoff(netuid).into(), // validator weights cut off period in blocks + activity_cutoff: Self::get_activity_cutoff_blocks(netuid).into(), // validator weights cut off period in blocks max_validators: Self::get_max_allowed_validators(netuid).into(), // max allowed validators. // Registration @@ -1051,7 +1051,7 @@ impl Pallet { }, Some(SelectiveMetagraphIndex::ActivityCutoff) => SelectiveMetagraph { netuid: netuid.into(), - activity_cutoff: Some(Self::get_activity_cutoff(netuid).into()), + activity_cutoff: Some(Self::get_activity_cutoff_blocks(netuid).into()), ..Default::default() }, Some(SelectiveMetagraphIndex::MaxValidators) => SelectiveMetagraph { diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 545edf6db6..2d3316f34d 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -2,6 +2,7 @@ extern crate alloc; use codec::Compact; use frame_support::pallet_prelude::{Decode, Encode}; +use sp_std::collections::btree_map::BTreeMap; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -21,6 +22,29 @@ pub struct StakeInfo { is_registered: bool, } +#[freeze_struct("2d52e2de04425fb6")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct StakeAvailability { + total: Compact, + locked: Compact, + available: Compact, +} + +// Per-subnet stake breakdown: total alpha, locked mass, and what is free to unstake. +impl StakeAvailability { + pub fn total(&self) -> AlphaBalance { + self.total.into() + } + + pub fn locked(&self) -> AlphaBalance { + self.locked.into() + } + + pub fn available(&self) -> AlphaBalance { + self.available.into() + } +} + impl Pallet { fn _get_stake_info_for_coldkeys( coldkeys: Vec, @@ -119,6 +143,69 @@ impl Pallet { }) } + /// Batch query of unstakable stake per coldkey and subnet. + /// + /// `netuids: None` scans every subnet; `Some(vec)` limits the scan. + /// Subnets with zero stake and zero lock are left out of the response. + /// + /// Invalid `Some(vec)` requests (empty or longer than the number of subnets on chain) + /// return each coldkey with an empty inner map. Non-existent netuids are omitted. + pub fn get_stake_availability_for_coldkeys( + coldkey_accounts: Vec, + netuids: Option>, + ) -> BTreeMap> { + if coldkey_accounts.is_empty() { + return BTreeMap::new(); + } + + let existing_netuids = Self::get_all_subnet_netuids(); + + let netuids = match netuids { + None => existing_netuids, + Some(mut requested) => { + // Same netuid may appear more than once in the request — keep one row per subnet. + requested.sort(); + requested.dedup(); + if requested.is_empty() || requested.len() > existing_netuids.len() { + return coldkey_accounts + .into_iter() + .map(|coldkey| (coldkey, BTreeMap::new())) + .collect(); + } + requested.retain(|n| Self::if_subnet_exist(*n)); + requested + } + }; + + coldkey_accounts + .into_iter() + .map(|coldkey| { + let availability: BTreeMap = netuids + .iter() + .filter_map(|netuid| { + let (total, locked, available) = + Self::stake_availability(&coldkey, *netuid); + // Nothing staked and no active lock — skip this subnet. + if total.is_zero() && locked.is_zero() { + None + } else { + Some(( + *netuid, + StakeAvailability { + total: total.into(), + locked: locked.into(), + available: available.into(), + }, + )) + } + }) + .collect(); + + (coldkey, availability) + }) + .collect() + } + pub fn get_stake_fee( origin: Option<(T::AccountId, NetUid)>, _origin_coldkey_account: T::AccountId, diff --git a/pallets/subtensor/src/rpc_info/subnet_info.rs b/pallets/subtensor/src/rpc_info/subnet_info.rs index 7446e4f6fc..aab27c19c6 100644 --- a/pallets/subtensor/src/rpc_info/subnet_info.rs +++ b/pallets/subtensor/src/rpc_info/subnet_info.rs @@ -53,7 +53,7 @@ pub struct SubnetInfov2 { identity: Option, } -#[freeze_struct("fd2db338b156d251")] +#[freeze_struct("5a0830a4518a7325")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SubnetHyperparams { rho: Compact, @@ -67,7 +67,7 @@ pub struct SubnetHyperparams { weights_version: Compact, weights_rate_limit: Compact, adjustment_interval: Compact, - activity_cutoff: Compact, + activity_cutoff: Compact, pub registration_allowed: bool, target_regs_per_interval: Compact, min_burn: Compact, @@ -85,7 +85,7 @@ pub struct SubnetHyperparams { liquid_alpha_enabled: bool, } -#[freeze_struct("bb4666554020e789")] +#[freeze_struct("336a6658e70b5554")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SubnetHyperparamsV2 { rho: Compact, @@ -99,7 +99,7 @@ pub struct SubnetHyperparamsV2 { weights_version: Compact, weights_rate_limit: Compact, adjustment_interval: Compact, - activity_cutoff: Compact, + activity_cutoff: Compact, pub registration_allowed: bool, target_regs_per_interval: Compact, min_burn: Compact, @@ -328,7 +328,7 @@ impl Pallet { let weights_version = Self::get_weights_version_key(netuid); let weights_rate_limit = Self::get_weights_set_rate_limit(netuid); let adjustment_interval = Self::get_adjustment_interval(netuid); - let activity_cutoff = Self::get_activity_cutoff(netuid); + let activity_cutoff = Self::get_activity_cutoff_blocks(netuid); let registration_allowed = Self::get_network_registration_allowed(netuid); let target_regs_per_interval = Self::get_target_registrations_per_interval(netuid); let min_burn = Self::get_min_burn(netuid); @@ -391,7 +391,7 @@ impl Pallet { let weights_version = Self::get_weights_version_key(netuid); let weights_rate_limit = Self::get_weights_set_rate_limit(netuid); let adjustment_interval = Self::get_adjustment_interval(netuid); - let activity_cutoff = Self::get_activity_cutoff(netuid); + let activity_cutoff = Self::get_activity_cutoff_blocks(netuid); let registration_allowed = Self::get_network_registration_allowed(netuid); let target_regs_per_interval = Self::get_target_registrations_per_interval(netuid); let min_burn = Self::get_min_burn(netuid); @@ -414,7 +414,6 @@ impl Pallet { let subnet_token_enabled = Self::get_subtoken_enabled(netuid); let transfers_enabled = Self::get_transfer_toggle(netuid); let bonds_reset = Self::get_bonds_reset(netuid); - let user_liquidity_enabled: bool = Self::is_user_liquidity_enabled(netuid); Some(SubnetHyperparamsV2 { rho: rho.into(), @@ -449,7 +448,7 @@ impl Pallet { subnet_is_active: subnet_token_enabled, transfers_enabled, bonds_reset_enabled: bonds_reset, - user_liquidity_enabled, + user_liquidity_enabled: false, }) } @@ -516,7 +515,12 @@ impl Pallet { .into(), ( "activity_cutoff", - HyperparamValue::U16(Self::get_activity_cutoff(netuid).into()), + HyperparamValue::U64(Self::get_activity_cutoff_blocks(netuid).into()), + ) + .into(), + ( + "activity_cutoff_factor", + HyperparamValue::U32(Self::get_activity_cutoff_factor_milli(netuid).into()), ) .into(), ( @@ -607,11 +611,7 @@ impl Pallet { HyperparamValue::Bool(Self::get_bonds_reset(netuid)), ) .into(), - ( - "user_liquidity_enabled", - HyperparamValue::Bool(Self::is_user_liquidity_enabled(netuid)), - ) - .into(), + ("user_liquidity_enabled", HyperparamValue::Bool(false),).into(), ( "owner_cut_enabled", HyperparamValue::Bool(Self::get_owner_cut_enabled(netuid)), @@ -622,6 +622,11 @@ impl Pallet { HyperparamValue::Bool(Self::get_owner_cut_auto_lock_enabled(netuid)), ) .into(), + ( + "min_childkey_take", + HyperparamValue::U16(Self::get_effective_min_childkey_take(netuid).into()), + ) + .into(), ]) } } diff --git a/pallets/subtensor/src/staking/add_stake.rs b/pallets/subtensor/src/staking/add_stake.rs index b88e75cd31..33cadf241b 100644 --- a/pallets/subtensor/src/staking/add_stake.rs +++ b/pallets/subtensor/src/staking/add_stake.rs @@ -66,7 +66,6 @@ impl Pallet { netuid, stake_to_be_added, T::SwapInterface::max_price(), - true, false, ) } @@ -155,7 +154,6 @@ impl Pallet { netuid, possible_stake, limit_price, - true, false, ) } @@ -172,7 +170,8 @@ impl Pallet { if limit_price >= 1_000_000_000.into() { return Ok(u64::MAX); } else { - return Err(Error::::ZeroMaxStakeAmount.into()); + // Price will never move down, so maximum amount that can be staked is zero + return Ok(0_u64); } } @@ -181,10 +180,6 @@ impl Pallet { let result = T::SwapInterface::swap(netuid.into(), order, limit_price, false, true) .map(|r| r.amount_paid_in.saturating_add(r.fee_paid))?; - if !result.is_zero() { - Ok(result.into()) - } else { - Err(Error::::ZeroMaxStakeAmount.into()) - } + Ok(result.into()) } } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 70e7f2ae57..f11012f0e2 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -1,7 +1,7 @@ use alloc::collections::BTreeMap; use safe_math::*; use share_pool::SafeFloat; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::{U64F64, U96F32}; use subtensor_runtime_common::{NetUid, TaoBalance}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -43,15 +43,13 @@ impl Pallet { Self::get_all_subnet_netuids() .into_iter() .map(|netuid| { - let alpha = U96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( + let alpha = U64F64::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( hotkey, netuid, )); - let alpha_price = U96F32::saturating_from_num( - T::SwapInterface::current_alpha_price(netuid.into()), - ); + let alpha_price = T::SwapInterface::current_alpha_price(netuid.into()); alpha.saturating_mul(alpha_price) }) - .sum::() + .sum::() .saturating_to_num::() .into() } @@ -71,7 +69,7 @@ impl Pallet { let order = GetTaoForAlpha::::with_amount(alpha_stake); T::SwapInterface::sim_swap(netuid.into(), order) .map(|r| { - let fee: u64 = U96F32::saturating_from_num(r.fee_paid) + let fee: u64 = U64F64::saturating_from_num(r.fee_paid) .saturating_mul(T::SwapInterface::current_alpha_price( netuid.into(), )) @@ -105,7 +103,7 @@ impl Pallet { let order = GetTaoForAlpha::::with_amount(alpha_stake); T::SwapInterface::sim_swap(netuid.into(), order) .map(|r| { - let fee: u64 = U96F32::saturating_from_num(r.fee_paid) + let fee: u64 = U64F64::saturating_from_num(r.fee_paid) .saturating_mul(T::SwapInterface::current_alpha_price( netuid.into(), )) @@ -238,7 +236,7 @@ impl Pallet { let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid); let min_alpha_stake = - U96F32::saturating_from_num(Self::get_nominator_min_required_stake()) + U64F64::saturating_from_num(Self::get_nominator_min_required_stake()) .safe_div(T::SwapInterface::current_alpha_price(netuid)) .saturating_to_num::(); if alpha_stake > 0.into() && alpha_stake < min_alpha_stake.into() { @@ -283,10 +281,6 @@ impl Pallet { } } - pub fn is_user_liquidity_enabled(netuid: NetUid) -> bool { - T::SwapInterface::is_user_liquidity_enabled(netuid) - } - /// The function clears Alpha map in batches. Each run will check ALPHA_MAP_BATCH_SIZE /// alphas. It keeps the alpha value stored when it's >= than MIN_ALPHA. /// The function uses AlphaMapLastKey as a storage for key iterator between runs. diff --git a/pallets/subtensor/src/staking/lock.rs b/pallets/subtensor/src/staking/lock.rs index 3e2f3c54e3..fcf699619d 100644 --- a/pallets/subtensor/src/staking/lock.rs +++ b/pallets/subtensor/src/staking/lock.rs @@ -9,6 +9,7 @@ use substrate_fixed::types::{I64F64, U64F64}; use subtensor_runtime_common::NetUid; pub const ONE_YEAR: u64 = 7200 * 365 + 1800; +pub const LOCK_STATE_ZERO_THRESHOLD: u64 = 100; /// Exponential lock state for a coldkey on a subnet. #[crate::freeze_struct("1f6be20a66128b8d")] @@ -22,6 +23,33 @@ pub struct LockState { pub last_update: u64, } +impl LockState { + pub fn is_zero(&self) -> bool { + self.locked_mass < AlphaBalance::from(LOCK_STATE_ZERO_THRESHOLD) + && self.conviction < U64F64::saturating_from_num(LOCK_STATE_ZERO_THRESHOLD) + } +} + +/// Unsigned decrease produced by rolling a lock forward. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct RollDelta { + pub locked_mass_delta: AlphaBalance, + pub conviction_delta: U64F64, +} + +impl RollDelta { + pub fn zero() -> Self { + Self { + locked_mass_delta: AlphaBalance::ZERO, + conviction_delta: U64F64::saturating_from_num(0), + } + } + + pub fn is_zero(&self) -> bool { + self.locked_mass_delta.is_zero() && self.conviction_delta == U64F64::saturating_from_num(0) + } +} + /// A struct that incapsulates Lock primitives such as adding, removing, /// rolling, and updating aggregates. /// @@ -75,54 +103,6 @@ impl ConvictionModel { } } - pub fn roll_forward(&mut self, now: u64, unlock_rate: u64, maturity_rate: u64) { - self.individual_lock = Self::roll_forward_lock( - self.individual_lock.clone(), - now, - unlock_rate, - maturity_rate, - self.owner_lock, - self.perpetual_lock, - ); - self.individual_lock_dirty = true; - self.agg_perpetual_general = Self::roll_forward_lock( - self.agg_perpetual_general.clone(), - now, - unlock_rate, - maturity_rate, - false, - true, - ); - self.agg_perpetual_general_dirty = true; - self.agg_decaying_general = Self::roll_forward_lock( - self.agg_decaying_general.clone(), - now, - unlock_rate, - maturity_rate, - false, - false, - ); - self.agg_decaying_general_dirty = true; - self.agg_perpetual_owner = Self::roll_forward_lock( - self.agg_perpetual_owner.clone(), - now, - unlock_rate, - maturity_rate, - true, - true, - ); - self.agg_perpetual_owner_dirty = true; - self.agg_decaying_owner = Self::roll_forward_lock( - self.agg_decaying_owner.clone(), - now, - unlock_rate, - maturity_rate, - true, - false, - ); - self.agg_decaying_owner_dirty = true; - } - pub fn individual_lock(&self) -> &LockState { &self.individual_lock } @@ -211,12 +191,13 @@ impl ConvictionModel { maturity_rate, self.owner_lock, self.perpetual_lock, - ); + ) + .0; self.individual_lock_dirty = true; } - pub fn roll_forward_individual(&mut self, now: u64, unlock_rate: u64, maturity_rate: u64) { - self.individual_lock = Self::roll_forward_lock( + pub fn roll_forward(&mut self, now: u64, unlock_rate: u64, maturity_rate: u64) { + let (rolled_individual_lock, roll_delta) = Self::roll_forward_lock( self.individual_lock.clone(), now, unlock_rate, @@ -224,7 +205,13 @@ impl ConvictionModel { self.owner_lock, self.perpetual_lock, ); + self.individual_lock = rolled_individual_lock; self.individual_lock_dirty = true; + if !roll_delta.is_zero() { + self.apply_roll_delta_to_aggregate(roll_delta, now); + } else { + self.roll_forward_aggregate(now, unlock_rate, maturity_rate); + } } pub fn roll_forward_aggregate(&mut self, now: u64, unlock_rate: u64, maturity_rate: u64) { @@ -238,7 +225,8 @@ impl ConvictionModel { maturity_rate, owner_lock, perpetual_lock, - ); + ) + .0; *aggregate_dirty = true; } @@ -254,6 +242,17 @@ impl ConvictionModel { *aggregate_dirty = true; } + fn apply_roll_delta_to_aggregate(&mut self, roll_delta: RollDelta, now: u64) { + let (aggregate, aggregate_dirty) = self.aggregate_mut(); + *aggregate = Self::reduce_lock( + aggregate, + roll_delta.locked_mass_delta, + roll_delta.conviction_delta, + ); + aggregate.last_update = now; + *aggregate_dirty = true; + } + pub fn reduce(&mut self, locked_mass: AlphaBalance, conviction: U64F64) { self.individual_lock = Self::reduce_lock(&self.individual_lock, locked_mass, conviction); self.individual_lock_dirty = true; @@ -414,7 +413,9 @@ impl ConvictionModel { maturity_rate: u64, owner_lock: bool, perpetual_lock: bool, - ) -> LockState { + ) -> (LockState, RollDelta) { + let previous_locked_mass = lock.locked_mass; + let previous_conviction = lock.conviction; let mut rolled = if now > lock.last_update { let dt = now.saturating_sub(lock.last_update); let (new_locked_mass, new_conviction) = Self::calculate_decayed_mass_and_conviction( @@ -439,11 +440,33 @@ impl ConvictionModel { rolled.conviction = U64F64::saturating_from_num(u64::from(rolled.locked_mass)); } - rolled + if rolled.is_zero() { + rolled.locked_mass = AlphaBalance::ZERO; + rolled.conviction = U64F64::saturating_from_num(0); + } + + let roll_delta = RollDelta { + locked_mass_delta: previous_locked_mass.saturating_sub(rolled.locked_mass), + conviction_delta: previous_conviction.saturating_sub(rolled.conviction), + }; + + (rolled, roll_delta) } } impl Pallet { + pub fn add_locking_coldkey(hotkey: &T::AccountId, netuid: NetUid, coldkey: &T::AccountId) { + LockingColdkeys::::insert((netuid, hotkey, coldkey), ()); + } + + pub fn maybe_remove_locking_coldkey( + hotkey: &T::AccountId, + netuid: NetUid, + coldkey: &T::AccountId, + ) { + LockingColdkeys::::remove((netuid, hotkey, coldkey)); + } + pub fn account_rejects_locked_alpha(coldkey: &T::AccountId) -> bool { AccountFlags::::get(coldkey) & crate::ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA != 1 } @@ -473,13 +496,13 @@ impl Pallet { hotkey: &T::AccountId, lock_state: LockState, ) { - if !lock_state.locked_mass.is_zero() - || lock_state.conviction > U64F64::saturating_from_num(0) - { - Lock::::insert((coldkey, netuid, hotkey), lock_state); - } else { + if lock_state.is_zero() { + Self::maybe_remove_locking_coldkey(hotkey, netuid, coldkey); // If there is no record previously, this is a no-op Lock::::remove((coldkey, netuid, hotkey)); + } else { + Self::add_locking_coldkey(hotkey, netuid, coldkey); + Lock::::insert((coldkey, netuid, hotkey), lock_state); } } @@ -527,11 +550,11 @@ impl Pallet { } } - fn is_subnet_owner_hotkey(netuid: NetUid, hotkey: &T::AccountId) -> bool { + pub(crate) fn is_subnet_owner_hotkey(netuid: NetUid, hotkey: &T::AccountId) -> bool { hotkey == &SubnetOwnerHotkey::::get(netuid) } - fn is_perpetual_lock(coldkey: &T::AccountId, netuid: NetUid) -> bool { + pub(crate) fn is_perpetual_lock(coldkey: &T::AccountId, netuid: NetUid) -> bool { DecayingLock::::get(coldkey, netuid) == Some(false) } @@ -543,7 +566,7 @@ impl Pallet { } } - fn read_conviction_model_for_hotkey( + pub(crate) fn read_conviction_model_for_hotkey( coldkey: &T::AccountId, netuid: NetUid, hotkey: &T::AccountId, @@ -573,7 +596,7 @@ impl Pallet { }) } - fn save_conviction_model( + pub(crate) fn save_conviction_model( coldkey: &T::AccountId, netuid: NetUid, hotkey: &T::AccountId, @@ -609,7 +632,7 @@ impl Pallet { let current_enabled = Self::is_perpetual_lock(coldkey, netuid); if let Some((hotkey, mut model)) = Self::read_conviction_model(coldkey, netuid, now) { - model.roll_forward_individual(now, UnlockRate::::get(), MaturityRate::::get()); + model.roll_forward(now, UnlockRate::::get(), MaturityRate::::get()); let rolled = model.individual_lock().clone(); Self::save_conviction_model(coldkey, netuid, &hotkey, model); @@ -658,11 +681,7 @@ impl Pallet { let now = Self::get_current_block_as_u64(); Self::read_conviction_model(coldkey, netuid, now) .map(|(_hotkey, mut model)| { - model.roll_forward_individual( - now, - UnlockRate::::get(), - MaturityRate::::get(), - ); + model.roll_forward(now, UnlockRate::::get(), MaturityRate::::get()); model.individual_lock().locked_mass }) .unwrap_or(AlphaBalance::ZERO) @@ -673,11 +692,7 @@ impl Pallet { let now = Self::get_current_block_as_u64(); Self::read_conviction_model(coldkey, netuid, now) .map(|(_hotkey, mut model)| { - model.roll_forward_individual( - now, - UnlockRate::::get(), - MaturityRate::::get(), - ); + model.roll_forward(now, UnlockRate::::get(), MaturityRate::::get()); model.individual_lock().conviction }) .unwrap_or_else(|| U64F64::saturating_from_num(0)) @@ -687,20 +702,29 @@ impl Pallet { pub fn get_coldkey_lock(coldkey: &T::AccountId, netuid: NetUid) -> Option { let now = Self::get_current_block_as_u64(); Self::read_conviction_model(coldkey, netuid, now).map(|(_hotkey, mut model)| { - model.roll_forward_individual(now, UnlockRate::::get(), MaturityRate::::get()); + model.roll_forward(now, UnlockRate::::get(), MaturityRate::::get()); model.individual_lock().clone() }) } - /// Returns the alpha amount available to unstake for a coldkey on a subnet. - pub fn available_to_unstake(coldkey: &T::AccountId, netuid: NetUid) -> AlphaBalance { + /// (total_stake, locked_mass, available_to_unstake) for a coldkey on one subnet. + /// + /// The lock is subnet-wide: it blocks unstaking from any hotkey on that subnet, + /// not from a single hotkey position. + pub fn stake_availability( + coldkey: &T::AccountId, + netuid: NetUid, + ) -> (AlphaBalance, AlphaBalance, AlphaBalance) { let total = Self::total_coldkey_alpha_on_subnet(coldkey, netuid); let locked = Self::get_current_locked(coldkey, netuid); - if total > locked { - total.saturating_sub(locked) - } else { - AlphaBalance::ZERO - } + let available = total.saturating_sub(locked); + (total, locked, available) + } + + /// Alpha the coldkey can still unstake on this subnet right now. + pub fn available_to_unstake(coldkey: &T::AccountId, netuid: NetUid) -> AlphaBalance { + let (_, _, available) = Self::stake_availability(coldkey, netuid); + available } /// Ensures that the amount can be unstaked @@ -739,7 +763,7 @@ impl Pallet { } None => Self::read_conviction_model_for_hotkey(coldkey, netuid, hotkey, now), }; - model.roll_forward_individual(now, UnlockRate::::get(), MaturityRate::::get()); + model.roll_forward(now, UnlockRate::::get(), MaturityRate::::get()); if model.individual_lock().locked_mass.is_zero() && model.individual_lock().conviction == U64F64::saturating_from_num(0) @@ -795,7 +819,7 @@ impl Pallet { pub fn force_reduce_lock(coldkey: &T::AccountId, netuid: NetUid, amount: AlphaBalance) { let now = Self::get_current_block_as_u64(); if let Some((hotkey, mut model)) = Self::read_conviction_model(coldkey, netuid, now) { - model.roll_forward_individual(now, UnlockRate::::get(), MaturityRate::::get()); + model.roll_forward(now, UnlockRate::::get(), MaturityRate::::get()); model.roll_forward_aggregate(now, UnlockRate::::get(), MaturityRate::::get()); model.force_reduce_individual(amount, now); Self::save_conviction_model(coldkey, netuid, &hotkey, model); @@ -809,18 +833,8 @@ impl Pallet { // Cleanup locks for the specific coldkey and hotkey if let Some((hotkey, mut model)) = Self::read_conviction_model(coldkey, netuid, now) { - model.roll_forward_individual(now, UnlockRate::::get(), MaturityRate::::get()); - let rolled = model.individual_lock().clone(); - if rolled.locked_mass.is_zero() { - model.set_individual_lock(LockState { - locked_mass: AlphaBalance::ZERO, - conviction: U64F64::saturating_from_num(0), - last_update: now, - }); - model.roll_forward_aggregate(now, UnlockRate::::get(), MaturityRate::::get()); - model.reduce_aggregate(rolled.locked_mass, rolled.conviction); - Self::save_conviction_model(coldkey, netuid, &hotkey, model); - } + model.roll_forward(now, UnlockRate::::get(), MaturityRate::::get()); + Self::save_conviction_model(coldkey, netuid, &hotkey, model); } } @@ -902,6 +916,7 @@ impl Pallet { false, true, ) + .0 .conviction }) .unwrap_or_else(|| U64F64::saturating_from_num(0)); @@ -915,6 +930,7 @@ impl Pallet { false, false, ) + .0 .conviction }) .unwrap_or_else(|| U64F64::saturating_from_num(0)); @@ -930,6 +946,7 @@ impl Pallet { true, true, ) + .0 .conviction }) .unwrap_or_else(|| U64F64::saturating_from_num(0)); @@ -943,6 +960,7 @@ impl Pallet { true, false, ) + .0 .conviction }) .unwrap_or_else(|| U64F64::saturating_from_num(0)); @@ -969,6 +987,7 @@ impl Pallet { false, true, ) + .0 .conviction }) .fold(U64F64::saturating_from_num(0), |acc, conviction| { @@ -984,6 +1003,7 @@ impl Pallet { false, false, ) + .0 .conviction }) .fold(U64F64::saturating_from_num(0), |acc, conviction| { @@ -999,6 +1019,7 @@ impl Pallet { true, true, ) + .0 .conviction }) .unwrap_or_else(|| U64F64::saturating_from_num(0)); @@ -1012,6 +1033,7 @@ impl Pallet { true, false, ) + .0 .conviction }) .unwrap_or_else(|| U64F64::saturating_from_num(0)); @@ -1041,7 +1063,7 @@ impl Pallet { let entry = scores .entry(hotkey) .or_insert_with(|| U64F64::saturating_from_num(0)); - *entry = entry.saturating_add(rolled.conviction); + *entry = entry.saturating_add(rolled.0.conviction); }); DecayingHotkeyLock::::iter_prefix(netuid).for_each(|(hotkey, lock)| { let rolled = ConvictionModel::roll_forward_lock( @@ -1055,7 +1077,7 @@ impl Pallet { let entry = scores .entry(hotkey) .or_insert_with(|| U64F64::saturating_from_num(0)); - *entry = entry.saturating_add(rolled.conviction); + *entry = entry.saturating_add(rolled.0.conviction); }); if let Some(lock) = OwnerLock::::get(netuid) { let owner_hotkey = SubnetOwnerHotkey::::get(netuid); @@ -1070,7 +1092,7 @@ impl Pallet { let entry = scores .entry(owner_hotkey) .or_insert_with(|| U64F64::saturating_from_num(0)); - *entry = entry.saturating_add(rolled.conviction); + *entry = entry.saturating_add(rolled.0.conviction); } if let Some(lock) = DecayingOwnerLock::::get(netuid) { let owner_hotkey = SubnetOwnerHotkey::::get(netuid); @@ -1085,7 +1107,7 @@ impl Pallet { let entry = scores .entry(owner_hotkey) .or_insert_with(|| U64F64::saturating_from_num(0)); - *entry = entry.saturating_add(rolled.conviction); + *entry = entry.saturating_add(rolled.0.conviction); } scores @@ -1172,6 +1194,7 @@ impl Pallet { false, true, ) + .0 }) .unwrap_or_else(|| Self::empty_lock(now)); Self::insert_hotkey_lock_state( @@ -1180,10 +1203,10 @@ impl Pallet { LockState { locked_mass: current .locked_mass - .saturating_add(moved_owner_lock.locked_mass), + .saturating_add(moved_owner_lock.0.locked_mass), conviction: current .conviction - .saturating_add(moved_owner_lock.conviction), + .saturating_add(moved_owner_lock.0.conviction), last_update: now, }, ); @@ -1207,6 +1230,7 @@ impl Pallet { false, false, ) + .0 }) .unwrap_or_else(|| Self::empty_lock(now)); Self::insert_decaying_hotkey_lock_state( @@ -1215,10 +1239,10 @@ impl Pallet { LockState { locked_mass: current .locked_mass - .saturating_add(moved_owner_lock.locked_mass), + .saturating_add(moved_owner_lock.0.locked_mass), conviction: current .conviction - .saturating_add(moved_owner_lock.conviction), + .saturating_add(moved_owner_lock.0.conviction), last_update: now, }, ); @@ -1242,6 +1266,7 @@ impl Pallet { true, true, ) + .0 }) .unwrap_or_else(|| Self::empty_lock(now)); Self::insert_owner_lock_state( @@ -1250,10 +1275,10 @@ impl Pallet { LockState { locked_mass: current .locked_mass - .saturating_add(moved_king_lock.locked_mass), + .saturating_add(moved_king_lock.0.locked_mass), conviction: current .conviction - .saturating_add(moved_king_lock.conviction), + .saturating_add(moved_king_lock.0.conviction), last_update: now, }, now, @@ -1261,7 +1286,8 @@ impl Pallet { maturity_rate, true, true, - ), + ) + .0, ); } if let Some(king_lock) = DecayingHotkeyLock::::take(netuid, &king_hotkey) { @@ -1283,6 +1309,7 @@ impl Pallet { true, false, ) + .0 }) .unwrap_or_else(|| Self::empty_lock(now)); Self::insert_decaying_owner_lock_state( @@ -1291,10 +1318,10 @@ impl Pallet { LockState { locked_mass: current .locked_mass - .saturating_add(moved_king_lock.locked_mass), + .saturating_add(moved_king_lock.0.locked_mass), conviction: current .conviction - .saturating_add(moved_king_lock.conviction), + .saturating_add(moved_king_lock.0.conviction), last_update: now, }, now, @@ -1302,7 +1329,8 @@ impl Pallet { maturity_rate, true, false, - ), + ) + .0, ); } @@ -1331,7 +1359,7 @@ impl Pallet { Self::is_subnet_owner_hotkey(netuid, &hotkey), Self::is_perpetual_lock(coldkey, netuid), ); - if rolled.locked_mass > AlphaBalance::ZERO { + if rolled.0.locked_mass > AlphaBalance::ZERO { return Err(Error::::ActiveLockExists); } } @@ -1371,7 +1399,7 @@ impl Pallet { let perpetual_lock = decaying_locks_to_transfer .iter() .any(|(decaying_netuid, decaying)| *decaying_netuid == netuid && !*decaying); - let old_lock = ConvictionModel::roll_forward_lock( + let (old_lock, _) = ConvictionModel::roll_forward_lock( lock, now, unlock_rate, @@ -1390,6 +1418,7 @@ impl Pallet { // perpetual-lock flags; aggregate selection depends on the old flag. for (netuid, hotkey, old_lock, _) in rolled_locks_to_transfer.iter() { Lock::::remove((old_coldkey.clone(), *netuid, hotkey.clone())); + Self::maybe_remove_locking_coldkey(hotkey, *netuid, old_coldkey); Self::reduce_aggregate_lock( old_coldkey, hotkey, @@ -1415,7 +1444,8 @@ impl Pallet { maturity_rate, Self::is_subnet_owner_hotkey(netuid, &hotkey), perpetual_lock, - ); + ) + .0; Self::insert_lock_state(new_coldkey, netuid, &hotkey, new_lock.clone()); Self::add_aggregate_lock(new_coldkey, &hotkey, netuid, new_lock); } @@ -1482,14 +1512,16 @@ impl Pallet { reads = reads.saturating_add(5); } - if !netuids_to_transfer.is_empty() { - for ((coldkey, netuid, hotkey), lock) in Lock::::iter() { - if hotkey == *old_hotkey - && netuids_to_transfer - .iter() - .any(|(rebuild_netuid, _, _)| *rebuild_netuid == netuid) - { - locks_to_transfer.push((coldkey, netuid, lock)); + // Build a concrete transfer list from the hotkey-to-coldkey index. + // The index can contain stale coldkeys, so only locks that still exist + // are carried forward; missing locks are pruned from the index. + for (netuid, _, _) in &netuids_to_transfer { + for (coldkey, _) in LockingColdkeys::::iter_prefix((*netuid, old_hotkey)) { + if let Some(lock) = Lock::::get((coldkey.clone(), *netuid, old_hotkey.clone())) { + locks_to_transfer.push((coldkey, *netuid, lock)); + } else { + Self::maybe_remove_locking_coldkey(old_hotkey, *netuid, &coldkey); + writes = writes.saturating_add(1); } reads = reads.saturating_add(1); } @@ -1513,7 +1545,8 @@ impl Pallet { maturity_rate, old_owner_lock, perpetual_lock, - ); + ) + .0; let moved = ConvictionModel::roll_forward_lock( rolled, now, @@ -1521,8 +1554,10 @@ impl Pallet { maturity_rate, new_owner_lock, perpetual_lock, - ); + ) + .0; Lock::::remove((coldkey.clone(), netuid, old_hotkey.clone())); + Self::maybe_remove_locking_coldkey(old_hotkey, netuid, &coldkey); Self::insert_lock_state(&coldkey, netuid, new_hotkey, moved); writes = writes.saturating_add(2); } @@ -1541,6 +1576,7 @@ impl Pallet { true, true, ) + .0 }) } else { HotkeyLock::::take(netuid, old_hotkey).map(|lock| { @@ -1552,6 +1588,7 @@ impl Pallet { false, true, ) + .0 }) }; let moved_decaying_lock = if old_was_owner { @@ -1564,6 +1601,7 @@ impl Pallet { true, false, ) + .0 }) } else { DecayingHotkeyLock::::take(netuid, old_hotkey).map(|lock| { @@ -1575,6 +1613,7 @@ impl Pallet { false, false, ) + .0 }) }; @@ -1589,7 +1628,8 @@ impl Pallet { maturity_rate, true, true, - ), + ) + .0, ); } else { Self::insert_hotkey_lock_state( @@ -1602,7 +1642,8 @@ impl Pallet { maturity_rate, false, true, - ), + ) + .0, ); } } @@ -1617,7 +1658,8 @@ impl Pallet { maturity_rate, true, false, - ), + ) + .0, ); } else { Self::insert_decaying_hotkey_lock_state( @@ -1630,7 +1672,8 @@ impl Pallet { maturity_rate, false, false, - ), + ) + .0, ); } } @@ -1662,7 +1705,7 @@ impl Pallet { Some((origin_hotkey, mut model)) => { let unlock_rate = UnlockRate::::get(); let maturity_rate = MaturityRate::::get(); - model.roll_forward_individual(now, unlock_rate, maturity_rate); + model.roll_forward(now, unlock_rate, maturity_rate); let mut lock = model.individual_lock().clone(); let removed = lock.clone(); @@ -1678,9 +1721,11 @@ impl Pallet { maturity_rate, Self::is_subnet_owner_hotkey(netuid, destination_hotkey), Self::is_perpetual_lock(coldkey, netuid), - ); + ) + .0; Lock::::remove((coldkey.clone(), netuid, origin_hotkey.clone())); + Self::maybe_remove_locking_coldkey(&origin_hotkey, netuid, coldkey); Self::insert_lock_state(coldkey, netuid, destination_hotkey, lock.clone()); Self::reduce_aggregate_lock( coldkey, @@ -1763,11 +1808,11 @@ impl Pallet { let unlock_rate = UnlockRate::::get(); let maturity_rate = MaturityRate::::get(); - source_model.roll_forward_individual(now, unlock_rate, maturity_rate); + source_model.roll_forward(now, unlock_rate, maturity_rate); let mut source_lock = source_model.individual_lock().clone(); let maybe_destination_lock = Self::read_conviction_model(destination_coldkey, netuid, now) .map(|(hotkey, mut model)| { - model.roll_forward_individual(now, unlock_rate, maturity_rate); + model.roll_forward(now, unlock_rate, maturity_rate); (hotkey, model.individual_lock().clone()) }); @@ -1839,7 +1884,8 @@ impl Pallet { maturity_rate, Self::is_subnet_owner_hotkey(netuid, &source_hotkey), Self::is_perpetual_lock(origin_coldkey, netuid), - ); + ) + .0; destination_lock = ConvictionModel::roll_forward_lock( destination_lock, now, @@ -1847,7 +1893,8 @@ impl Pallet { maturity_rate, Self::is_subnet_owner_hotkey(netuid, &destination_hotkey), Self::is_perpetual_lock(destination_coldkey, netuid), - ); + ) + .0; // Upsert updated locks (only once per this fn) even if there were no updates because // of roll-forward @@ -1883,43 +1930,23 @@ impl Pallet { /// Destroys all lock maps for network dissolution pub fn destroy_lock_maps(netuid: NetUid) { + // LockingColdkeys: (netuid, hotkey, coldkey) // Lock: (coldkey, netuid, hotkey) { - let to_rm: sp_std::vec::Vec<(T::AccountId, T::AccountId)> = Lock::::iter() - .filter_map( - |((cold, n, hot), _)| { - if n == netuid { Some((cold, hot)) } else { None } - }, - ) - .collect(); + let to_rm: sp_std::vec::Vec<((T::AccountId, T::AccountId), ())> = + LockingColdkeys::::iter_prefix((netuid,)).collect(); - for (cold, hot) in to_rm { + for ((hot, cold), _) in to_rm { Lock::::remove((cold, netuid, hot)); } + let _ = LockingColdkeys::::clear_prefix((netuid,), u32::MAX, None); } // HotkeyLock: (netuid, hotkey) → LockState - { - let to_rm: sp_std::vec::Vec = HotkeyLock::::iter_prefix(netuid) - .map(|(hot, _)| hot) - .collect(); - - for hot in to_rm { - HotkeyLock::::remove(netuid, hot); - } - } + let _ = HotkeyLock::::clear_prefix(netuid, u32::MAX, None); // DecayingHotkeyLock: (netuid, hotkey) - { - let to_rm: sp_std::vec::Vec = - DecayingHotkeyLock::::iter_prefix(netuid) - .map(|(hot, _)| hot) - .collect(); - - for hot in to_rm { - DecayingHotkeyLock::::remove(netuid, hot); - } - } + let _ = DecayingHotkeyLock::::clear_prefix(netuid, u32::MAX, None); // OwnerLock / DecayingOwnerLock: (netuid) OwnerLock::::remove(netuid); diff --git a/pallets/subtensor/src/staking/mod.rs b/pallets/subtensor/src/staking/mod.rs index a10908eca3..cf8ac006ac 100644 --- a/pallets/subtensor/src/staking/mod.rs +++ b/pallets/subtensor/src/staking/mod.rs @@ -7,6 +7,7 @@ pub mod helpers; pub mod increase_take; pub mod lock; pub mod move_stake; +pub mod order_swap; pub mod recycle_alpha; pub mod remove_stake; pub mod set_children; diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index aafefa28ed..b189c90968 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -50,7 +50,6 @@ impl Pallet { None, None, false, - true, )?; // Log the event. @@ -141,7 +140,6 @@ impl Pallet { None, None, true, - false, )?; // 9. Emit an event for logging/monitoring. @@ -206,7 +204,6 @@ impl Pallet { None, None, false, - true, )?; // Emit an event for logging. @@ -274,7 +271,6 @@ impl Pallet { Some(limit_price), Some(allow_partial), false, - true, )?; // Emit an event for logging. @@ -306,7 +302,6 @@ impl Pallet { maybe_limit_price: Option, maybe_allow_partial: Option, check_transfer_toggle: bool, - set_limit: bool, ) -> Result { // Cap the alpha_amount at available Alpha because user might be paying transaxtion fees // in Alpha and their total is already reduced by now. @@ -385,7 +380,6 @@ impl Pallet { destination_netuid, tao_unstaked, T::SwapInterface::max_price(), - set_limit, drop_fee_destination, )?; } @@ -424,8 +418,9 @@ impl Pallet { /// /// In the corner case when SubnetTAO(2) == SubnetTAO(1), no slippage is going to occur. /// - /// TODO: This formula only works for a single swap step, so it is not 100% correct for swap v3. We need an updated one. - /// + /// TODO: This formula only works for a single swap step, so it is not 100% correct for swap v3 or + /// highly assymetric balancers. + /// We need an updated one. pub fn get_max_amount_move( origin_netuid: NetUid, destination_netuid: NetUid, @@ -440,7 +435,7 @@ impl Pallet { && (destination_netuid.is_root() || SubnetMechanism::::get(destination_netuid) == 0) { if limit_price > tao.saturating_to_num::().into() { - return Err(Error::::ZeroMaxStakeAmount.into()); + return Ok(AlphaBalance::ZERO); } else { return Ok(AlphaBalance::MAX); } @@ -477,23 +472,19 @@ impl Pallet { } // Corner case: SubnetTAO for any of two subnets is zero - let subnet_tao_1 = Self::get_subnet_tao(origin_netuid) - .saturating_add(SubnetTaoProvided::::get(origin_netuid)); - let subnet_tao_2 = Self::get_subnet_tao(destination_netuid) - .saturating_add(SubnetTaoProvided::::get(destination_netuid)); + let subnet_tao_1 = SubnetTAO::::get(origin_netuid); + let subnet_tao_2 = SubnetTAO::::get(destination_netuid); if subnet_tao_1.is_zero() || subnet_tao_2.is_zero() { - return Err(Error::::ZeroMaxStakeAmount.into()); + return Ok(AlphaBalance::ZERO); } let subnet_tao_1_float: U64F64 = U64F64::saturating_from_num(subnet_tao_1); let subnet_tao_2_float: U64F64 = U64F64::saturating_from_num(subnet_tao_2); // Corner case: SubnetAlphaIn for any of two subnets is zero - let alpha_in_1 = SubnetAlphaIn::::get(origin_netuid) - .saturating_add(SubnetAlphaInProvided::::get(origin_netuid)); - let alpha_in_2 = SubnetAlphaIn::::get(destination_netuid) - .saturating_add(SubnetAlphaInProvided::::get(destination_netuid)); + let alpha_in_1 = SubnetAlphaIn::::get(origin_netuid); + let alpha_in_2 = SubnetAlphaIn::::get(destination_netuid); if alpha_in_1.is_zero() || alpha_in_2.is_zero() { - return Err(Error::::ZeroMaxStakeAmount.into()); + return Ok(AlphaBalance::ZERO); } let alpha_in_1_float: U64F64 = U64F64::saturating_from_num(alpha_in_1); let alpha_in_2_float: U64F64 = U64F64::saturating_from_num(alpha_in_2); @@ -509,7 +500,7 @@ impl Pallet { T::SwapInterface::current_alpha_price(destination_netuid.into()), ); if limit_price_float > current_price { - return Err(Error::::ZeroMaxStakeAmount.into()); + return Ok(AlphaBalance::ZERO); } // Corner case: limit_price is zero @@ -532,10 +523,6 @@ impl Pallet { .saturating_sub(alpha_in_1_float.saturating_mul(t2_over_sum)) .saturating_to_num::(); - if final_result != 0 { - Ok(final_result.into()) - } else { - Err(Error::::ZeroMaxStakeAmount.into()) - } + Ok(final_result.into()) } } diff --git a/pallets/subtensor/src/staking/order_swap.rs b/pallets/subtensor/src/staking/order_swap.rs new file mode 100644 index 0000000000..fd0ae3461c --- /dev/null +++ b/pallets/subtensor/src/staking/order_swap.rs @@ -0,0 +1,191 @@ +use super::*; +use frame_support::traits::fungible::Mutate; +use frame_support::traits::tokens::Preservation; +use frame_support::transactional; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance}; +use subtensor_swap_interface::{Order, OrderSwapInterface, SwapHandler, SwapResult}; + +impl OrderSwapInterface for Pallet { + #[transactional] + fn buy_alpha( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + netuid: NetUid, + tao_amount: TaoBalance, + limit_price: TaoBalance, + validate: bool, + ) -> Result { + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + Self::ensure_subtoken_enabled(netuid)?; + if validate { + ensure!( + Self::hotkey_account_exists(hotkey), + Error::::HotKeyAccountNotExists + ); + ensure!( + tao_amount >= DefaultMinStake::::get(), + Error::::AmountTooLow + ); + ensure!( + Self::can_remove_balance_from_coldkey_account(coldkey, tao_amount), + Error::::NotEnoughBalanceToStake + ); + } + // `limit_price` is already in ×10⁹ scale (same as the `current_alpha_price` RPC + // endpoint), which is also the scale the AMM uses for its price_limit argument. + // Pass it directly without any scaling. u64::MAX means "no ceiling". + let amm_limit = limit_price; + // Stable subnets (mechanism_id != 1) are always 1:1 and never stop early. + if SubnetMechanism::::get(netuid) == 1 { + let sim_order = GetAlphaForTao::::with_amount(tao_amount); + let sim: SwapResult = + T::SwapInterface::swap(netuid.into(), sim_order, amm_limit, false, true)?; + ensure!( + sim.amount_paid_in.saturating_add(sim.fee_paid) >= tao_amount, + Error::::SlippageTooHigh + ); + } + let alpha_out = + Self::stake_into_subnet(hotkey, coldkey, netuid, tao_amount, amm_limit, false)?; + Ok(alpha_out) + } + + #[transactional] + fn sell_alpha( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + netuid: NetUid, + alpha_amount: AlphaBalance, + limit_price: TaoBalance, + validate: bool, + ) -> Result { + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + Self::ensure_subtoken_enabled(netuid)?; + if validate { + Self::validate_remove_stake( + coldkey, + hotkey, + netuid, + alpha_amount, + alpha_amount, + false, + )?; + } + // `limit_price` is already in ×10⁹ scale (same as the `current_alpha_price` RPC + // endpoint), which is also the scale the AMM uses for its price_limit argument. + // Pass it directly without any scaling. 0 means "no floor". + let amm_limit = limit_price; + // Stable subnets (mechanism_id != 1) are always 1:1 and never stop early. + if SubnetMechanism::::get(netuid) == 1 { + let sim_order = GetTaoForAlpha::::with_amount(alpha_amount); + let sim: SwapResult = + T::SwapInterface::swap(netuid.into(), sim_order, amm_limit, false, true)?; + ensure!( + sim.amount_paid_in.saturating_add(sim.fee_paid) >= alpha_amount, + Error::::SlippageTooHigh + ); + } + let tao_out = Self::unstake_from_subnet( + hotkey, + coldkey, + coldkey, + netuid, + alpha_amount, + amm_limit, + false, + )?; + Ok(tao_out) + } + + fn current_alpha_price(netuid: NetUid) -> U64F64 { + T::SwapInterface::current_alpha_price(netuid) + } + + fn transfer_tao(from: &T::AccountId, to: &T::AccountId, amount: TaoBalance) -> DispatchResult { + ::Currency::transfer(from, to, amount, Preservation::Expendable)?; + Ok(()) + } + + #[transactional] + fn transfer_staked_alpha( + from_coldkey: &T::AccountId, + from_hotkey: &T::AccountId, + to_coldkey: &T::AccountId, + to_hotkey: &T::AccountId, + netuid: NetUid, + amount: AlphaBalance, + validate_sender: bool, + validate_receiver: bool, + ) -> DispatchResult { + ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + Self::ensure_subtoken_enabled(netuid)?; + if validate_sender { + ensure!( + Self::hotkey_account_exists(from_hotkey), + Error::::HotKeyAccountNotExists + ); + ensure!(!amount.is_zero(), Error::::AmountTooLow); + let tao_equiv = T::SwapInterface::current_alpha_price(netuid) + .saturating_mul(U64F64::saturating_from_num(amount.to_u64())) + .saturating_to_num::(); + ensure!( + TaoBalance::from(tao_equiv) >= DefaultMinStake::::get(), + Error::::AmountTooLow + ); + Self::ensure_available_to_unstake(from_coldkey, netuid, amount)?; + } + + if validate_receiver { + ensure!( + Self::hotkey_account_exists(to_hotkey), + Error::::HotKeyAccountNotExists + ); + } + + let available = + Self::get_stake_for_hotkey_and_coldkey_on_subnet(from_hotkey, from_coldkey, netuid); + ensure!(available >= amount, Error::::NotEnoughStakeToWithdraw); + Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( + from_hotkey, + from_coldkey, + netuid, + amount, + ); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + to_hotkey, to_coldkey, netuid, amount, + ); + LastColdkeyHotkeyStakeBlock::::insert( + to_coldkey, + to_hotkey, + Self::get_current_block_as_u64(), + ); + Ok(()) + } + + fn register_pallet_hotkey(coldkey: &T::AccountId, hotkey: &T::AccountId) -> DispatchResult { + Self::create_account_if_non_existent(coldkey, hotkey) + } + + fn pallet_hotkey_registered(coldkey: &T::AccountId, hotkey: &T::AccountId) -> bool { + Self::coldkey_owns_hotkey(coldkey, hotkey) + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_up_netuid_for_benchmark(netuid: NetUid) { + if !Self::if_subnet_exist(netuid) { + Self::init_new_network(netuid, 100); + } + SubtokenEnabled::::insert(netuid, true); + // Seed pool reserves so the AMM price is well-defined and swaps return non-zero. + SubnetTAO::::insert(netuid, TaoBalance::from(1_000_000_000_000_u64)); + SubnetAlphaIn::::insert(netuid, AlphaBalance::from(1_000_000_000_000_u64)); + } + + #[cfg(feature = "runtime-benchmarks")] + fn set_up_acc_for_benchmark(hotkey: &T::AccountId, coldkey: &T::AccountId) { + let _ = Self::create_account_if_non_existent(coldkey, hotkey); + let credit = Self::mint_tao(TaoBalance::from(1_000_000_000_000_u64)); + let _ = Self::spend_tao(coldkey, credit, TaoBalance::from(1_000_000_000_000_u64)); + } +} diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 03b962202c..cf640dc661 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -274,7 +274,6 @@ impl Pallet { NetUid::ROOT, total_tao_unstaked, T::SwapInterface::max_price(), - false, // no limit for Root subnet false, )?; @@ -391,7 +390,7 @@ impl Pallet { if limit_price <= 1_000_000_000.into() { return Ok(AlphaBalance::MAX); } else { - return Err(Error::::ZeroMaxStakeAmount.into()); + return Ok(AlphaBalance::ZERO); } } @@ -400,11 +399,7 @@ impl Pallet { let result = T::SwapInterface::swap(netuid.into(), order, limit_price.into(), false, true) .map(|r| r.amount_paid_in.saturating_add(r.fee_paid))?; - if !result.is_zero() { - Ok(result) - } else { - Err(Error::::ZeroMaxStakeAmount.into()) - } + Ok(result) } pub fn do_remove_stake_full_limit( @@ -455,7 +450,9 @@ impl Pallet { .saturating_to_num::(); owner_emission_tao = if owner_alpha_u64 > 0 { - let cur_price: U96F32 = T::SwapInterface::current_alpha_price(netuid.into()); + let cur_price: U96F32 = U96F32::saturating_from_num( + T::SwapInterface::current_alpha_price(netuid.into()), + ); let val_u64 = U96F32::from_num(owner_alpha_u64) .saturating_mul(cur_price) .floor() @@ -610,7 +607,6 @@ impl Pallet { } // 7.c) Remove α‑in/α‑out counters (fully destroyed). SubnetAlphaIn::::remove(netuid); - SubnetAlphaInProvided::::remove(netuid); SubnetAlphaOut::::remove(netuid); SubnetProtocolAlpha::::remove(netuid); @@ -646,21 +642,8 @@ impl Pallet { } } - // 9) Cleanup all subnet stake locks if any. - let lock_keys: Vec<(T::AccountId, NetUid, T::AccountId)> = Lock::::iter_keys() - .filter(|(_, this_netuid, _)| *this_netuid == netuid) - .collect(); - for (coldkey, netuid, hotkey) in lock_keys { - Lock::::remove((coldkey, netuid, hotkey)); - } - - // 10) Cleanup all subnet hotkey locks if any. - let hotkey_lock_keys: Vec<(NetUid, T::AccountId)> = HotkeyLock::::iter_keys() - .filter(|(this_netuid, _)| *this_netuid == netuid) - .collect(); - for (netuid, hotkey) in hotkey_lock_keys { - HotkeyLock::::remove(netuid, hotkey); - } + // 10) Cleanup all subnet stake locks and lock aggregates if any. + Self::destroy_lock_maps(netuid); Ok(()) } diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index fb2de35e95..2826590c50 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -2,7 +2,7 @@ use super::*; use safe_math::*; use share_pool::{SafeFloat, SharePool, SharePoolDataOperations}; use sp_std::{collections::btree_map::BTreeMap, ops::Neg}; -use substrate_fixed::types::{I64F64, I96F32, U96F32}; +use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32}; use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, NetUid, TaoBalance, Token}; use subtensor_swap_interface::{Order, SwapHandler, SwapResult}; @@ -21,8 +21,8 @@ impl Pallet { SubnetAlphaIn::::get(netuid).saturating_add(SubnetAlphaOut::::get(netuid)) } - pub fn get_moving_alpha_price(netuid: NetUid) -> U96F32 { - let one = U96F32::saturating_from_num(1.0); + pub fn get_moving_alpha_price(netuid: NetUid) -> U64F64 { + let one = U64F64::saturating_from_num(1.0); if netuid.is_root() { // Root. one @@ -30,12 +30,12 @@ impl Pallet { // Stable one } else { - U96F32::saturating_from_num(SubnetMovingPrice::::get(netuid)) + U64F64::saturating_from_num(SubnetMovingPrice::::get(netuid)) } } pub fn update_moving_price(netuid: NetUid) { - let blocks_since_start_call = U96F32::saturating_from_num({ + let blocks_since_start_call = U64F64::saturating_from_num({ // We expect FirstEmissionBlockNumber to be set earlier, and we take the block when // `start_call` was called (first block before FirstEmissionBlockNumber). let start_call_block = FirstEmissionBlockNumber::::get(netuid) @@ -50,19 +50,20 @@ impl Pallet { // will take in order for the distance between current EMA of price and current price to shorten // by half. let halving_time = EMAPriceHalvingBlocks::::get(netuid); - let current_ma_unsigned = U96F32::saturating_from_num(SubnetMovingAlpha::::get()); - let alpha: U96F32 = current_ma_unsigned.saturating_mul(blocks_since_start_call.safe_div( - blocks_since_start_call.saturating_add(U96F32::saturating_from_num(halving_time)), + let current_ma_unsigned = U64F64::saturating_from_num(SubnetMovingAlpha::::get()); + let alpha: U64F64 = current_ma_unsigned.saturating_mul(blocks_since_start_call.safe_div( + blocks_since_start_call.saturating_add(U64F64::saturating_from_num(halving_time)), )); // Because alpha = b / (b + h), where b and h > 0, alpha < 1, so 1 - alpha > 0. // We can use unsigned type here: U96F32 - let one_minus_alpha: U96F32 = U96F32::saturating_from_num(1.0).saturating_sub(alpha); - let current_price: U96F32 = alpha.saturating_mul( + let one_minus_alpha: U64F64 = U64F64::saturating_from_num(1.0).saturating_sub(alpha); + let current_price: U64F64 = alpha.saturating_mul(U64F64::saturating_from_num( T::SwapInterface::current_alpha_price(netuid.into()) - .min(U96F32::saturating_from_num(1.0)), - ); - let current_moving: U96F32 = - one_minus_alpha.saturating_mul(Self::get_moving_alpha_price(netuid)); + .min(U64F64::saturating_from_num(1.0)), + )); + let current_moving: U64F64 = one_minus_alpha.saturating_mul(U64F64::saturating_from_num( + Self::get_moving_alpha_price(netuid), + )); // Convert batch to signed I96F32 to avoid migration of SubnetMovingPrice for now`` let new_moving: I96F32 = I96F32::saturating_from_num(current_price.saturating_add(current_moving)); @@ -70,12 +71,12 @@ impl Pallet { } /// Gets the Median Subnet Alpha Price - pub fn get_median_subnet_alpha_price() -> U96F32 { - let default_price = U96F32::saturating_from_num(1_u64); - let zero_price = U96F32::saturating_from_num(0_u64); - let two = U96F32::saturating_from_num(2_u64); + pub fn get_median_subnet_alpha_price() -> U64F64 { + let default_price = U64F64::saturating_from_num(1_u64); + let zero_price = U64F64::saturating_from_num(0_u64); + let two = U64F64::saturating_from_num(2_u64); - let mut price_counts: BTreeMap = BTreeMap::new(); + let mut price_counts: BTreeMap = BTreeMap::new(); let mut total_prices: usize = 0; for (netuid, added) in NetworksAdded::::iter() { @@ -112,8 +113,8 @@ impl Pallet { }; let mut cumulative: usize = 0; - let mut lower_price: Option = None; - let mut upper_price: Option = None; + let mut lower_price: Option = None; + let mut upper_price: Option = None; for (price, count) in price_counts.into_iter() { let next_cumulative = cumulative.saturating_add(count); @@ -853,7 +854,6 @@ impl Pallet { netuid: NetUid, tao: TaoBalance, price_limit: TaoBalance, - set_limit: bool, drop_fees: bool, ) -> Result { // Transfer TAO from coldkey to the subnet account. @@ -919,10 +919,6 @@ impl Pallet { LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); - if set_limit { - Self::set_stake_operation_limit(hotkey, coldkey, netuid.into()); - } - // If this is a root-stake if netuid == NetUid::ROOT { // Adjust root claimed for this hotkey and coldkey. @@ -1004,7 +1000,7 @@ impl Pallet { let current_price = ::SwapInterface::current_alpha_price(netuid.into()); let tao_equivalent: TaoBalance = current_price - .saturating_mul(U96F32::saturating_from_num(alpha)) + .saturating_mul(U64F64::saturating_from_num(alpha)) .saturating_to_num::() .into(); @@ -1147,8 +1143,6 @@ impl Pallet { // Ensure that the subnet exists. ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); - Self::ensure_stake_operation_limit_not_exceeded(hotkey, coldkey, netuid.into())?; - // Ensure that the subnet is enabled. // Self::ensure_subtoken_enabled(netuid)?; @@ -1250,12 +1244,6 @@ impl Pallet { ensure!(origin_netuid != destination_netuid, Error::::SameNetuid); } - Self::ensure_stake_operation_limit_not_exceeded( - origin_hotkey, - origin_coldkey, - origin_netuid.into(), - )?; - // Ensure that both subnets exist. ensure!( Self::if_subnet_exist(origin_netuid), @@ -1376,27 +1364,6 @@ impl Pallet { }); } } - - pub fn set_stake_operation_limit( - hotkey: &T::AccountId, - coldkey: &T::AccountId, - netuid: NetUid, - ) { - StakingOperationRateLimiter::::insert((hotkey, coldkey, netuid), true); - } - - pub fn ensure_stake_operation_limit_not_exceeded( - hotkey: &T::AccountId, - coldkey: &T::AccountId, - netuid: NetUid, - ) -> Result<(), Error> { - ensure!( - !StakingOperationRateLimiter::::contains_key((hotkey, coldkey, netuid)), - Error::::StakingOperationRateLimitExceeded - ); - - Ok(()) - } } /////////////////////////////////////////// diff --git a/pallets/subtensor/src/subnets/serving.rs b/pallets/subtensor/src/subnets/serving.rs index 5416e3df5d..585fc897c0 100644 --- a/pallets/subtensor/src/subnets/serving.rs +++ b/pallets/subtensor/src/subnets/serving.rs @@ -297,9 +297,9 @@ impl Pallet { placeholder1: u8, placeholder2: u8, ) -> Result<(), Error> { - // Ensure the hotkey is registered somewhere. + // Ensure the hotkey is registered on the subnet it is serving. ensure!( - Self::is_hotkey_registered_on_any_network(hotkey_id), + Self::is_hotkey_registered_on_network(netuid, hotkey_id), Error::::HotKeyNotRegisteredInNetwork ); @@ -354,7 +354,7 @@ impl Pallet { ); ensure!( - Self::is_hotkey_registered_on_any_network(hotkey_id), + Self::is_hotkey_registered_on_network(netuid, hotkey_id), Error::::HotKeyNotRegisteredInNetwork ); diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 10e67a051a..f304c3d836 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -3,7 +3,7 @@ use frame_support::PalletId; use safe_math::FixedExt; use sp_core::Get; use sp_runtime::traits::AccountIdConversion; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{NetUid, TaoBalance}; impl Pallet { /// Returns true if the subnetwork exists. @@ -227,14 +227,12 @@ impl Pallet { pool_initial_tao }; - let total_pool_alpha: AlphaBalance = U96F32::saturating_from_num(total_pool_tao.to_u64()) + let total_pool_alpha: AlphaBalance = U64F64::saturating_from_num(total_pool_tao.to_u64()) .safe_div(median_subnet_alpha_price) .saturating_floor() .saturating_to_num::() .into(); - let owner_alpha_stake = AlphaBalance::ZERO; - // With the full lock retained in the reserve, this will normally be zero. let tao_recycled_for_registration = actual_tao_lock_amount.saturating_sub(total_pool_tao); @@ -244,9 +242,7 @@ impl Pallet { SubnetOwner::::insert(netuid_to_register, coldkey.clone()); Self::set_subnet_owner_hotkey(netuid_to_register, hotkey)?; SubnetLocked::::insert(netuid_to_register, actual_tao_lock_amount); - SubnetTaoProvided::::insert(netuid_to_register, TaoBalance::ZERO); - SubnetAlphaInProvided::::insert(netuid_to_register, AlphaBalance::ZERO); - SubnetAlphaOut::::insert(netuid_to_register, owner_alpha_stake); + SubnetAlphaOut::::insert(netuid_to_register, AlphaBalance::ZERO); SubnetVolume::::insert(netuid_to_register, 0u128); RAORecycledForRegistration::::insert(netuid_to_register, tao_recycled_for_registration); @@ -305,6 +301,12 @@ impl Pallet { // --- 3. Fill tempo memory item. Tempo::::insert(netuid, tempo); + // --- 3.1. Initialise `LastEpochBlock` with a per-netuid stagger + let now = Self::get_current_block_as_u64(); + let period = (tempo as u64).max(1); + let stagger = (u16::from(netuid) as u64).checked_rem(period).unwrap_or(0); + LastEpochBlock::::insert(netuid, now.saturating_sub(stagger)); + // --- 4. Increase total network count. TotalNetworks::::mutate(|n| *n = n.saturating_add(1)); diff --git a/pallets/subtensor/src/subnets/weights.rs b/pallets/subtensor/src/subnets/weights.rs index 39bcfb80b5..7a8dc50a60 100644 --- a/pallets/subtensor/src/subnets/weights.rs +++ b/pallets/subtensor/src/subnets/weights.rs @@ -96,17 +96,21 @@ impl Pallet { Error::::CommittingWeightsTooFast ); - // 5. Calculate the reveal blocks based on network tempo and reveal period. - let (first_reveal_block, last_reveal_block) = Self::get_reveal_blocks(netuid, commit_block); + // 5. Resolve the epoch this commit belongs to under the stateful counter. + let commit_epoch = Self::current_epoch_with_lookahead(netuid); // 6. Retrieve or initialize the VecDeque of commits for the hotkey. WeightCommits::::try_mutate(netuid_index, &who, |maybe_commits| -> DispatchResult { + // Tuple shape `(hash, commit_epoch, commit_block, _)`. `commit_epoch` + // drives reveal-window timing; `commit_block` is kept for the epoch's + // commit-reveal weight column-mask. The 4th field is a legacy + // reveal-block bound, now unused and left at 0. let mut commits: VecDeque<(H256, u64, u64, u64)> = maybe_commits.take().unwrap_or_default(); // 7. Remove any expired commits from the front of the queue. - while let Some((_, commit_block_existing, _, _)) = commits.front() { - if Self::is_commit_expired(netuid, *commit_block_existing) { + while let Some((_, commit_epoch_existing, _, _)) = commits.front() { + if Self::is_commit_expired(netuid, *commit_epoch_existing) { commits.pop_front(); } else { break; @@ -116,13 +120,8 @@ impl Pallet { // 8. Verify that the number of unrevealed commits is within the allowed limit. ensure!(commits.len() < 10, Error::::TooManyUnrevealedCommits); - // 9. Append the new commit with calculated reveal blocks. - commits.push_back(( - commit_hash, - commit_block, - first_reveal_block, - last_reveal_block, - )); + // 9. Append the new commit, tagged with its epoch and block. + commits.push_back((commit_hash, commit_epoch, commit_block, 0)); // 10. Store the updated commits queue back to storage. *maybe_commits = Some(commits); @@ -342,10 +341,8 @@ impl Pallet { // 6. Retrieve or initialize the VecDeque of commits for the hotkey. let cur_block = Self::get_current_block_as_u64(); - let cur_epoch = match Self::should_run_epoch(netuid, commit_block) { - true => Self::get_epoch_index(netuid, cur_block).saturating_add(1), - false => Self::get_epoch_index(netuid, cur_block), - }; + // Key the commit by the epoch it belongs to under the stateful counter. + let cur_epoch = Self::current_epoch_with_lookahead(netuid); TimelockedWeightCommits::::try_mutate( netuid_index, @@ -1249,49 +1246,49 @@ impl Pallet { uids.len() <= subnetwork_n as usize } - pub fn is_reveal_block_range(netuid: NetUid, commit_block: u64) -> bool { - let current_block: u64 = Self::get_current_block_as_u64(); - let commit_epoch: u64 = Self::get_epoch_index(netuid, commit_block); - let current_epoch: u64 = Self::get_epoch_index(netuid, current_block); + /// True when the current epoch is exactly `commit_epoch + reveal_period`. + /// + /// `commit_epoch` is the `SubnetEpochIndex` value stored with the commit (CR-v2 + /// `WeightCommits` tuple field 1). The current epoch uses the look-ahead value + /// so a reveal submitted on a fire-block is judged against the about-to-fire + /// epoch, consistent with how the commit was tagged. + pub fn is_reveal_block_range(netuid: NetUid, commit_epoch: u64) -> bool { + let current_epoch: u64 = Self::current_epoch_with_lookahead(netuid); let reveal_period: u64 = Self::get_reveal_period(netuid); - // Reveal is allowed only in the exact epoch `commit_epoch + reveal_period` current_epoch == commit_epoch.saturating_add(reveal_period) } - pub fn get_epoch_index(netuid: NetUid, block_number: u64) -> u64 { - let tempo: u64 = Self::get_tempo(netuid) as u64; - let tempo_plus_one: u64 = tempo.saturating_add(1); - let netuid_plus_one: u64 = (u16::from(netuid) as u64).saturating_add(1); - let block_with_offset: u64 = block_number.saturating_add(netuid_plus_one); - - block_with_offset.checked_div(tempo_plus_one).unwrap_or(0) + /// Canonical epoch index for a subnet — the monotonic `SubnetEpochIndex` counter. + pub fn get_epoch_index(netuid: NetUid, _block_number: u64) -> u64 { + SubnetEpochIndex::::get(netuid) } - pub fn is_commit_expired(netuid: NetUid, commit_block: u64) -> bool { - let current_block: u64 = Self::get_current_block_as_u64(); - let current_epoch: u64 = Self::get_epoch_index(netuid, current_block); - let commit_epoch: u64 = Self::get_epoch_index(netuid, commit_block); - let reveal_period: u64 = Self::get_reveal_period(netuid); - - current_epoch > commit_epoch.saturating_add(reveal_period) + /// Epoch index that a commit or reveal happening at the *current* block + /// belongs to: the `SubnetEpochIndex` counter, plus one if an epoch slot is + /// due to fire this block. + /// + /// The look-ahead is needed because `block_step` runs in `on_initialize`: + /// `reveal_crv3_commits` (which must see the about-to-fire epoch) runs before + /// `run_coinbase` increments the counter, and a commit extrinsic submitted on + /// a deferred fire-block belongs to the next epoch, not the current one. + pub fn current_epoch_with_lookahead(netuid: NetUid) -> u64 { + let block = Self::get_current_block_as_u64(); + let base = SubnetEpochIndex::::get(netuid); + if Self::should_run_epoch(netuid, block) { + base.saturating_add(1) + } else { + base + } } - pub fn get_reveal_blocks(netuid: NetUid, commit_block: u64) -> (u64, u64) { + /// True once the current epoch has moved past the commit's reveal epoch + /// (`commit_epoch + reveal_period`). `commit_epoch` is the stored counter value. + pub fn is_commit_expired(netuid: NetUid, commit_epoch: u64) -> bool { + let current_epoch: u64 = Self::current_epoch_with_lookahead(netuid); let reveal_period: u64 = Self::get_reveal_period(netuid); - let tempo: u64 = Self::get_tempo(netuid) as u64; - let tempo_plus_one: u64 = tempo.saturating_add(1); - let netuid_plus_one: u64 = (u16::from(netuid) as u64).saturating_add(1); - let commit_epoch: u64 = Self::get_epoch_index(netuid, commit_block); - let reveal_epoch: u64 = commit_epoch.saturating_add(reveal_period); - - let first_reveal_block = reveal_epoch - .saturating_mul(tempo_plus_one) - .saturating_sub(netuid_plus_one); - let last_reveal_block = first_reveal_block.saturating_add(tempo); - - (first_reveal_block, last_reveal_block) + current_epoch > commit_epoch.saturating_add(reveal_period) } pub fn set_reveal_period(netuid: NetUid, reveal_period: u64) -> DispatchResult { @@ -1314,6 +1311,11 @@ impl Pallet { RevealPeriodEpochs::::get(netuid) } + /// Legacy modulo first-block-of-epoch: `epoch * (tempo + 1) - (netuid + 1)`. + /// + /// NOT used by live commit-reveal logic — that keys off the stateful + /// `SubnetEpochIndex` counter. Retained solely so the already-executed, + /// one-shot `migrate_crv3_commits_add_block` migration stays untouched. pub fn get_first_block_of_epoch(netuid: NetUid, epoch: u64) -> u64 { let tempo: u64 = Self::get_tempo(netuid) as u64; let tempo_plus_one: u64 = tempo.saturating_add(1); @@ -1334,18 +1336,20 @@ impl Pallet { BlakeTwo256::hash_of(&(who.clone(), netuid_index, uids, values, salt, version_key)) } - pub fn find_commit_block_via_hash(hash: H256) -> Option { + /// Returns the stored `commit_epoch` (CR-v2 `WeightCommits` tuple field 1) for + /// the commit with the given hash, if any. + pub fn find_commit_epoch_via_hash(hash: H256) -> Option { WeightCommits::::iter().find_map(|(_, _, commits)| { commits .iter() .find(|(h, _, _, _)| *h == hash) - .map(|(_, commit_block, _, _)| *commit_block) + .map(|(_, commit_epoch, _, _)| *commit_epoch) }) } - pub fn is_batch_reveal_block_range(netuid: NetUid, commit_block: Vec) -> bool { - commit_block + pub fn is_batch_reveal_epoch_range(netuid: NetUid, commit_epochs: Vec) -> bool { + commit_epochs .iter() - .all(|block| Self::is_reveal_block_range(netuid, *block)) + .all(|epoch| Self::is_reveal_block_range(netuid, *epoch)) } } diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index ec191ba0e7..922fd2c894 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -2318,7 +2318,6 @@ fn test_do_remove_stake_clears_pending_childkeys() { assert!(pending_before.1 > 0); // Remove stake - remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); assert_ok!(SubtensorModule::do_remove_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -3144,6 +3143,9 @@ fn test_parent_child_chain_emission() { PendingValidatorEmission::::insert(netuid, AlphaBalance::ZERO); PendingServerEmission::::insert(netuid, AlphaBalance::ZERO); + // To trigger the epoch, block should be > tempo. So we advance it before + System::set_block_number(2); + // Run epoch with emission value let emission_value = u64::from(emission.peek()); SubtensorModule::run_coinbase(emission); diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index 4b75d164f4..12606c5266 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -18,7 +18,7 @@ use frame_support::{assert_err, assert_noop, assert_ok}; use sp_core::{H256, U256}; use sp_runtime::DispatchError; use std::collections::BTreeSet; -use substrate_fixed::types::{I96F32, U64F64}; +use substrate_fixed::types::I96F32; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; use subtensor_swap_interface::SwapHandler; @@ -758,6 +758,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { }); } +/// cargo test --package pallet-subtensor --lib -- tests::claim_root::test_claim_root_with_run_coinbase --exact --nocapture #[test] fn test_claim_root_swap_failure_does_not_consume_claim() { new_test_ext(1).execute_with(|| { @@ -885,10 +886,15 @@ fn test_claim_root_with_run_coinbase() { // Set moving price > 1.0 and price > 1.0 // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); + let tao = TaoBalance::from(10_000_000_000_000_u64); + let alpha = AlphaBalance::from(1_000_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + assert_eq!(current_price, 10.0f64); + RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); // Make sure we are root selling, so we have root alpha divs. let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); @@ -901,6 +907,9 @@ fn test_claim_root_with_run_coinbase() { .into(); assert_eq!(initial_stake, 0u64); + // To trigger the epoch, block should be > tempo. So we advance it before + System::set_block_number(2); + let block_emissions = SubtensorModule::mint_tao(1_000_000u64.into()); SubtensorModule::run_coinbase(block_emissions); @@ -997,10 +1006,15 @@ fn test_claim_root_with_block_emissions() { // Set moving price > 1.0 and price > 1.0 // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); + let tao = TaoBalance::from(10_000_000_000_000_u64); + let alpha = AlphaBalance::from(1_000_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + assert_eq!(current_price, 10.0f64); + RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); // Make sure we are root selling, so we have root alpha divs. let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); @@ -1087,6 +1101,7 @@ fn test_populate_staking_maps() { }); } +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::claim_root::test_claim_root_coinbase_distribution --exact --show-output #[test] fn test_claim_root_coinbase_distribution() { new_test_ext(1).execute_with(|| { @@ -1095,7 +1110,10 @@ fn test_claim_root_coinbase_distribution() { let coldkey = U256::from(1003); let netuid = add_dynamic_network(&hotkey, &owner_coldkey); - Tempo::::insert(netuid, 1); + // Period is `tempo`; with `tempo = 2` and the scheduler re-anchored at the + // current block, the epoch fires two steps later (at `run_to_block(3)`). + Tempo::::insert(netuid, 2); + crate::LastEpochBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 200_000_000u64; @@ -1117,16 +1135,21 @@ fn test_claim_root_coinbase_distribution() { initial_total_hotkey_alpha.into(), ); - let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); - let alpha_emissions: AlphaBalance = 1_000_000_000u64.into(); - // Set moving price > 1.0 and price > 1.0 // So we turn ON root sell SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); + let tao = TaoBalance::from(100_000_000_000_u64); + let alpha = AlphaBalance::from(100_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); + // let current_price = + // ::SwapInterface::current_alpha_price(netuid.into()) + // .saturating_to_num::(); + // assert_eq!(current_price, 2.0f64); + RootClaimableThreshold::::insert(netuid, I96F32::from_num(0)); + + let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); + let alpha_emissions: AlphaBalance = 1_000_000_000u64.into(); // Make sure we are root selling, so we have root alpha divs. let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 1425d836ce..0e3e8038ff 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -12,8 +12,6 @@ use crate::*; use alloc::collections::BTreeMap; use approx::assert_abs_diff_eq; use frame_support::assert_ok; -use pallet_subtensor_swap::position::PositionId; -use safe_math::FixedExt; use sp_core::U256; use substrate_fixed::{ transcendental::sqrt, @@ -378,20 +376,8 @@ fn test_coinbase_tao_issuance_different_prices() { mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid1, - TaoBalance::ZERO, - 1_000_000_000_000_u64.into(), - false, - ) - .unwrap(); - SubtensorModule::swap_tao_for_alpha( - netuid2, - TaoBalance::ZERO, - 1_000_000_000_000_u64.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid1, None); + ::SwapInterface::init_swap(netuid2, None); // Make subnets dynamic. SubnetMechanism::::insert(netuid1, 1); @@ -456,20 +442,8 @@ fn test_coinbase_tao_issuance_different_prices() { // mock::setup_reserves(netuid2, initial_tao.into(), initial_alpha2.into()); // // Force the swap to initialize -// SubtensorModule::swap_tao_for_alpha( -// netuid1, -// TaoBalance::ZERO, -// 1_000_000_000_000.into(), -// false, -// ) -// .unwrap(); -// SubtensorModule::swap_tao_for_alpha( -// netuid2, -// TaoBalance::ZERO, -// 1_000_000_000_000.into(), -// false, -// ) -// .unwrap(); +// ::SwapInterface::init_swap(netuid1); +// ::SwapInterface::init_swap(netuid2); // // Set subnet prices to reversed proportion to ensure they don't affect emissions. // SubnetMovingPrice::::insert(netuid1, I96F32::from_num(2)); @@ -557,7 +531,7 @@ fn test_coinbase_moving_prices() { // Run moving 1 times. SubtensorModule::update_moving_price(netuid); // Assert price is ~ 100% of the real price. - assert!(U96F32::from_num(1.0) - SubtensorModule::get_moving_alpha_price(netuid) < 0.05); + assert!(U64F64::from_num(1.0) - SubtensorModule::get_moving_alpha_price(netuid) < 0.05); // Set price to zero. SubnetMovingPrice::::insert(netuid, I96F32::from_num(0)); SubnetMovingAlpha::::set(I96F32::from_num(0.1)); @@ -786,20 +760,8 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid1, - TaoBalance::ZERO, - 1_000_000_000_000_u64.into(), - false, - ) - .unwrap(); - SubtensorModule::swap_tao_for_alpha( - netuid2, - TaoBalance::ZERO, - 1_000_000_000_000_u64.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid1, None); + ::SwapInterface::init_swap(netuid2, None); // Get the prices before the run_coinbase let price_1_before = ::SwapInterface::current_alpha_price(netuid1); @@ -853,7 +815,7 @@ fn test_owner_cut_base() { 1_000_000_000_000_u64.into(), 1_000_000_000_000_u64.into(), ); - SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) + SubtensorModule::set_tempo_unchecked(netuid, 10000); // Large number (dont drain) SubtensorModule::set_subnet_owner_cut(0); SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); assert_eq!(PendingOwnerCut::::get(netuid), 0.into()); // No cut @@ -863,7 +825,7 @@ fn test_owner_cut_base() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_pending_swapped --exact --show-output --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_pending_emission --exact --show-output --nocapture #[test] fn test_pending_emission() { new_test_ext(1).execute_with(|| { @@ -875,10 +837,13 @@ fn test_pending_emission() { FirstEmissionBlockNumber::::insert(netuid, 0); mock::setup_reserves(netuid, 1_000_000.into(), 1.into()); + LastEpochBlock::::insert(netuid, 0); + System::set_block_number(10); SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); SubnetTAO::::insert(NetUid::ROOT, TaoBalance::from(1_000_000_000)); // Add root weight. + System::set_block_number(12); SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into())); - SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) + SubtensorModule::set_tempo_unchecked(netuid, 10000); // Large number (dont drain) SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 // Set moving price > 1.0 @@ -2655,7 +2620,7 @@ fn test_distribute_emission_zero_emission() { let miner_ck = U256::from(6); let init_stake: u64 = 100_000_000_000_000; let tempo = 2; - SubtensorModule::set_tempo(netuid, tempo); + SubtensorModule::set_tempo_unchecked(netuid, tempo); // Set weight-set limit to 0. SubtensorModule::set_weights_set_rate_limit(netuid, 0); @@ -2748,7 +2713,7 @@ fn test_run_coinbase_not_started() { let miner_ck = U256::from(6); let init_stake: u64 = 100_000_000_000_000; let tempo = 2; - SubtensorModule::set_tempo(netuid, tempo); + SubtensorModule::set_tempo_unchecked(netuid, tempo); // Set weight-set limit to 0. SubtensorModule::set_weights_set_rate_limit(netuid, 0); @@ -2843,7 +2808,7 @@ fn test_run_coinbase_not_started_start_after() { let miner_ck = U256::from(6); let init_stake: u64 = 100_000_000_000_000; let tempo = 2; - SubtensorModule::set_tempo(netuid, tempo); + SubtensorModule::set_tempo_unchecked(netuid, tempo); // Set weight-set limit to 0. SubtensorModule::set_weights_set_rate_limit(netuid, 0); @@ -2911,6 +2876,12 @@ fn test_run_coinbase_not_started_start_after() { Some(current_block + 1) ); + // Advance the block past `LastEpochBlock + tempo` so the state-based + // scheduler is due again (the previous `run_coinbase` advanced it). + next_block_no_epoch(netuid); + next_block_no_epoch(netuid); + next_block_no_epoch(netuid); + // Run coinbase with emission. let emission_credit = SubtensorModule::mint_tao(100_000_000.into()); SubtensorModule::run_coinbase(emission_credit); @@ -2925,59 +2896,6 @@ fn test_run_coinbase_not_started_start_after() { }); } -/// Test that coinbase updates protocol position liquidity -/// cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_v3_liquidity_update --exact --show-output -#[test] -fn test_coinbase_v3_liquidity_update() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(1); - let owner_coldkey = U256::from(2); - - // add network - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid, - TaoBalance::ZERO, - 1_000_000_000_000_u64.into(), - false, - ) - .unwrap(); - - let protocol_account_id = pallet_subtensor_swap::Pallet::::protocol_account_id(); - let position = pallet_subtensor_swap::Positions::::get(( - netuid, - protocol_account_id, - PositionId::from(1), - )) - .unwrap(); - let liquidity_before = position.liquidity; - - // Enable emissions and run coinbase (which will increase position liquidity) - let emission: u64 = 1_234_567; - let emission_credit = SubtensorModule::mint_tao(emission.into()); - // Dynamic subnets register with emission disabled by default. - SubnetEmissionEnabled::::insert(netuid, true); - // Price-based emission shares require a non-zero moving price. - SubnetMovingPrice::::insert(netuid, I96F32::from_num(1)); - // Keep root_proportion ~1 so the injection cap does not bind. - set_full_injection_root_stake(); - FirstEmissionBlockNumber::::insert(netuid, 0); - SubtensorModule::run_coinbase(emission_credit); - - let position_after = pallet_subtensor_swap::Positions::::get(( - netuid, - protocol_account_id, - PositionId::from(1), - )) - .unwrap(); - let liquidity_after = position_after.liquidity; - - assert!(liquidity_before < liquidity_after); - }); -} - // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_alpha_childkey_parentkey_with_burn --exact --show-output --nocapture #[test] fn test_drain_alpha_childkey_parentkey_with_burn() { @@ -3178,6 +3096,7 @@ fn test_zero_shares_zero_emission() { }); } +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_no_root_sell --exact --show-output --nocapture #[test] fn test_mining_emission_distribution_with_no_root_sell() { new_test_ext(1).execute_with(|| { @@ -3271,11 +3190,8 @@ fn test_mining_emission_distribution_with_no_root_sell() { // Make root sell NOT happen // set price very low, e.g. a lot of alpha in - //SubnetAlphaIn::::insert(netuid, AlphaBalance::from(1_000_000_000_000_000)); - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(0.01), - ); + let alpha = AlphaBalance::from(1_000_000_000_000_000_000_u64); + SubnetAlphaIn::::insert(netuid, alpha); // Make sure we ARE NOT root selling, so we do not have root alpha divs. let root_sell_flag = SubtensorModule::get_network_root_sell_flag(&[netuid]); @@ -3305,17 +3221,20 @@ fn test_mining_emission_distribution_with_no_root_sell() { AlphaBalance::ZERO, "Root alpha divs should be zero" ); + step_block(1); + // Drain to a clean epoch boundary so accumulation starts fresh. + step_epochs(1, netuid); let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &miner_hotkey, &miner_coldkey, netuid, ); // Run again but with some root stake - step_block(subnet_tempo - 2); + step_block(subnet_tempo - 1); assert_abs_diff_eq!( PendingServerEmission::::get(netuid).to_u64(), U96F32::saturating_from_num(per_block_emission) - .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + .saturating_mul(U96F32::saturating_from_num((subnet_tempo - 1) as u64)) .saturating_mul(U96F32::saturating_from_num(0.5)) // miner cut .saturating_mul(U96F32::saturating_from_num(0.90)) .saturating_to_num::(), @@ -3362,7 +3281,7 @@ fn test_mining_emission_distribution_with_no_root_sell() { U96F32::saturating_from_num(miner_incentive) .saturating_div(u16::MAX.into()) .saturating_mul(U96F32::saturating_from_num(per_block_emission)) - .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo)) .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut .saturating_to_num::(), epsilon = 1_000_000_u64 @@ -3393,7 +3312,9 @@ fn test_mining_emission_distribution_with_root_sell() { let owner_hotkey = U256::from(10); let owner_coldkey = U256::from(11); let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - Tempo::::insert(netuid, 1); + // Period is `tempo`; `tempo = 2` keeps a one-block gap between epochs so + // pending root-alpha-divs can be observed accumulating before a drain. + Tempo::::insert(netuid, 2); FirstEmissionBlockNumber::::insert(netuid, 0); // Setup large LPs to prevent slippage @@ -3467,10 +3388,8 @@ fn test_mining_emission_distribution_with_root_sell() { // Make root sell happen // Set moving price > 1.0 // Set price > 1.0 - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); + let alpha = AlphaBalance::from(100_000_000_000_000_u64); + SubnetAlphaIn::::insert(netuid, alpha); SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); @@ -3481,6 +3400,7 @@ fn test_mining_emission_distribution_with_root_sell() { // Run run_coinbase until emissions are drained step_block(subnet_tempo); + LastEpochBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &miner_hotkey, @@ -3536,7 +3456,7 @@ fn test_mining_emission_distribution_with_root_sell() { U96F32::saturating_from_num(miner_incentive) .saturating_div(u16::MAX.into()) .saturating_mul(U96F32::saturating_from_num(per_block_emission)) - .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo)) .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut .saturating_to_num::(), epsilon = 1_000_000_u64 @@ -3595,8 +3515,8 @@ fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { TaoBalance::from(1_000_000_000_000_000_u64), AlphaBalance::from(1_000_000_000_000_000_u64), ); - // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + // Initialize swap + Swap::maybe_initialize_palswap(netuid0, None); // Set netuid0 to have price tao_emission / price > alpha_emission let alpha_emission = U96F32::saturating_from_num( @@ -3607,14 +3527,19 @@ fn test_coinbase_subnet_terms_with_alpha_in_gt_alpha_emission() { ); let price_to_set: U64F64 = U64F64::saturating_from_num(0.01); let price_to_set_fixed: U96F32 = U96F32::saturating_from_num(price_to_set); - let sqrt_price_to_set: U64F64 = sqrt(price_to_set).unwrap(); let tao_emission: U96F32 = U96F32::saturating_from_num(alpha_emission) .saturating_mul(price_to_set_fixed) .saturating_add(U96F32::saturating_from_num(0.01)); // Set the price - pallet_subtensor_swap::AlphaSqrtPrice::::insert(netuid0, sqrt_price_to_set); + let tao = TaoBalance::from(1_000_000_000_u64); + let alpha = AlphaBalance::from( + (U64F64::saturating_from_num(u64::from(tao)) / price_to_set).to_num::(), + ); + SubnetTAO::::insert(netuid0, tao); + SubnetAlphaIn::::insert(netuid0, alpha); + // Check the price is set assert_abs_diff_eq!( pallet_subtensor_swap::Pallet::::current_alpha_price(netuid0).to_num::(), @@ -3677,8 +3602,8 @@ fn test_coinbase_subnet_terms_with_alpha_in_lte_alpha_emission() { TaoBalance::from(1_000_000_000_000_000_u64), AlphaBalance::from(1_000_000_000_000_000_u64), ); - // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + // Initialize swap + Swap::maybe_initialize_palswap(netuid0, None); let alpha_emission = U96F32::saturating_from_num( SubtensorModule::get_block_emission_for_issuance( @@ -3688,7 +3613,7 @@ fn test_coinbase_subnet_terms_with_alpha_in_lte_alpha_emission() { ); let tao_emission = U96F32::saturating_from_num(34566756_u64); - let price: U96F32 = Swap::current_alpha_price(netuid0); + let price: U96F32 = U96F32::saturating_from_num(Swap::current_alpha_price(netuid0)); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); @@ -3746,8 +3671,8 @@ fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { TaoBalance::from(1_000_000_000_000_000_u64), AlphaBalance::from(1_000_000_000_000_000_u64), ); - // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + // Initialize swap + Swap::maybe_initialize_palswap(netuid0, None); let tao_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(123))]); let alpha_in = BTreeMap::from([(netuid0, U96F32::saturating_from_num(456))]); @@ -3802,8 +3727,8 @@ fn test_coinbase_drain_pending_resets_blockssincelaststep() { let zero = U96F32::saturating_from_num(0); let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); Tempo::::insert(netuid0, 100); - // Ensure the block number we use is the tempo block - let block_number = 98; + LastEpochBlock::::insert(netuid0, 0); + let block_number = 102; assert!(SubtensorModule::should_run_epoch(netuid0, block_number)); let blocks_since_last_step_before = 12345678; @@ -3815,8 +3740,7 @@ fn test_coinbase_drain_pending_resets_blockssincelaststep() { let blocks_since_last_step_after = BlocksSinceLastStep::::get(netuid0); assert_eq!(blocks_since_last_step_after, 0); - // Also check LastMechansimStepBlock is set to the block number we ran on - assert_eq!(LastMechansimStepBlock::::get(netuid0), block_number); + assert_eq!(LastMechansimStepBlock::::get(netuid0), 12345); }); } @@ -3826,8 +3750,8 @@ fn test_coinbase_drain_pending_gets_counters_and_resets_them() { let zero = U96F32::saturating_from_num(0); let netuid0 = add_dynamic_network(&U256::from(1), &U256::from(2)); Tempo::::insert(netuid0, 100); - // Ensure the block number we use is the tempo block - let block_number = 98; + LastEpochBlock::::insert(netuid0, 0); + let block_number = 102; assert!(SubtensorModule::should_run_epoch(netuid0, block_number)); let pending_server_em = AlphaBalance::from(123434534); @@ -3881,8 +3805,8 @@ fn test_coinbase_emit_to_subnets_with_no_root_sell() { TaoBalance::from(1_000_000_000_000_000_u64), AlphaBalance::from(1_000_000_000_000_000_u64), ); - // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + // Initialize swap + Swap::maybe_initialize_palswap(netuid0, None); let tao_emission = U96F32::saturating_from_num(12345678); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); @@ -3896,7 +3820,7 @@ fn test_coinbase_emit_to_subnets_with_no_root_sell() { ) .unwrap_or(0), ); - let price: U96F32 = Swap::current_alpha_price(netuid0); + let price: U96F32 = U96F32::saturating_from_num(Swap::current_alpha_price(netuid0)); let (tao_in, alpha_in, alpha_out, excess_tao) = SubtensorModule::get_subnet_terms(&subnet_emissions); // Based on the price, we should have NO excess TAO @@ -3973,8 +3897,8 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { TaoBalance::from(1_000_000_000_000_000_u64), AlphaBalance::from(1_000_000_000_000_000_u64), ); - // Initialize swap v3 - Swap::maybe_initialize_v3(netuid0); + // Initialize swap + Swap::maybe_initialize_palswap(netuid0, None); let tao_emission = U96F32::saturating_from_num(12345678); let subnet_emissions = BTreeMap::from([(netuid0, tao_emission)]); @@ -3988,7 +3912,7 @@ fn test_coinbase_emit_to_subnets_with_root_sell() { ) .unwrap_or(0), ); - let price: U96F32 = Swap::current_alpha_price(netuid0); + let price: U96F32 = U96F32::saturating_from_num(Swap::current_alpha_price(netuid0)); let (tao_in, alpha_in, alpha_out, excess_tao) = SubtensorModule::get_subnet_terms(&subnet_emissions); // Based on the price, we should have NO excess TAO @@ -4065,10 +3989,11 @@ fn test_disabling_owner_cut_sends_subnet_emission_to_miners_and_validators() { let miner_coldkey = U256::from(5); let miner_hotkey = U256::from(6); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + LastEpochBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); let subnet_tempo = 10; let stake = 100_000_000_000u64; - SubtensorModule::set_tempo(netuid, subnet_tempo); + SubtensorModule::set_tempo_unchecked(netuid, subnet_tempo); setup_reserves(netuid, (stake * 10_000).into(), (stake * 10_000).into()); register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); @@ -4221,10 +4146,10 @@ fn test_pending_emission_start_call_not_done() { // Make root sell happen // Set moving price > 1.0 // Set price > 1.0 - pallet_subtensor_swap::AlphaSqrtPrice::::insert( - netuid, - U64F64::saturating_from_num(10.0), - ); + let tao = TaoBalance::from(10_000_000_000_u64); + let alpha = AlphaBalance::from(1_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); SubnetMovingPrice::::insert(netuid, I96F32::from_num(2)); @@ -4353,3 +4278,103 @@ fn test_get_subnet_terms_alpha_emissions_cap() { assert_eq!(alpha_in.get(&netuid).copied().unwrap(), injection_cap); }); } + +#[test] +fn test_epochs_deferred_this_block_respects_cap() { + new_test_ext(1).execute_with(|| { + let cap = SubtensorModule::get_max_epochs_per_block() as usize; + let n = cap + 2; + + for i in 0..n { + let netuid = NetUid::from((i + 1) as u16); + add_network(netuid, 100, 0); + // Force "due this block". + PendingEpochAt::::insert(netuid, 1); + } + + let block = SubtensorModule::get_current_block_as_u64(); + let subnets: Vec = SubtensorModule::get_all_subnet_netuids() + .into_iter() + .filter(|x| *x != NetUid::ROOT) + .collect(); + + // All `n` subnets are due, but only `cap` may fire — the rest are deferred. + let deferred = SubtensorModule::epochs_deferred_this_block(&subnets, block); + assert_eq!( + deferred.len(), + n - cap, + "exactly the due subnets beyond MaxEpochsPerBlock are deferred" + ); + for netuid in &deferred { + assert!(SubtensorModule::should_run_epoch(*netuid, block)); + } + }); +} + +// Regression test for the dynamic-tempo / CR-v3 interaction: when a subnet's epoch +// is deferred by the per-block cap, its timelock reveal must be held back to the +// deferred fire-block (not run on the originally-scheduled block, which would +// surface weights before the epoch consumes them). +// +// Crypto-free probe: the reveal path removes *expired* commits only when it runs +// for a subnet, so a retained expired (epoch-0) commit means the reveal was skipped. +#[test] +fn test_reveal_crv3_defers_with_capped_epoch() { + new_test_ext(1).execute_with(|| { + let cap = SubtensorModule::get_max_epochs_per_block() as usize; + let n = cap + 2; + let mec0 = subtensor_runtime_common::MechId::from(0); + + for i in 0..n { + let netuid = NetUid::from((i + 1) as u16); + add_network(netuid, 100, 0); + PendingEpochAt::::insert(netuid, 1); // due this block + SubnetEpochIndex::::insert(netuid, 10); // cur_epoch >> reveal_period + // Plant an expired commit at epoch 0 (field types inferred from the queue). + let idx = SubtensorModule::get_mechanism_storage_index(netuid, mec0); + TimelockedWeightCommits::::mutate(idx, 0u64, |q| { + q.push_back((U256::from(1u64), 0u64, Default::default(), 0u64)); + }); + } + + let subnets: Vec = SubtensorModule::get_all_subnet_netuids() + .into_iter() + .filter(|x| *x != NetUid::ROOT) + .collect(); + + let still_holds = |netuid: NetUid| -> bool { + let idx = SubtensorModule::get_mechanism_storage_index(netuid, mec0); + TimelockedWeightCommits::::contains_key(idx, 0u64) + }; + let retained = |subnets: &[NetUid]| subnets.iter().filter(|n| still_holds(**n)).count(); + + // --- Phase 1: cap-deferred subnets must NOT reveal this block. + SubtensorModule::reveal_crv3_commits(); + assert_eq!( + retained(&subnets), + n - cap, + "only cap-deferred subnets keep their commit (their reveal was skipped)" + ); + + let deferred: Vec = subnets + .iter() + .copied() + .filter(|n| still_holds(*n)) + .collect(); + + // --- Phase 2: drop the cap pressure so only the deferred subnets are due; + // they should now reveal (and clean their expired commit). + for netuid in &subnets { + if !deferred.contains(netuid) { + PendingEpochAt::::insert(*netuid, 0); + LastEpochBlock::::insert(*netuid, 1); // blocks_since < tempo => not due + } + } + SubtensorModule::reveal_crv3_commits(); + assert_eq!( + retained(&subnets), + 0, + "deferred subnets reveal once they actually fire" + ); + }); +} diff --git a/pallets/subtensor/src/tests/emission.rs b/pallets/subtensor/src/tests/emission.rs index ecd2df544b..151fd3cddb 100644 --- a/pallets/subtensor/src/tests/emission.rs +++ b/pallets/subtensor/src/tests/emission.rs @@ -1,6 +1,7 @@ use subtensor_runtime_common::NetUid; use super::mock::*; +use crate::LastEpochBlock; // 1. Test Zero Tempo // Description: Verify that when tempo is 0, the function returns u64::MAX. @@ -9,7 +10,7 @@ use super::mock::*; fn test_zero_tempo() { new_test_ext(1).execute_with(|| { assert_eq!( - SubtensorModule::blocks_until_next_epoch(1.into(), 0, 100), + SubtensorModule::blocks_until_next_auto_epoch(1.into(), 0, 100), u64::MAX ); }); @@ -21,14 +22,21 @@ fn test_zero_tempo() { #[test] fn test_regular_case() { new_test_ext(1).execute_with(|| { - assert_eq!(SubtensorModule::blocks_until_next_epoch(1.into(), 10, 5), 3); + LastEpochBlock::::insert(NetUid::from(1), 0); + LastEpochBlock::::insert(NetUid::from(2), 0); + LastEpochBlock::::insert(NetUid::from(3), 0); + // (LastEpochBlock + tempo) - block. assert_eq!( - SubtensorModule::blocks_until_next_epoch(2.into(), 20, 15), - 2 + SubtensorModule::blocks_until_next_auto_epoch(1.into(), 10, 5), + 5 ); assert_eq!( - SubtensorModule::blocks_until_next_epoch(3.into(), 30, 25), - 1 + SubtensorModule::blocks_until_next_auto_epoch(2.into(), 20, 15), + 5 + ); + assert_eq!( + SubtensorModule::blocks_until_next_auto_epoch(3.into(), 30, 25), + 5 ); }); } @@ -39,12 +47,16 @@ fn test_regular_case() { #[test] fn test_boundary_conditions() { new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(u16::MAX); + LastEpochBlock::::insert(netuid, 0); + // Far past the next-auto block — saturating to 0. assert_eq!( - SubtensorModule::blocks_until_next_epoch(u16::MAX.into(), u16::MAX, u64::MAX), + SubtensorModule::blocks_until_next_auto_epoch(netuid, u16::MAX, u64::MAX), 0 ); + // Block 0 — full period until next auto epoch. assert_eq!( - SubtensorModule::blocks_until_next_epoch(u16::MAX.into(), u16::MAX, 0), + SubtensorModule::blocks_until_next_auto_epoch(netuid, u16::MAX, 0), u16::MAX as u64 ); }); @@ -56,9 +68,11 @@ fn test_boundary_conditions() { #[test] fn test_overflow_handling() { new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(u16::MAX); + LastEpochBlock::::insert(netuid, 0); assert_eq!( - SubtensorModule::blocks_until_next_epoch(u16::MAX.into(), u16::MAX, u64::MAX - 1), - 1 + SubtensorModule::blocks_until_next_auto_epoch(netuid, u16::MAX, u64::MAX - 1), + 0 ); }); } @@ -69,13 +83,17 @@ fn test_overflow_handling() { #[test] fn test_epoch_alignment() { new_test_ext(1).execute_with(|| { + LastEpochBlock::::insert(NetUid::from(1), 0); + LastEpochBlock::::insert(NetUid::from(2), 0); + // (LastEpochBlock + tempo) - block_number. assert_eq!( - SubtensorModule::blocks_until_next_epoch(1.into(), 10, 9), - 10 + SubtensorModule::blocks_until_next_auto_epoch(1.into(), 10, 9), + 1 ); + // Block exactly at next-auto — returns 0. assert_eq!( - SubtensorModule::blocks_until_next_epoch(2.into(), 20, 21), - 17 + SubtensorModule::blocks_until_next_auto_epoch(2.into(), 20, 21), + 0 ); }); } @@ -86,9 +104,23 @@ fn test_epoch_alignment() { #[test] fn test_different_network_ids() { new_test_ext(1).execute_with(|| { - assert_eq!(SubtensorModule::blocks_until_next_epoch(1.into(), 10, 5), 3); - assert_eq!(SubtensorModule::blocks_until_next_epoch(2.into(), 10, 5), 2); - assert_eq!(SubtensorModule::blocks_until_next_epoch(3.into(), 10, 5), 1); + // Anchor each subnet identically — proves the new formula does NOT + // depend on `netuid` (only on the per-subnet `LastEpochBlock`). + LastEpochBlock::::insert(NetUid::from(1), 0); + LastEpochBlock::::insert(NetUid::from(2), 0); + LastEpochBlock::::insert(NetUid::from(3), 0); + assert_eq!( + SubtensorModule::blocks_until_next_auto_epoch(1.into(), 10, 5), + 5 + ); + assert_eq!( + SubtensorModule::blocks_until_next_auto_epoch(2.into(), 10, 5), + 5 + ); + assert_eq!( + SubtensorModule::blocks_until_next_auto_epoch(3.into(), 10, 5), + 5 + ); }); } @@ -98,9 +130,11 @@ fn test_different_network_ids() { #[test] fn test_large_tempo_values() { new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + LastEpochBlock::::insert(netuid, 0); assert_eq!( - SubtensorModule::blocks_until_next_epoch(1.into(), u16::MAX - 1, 100), - u16::MAX as u64 - 103 + SubtensorModule::blocks_until_next_auto_epoch(netuid, u16::MAX - 1, 100), + (u16::MAX as u64).saturating_sub(1).saturating_sub(100) ); }); } @@ -113,9 +147,11 @@ fn test_consecutive_blocks() { new_test_ext(1).execute_with(|| { let tempo = 10; let netuid = NetUid::from(1); - let mut last_result = SubtensorModule::blocks_until_next_epoch(netuid, tempo, 0); + LastEpochBlock::::insert(netuid, 0); + let mut last_result = SubtensorModule::blocks_until_next_auto_epoch(netuid, tempo, 0); for i in 1..tempo - 1 { - let current_result = SubtensorModule::blocks_until_next_epoch(netuid, tempo, i as u64); + let current_result = + SubtensorModule::blocks_until_next_auto_epoch(netuid, tempo, i as u64); assert_eq!(current_result, last_result - 1); last_result = current_result; } @@ -128,13 +164,16 @@ fn test_consecutive_blocks() { #[test] fn test_wrap_around_behavior() { new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + LastEpochBlock::::insert(netuid, 0); + // `next_auto - block_number` saturates to 0 for far-future blocks. assert_eq!( - SubtensorModule::blocks_until_next_epoch(1.into(), 10, u64::MAX), - 9 + SubtensorModule::blocks_until_next_auto_epoch(netuid, 10, u64::MAX), + 0 ); assert_eq!( - SubtensorModule::blocks_until_next_epoch(1.into(), 10, u64::MAX - 1), - 10 + SubtensorModule::blocks_until_next_auto_epoch(netuid, 10, u64::MAX - 1), + 0 ); }); } diff --git a/pallets/subtensor/src/tests/ensure.rs b/pallets/subtensor/src/tests/ensure.rs index 1253285306..008be48b15 100644 --- a/pallets/subtensor/src/tests/ensure.rs +++ b/pallets/subtensor/src/tests/ensure.rs @@ -66,16 +66,22 @@ fn ensure_subnet_owner_or_root_distinguishes_root_and_owner() { fn ensure_admin_window_open_blocks_in_freeze_window() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(0); - let tempo = 10; - add_network(netuid, 10, 0); + let tempo: u16 = 10; + add_network(netuid, tempo, 0); - let freeze_window = 3; + let freeze_window: u16 = 3; crate::Pallet::::set_admin_freeze_window(freeze_window); - System::set_block_number((tempo - freeze_window).into()); + crate::LastEpochBlock::::insert(netuid, 0); + // Period is `tempo`: next auto-epoch fires at `LastEpochBlock + tempo`. + let next_auto = tempo as u64; + + // Inside freeze window: `next_auto - freeze_window + 1`. + System::set_block_number(next_auto - freeze_window as u64 + 1); assert!(crate::Pallet::::ensure_admin_window_open(netuid).is_err()); - System::set_block_number((tempo - freeze_window - 1).into()); + // Outside freeze window: `next_auto - freeze_window`. + System::set_block_number(next_auto - freeze_window as u64); assert!(crate::Pallet::::ensure_admin_window_open(netuid).is_ok()); }); } @@ -93,7 +99,7 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { crate::Pallet::::set_admin_freeze_window(0); // Set tempo to 1 so owner hyperparam RL = 2 blocks - crate::Pallet::::set_tempo(netuid, 1); + crate::Pallet::::set_tempo_unchecked(netuid, 1); assert_eq!(OwnerHyperparamRateLimit::::get(), 2); @@ -135,12 +141,12 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { // (using loop for clarity, because epoch calculation function uses netuid) // Restore tempo and configure freeze window for this part let freeze_window = 3; - crate::Pallet::::set_tempo(netuid, tempo); + crate::Pallet::::set_tempo_unchecked(netuid, tempo); crate::Pallet::::set_admin_freeze_window(freeze_window); let freeze_window = freeze_window as u64; loop { let cur = crate::Pallet::::get_current_block_as_u64(); - let rem = crate::Pallet::::blocks_until_next_epoch(netuid, tempo, cur); + let rem = crate::Pallet::::blocks_until_next_auto_epoch(netuid, tempo, cur); if rem < freeze_window { break; } diff --git a/pallets/subtensor/src/tests/epoch.rs b/pallets/subtensor/src/tests/epoch.rs index 02236d892d..b0383521a8 100644 --- a/pallets/subtensor/src/tests/epoch.rs +++ b/pallets/subtensor/src/tests/epoch.rs @@ -2052,14 +2052,14 @@ fn test_deregistered_miner_bonds() { } // Set tempo high so we don't automatically run epochs - SubtensorModule::set_tempo(netuid, high_tempo); + SubtensorModule::set_tempo_unchecked(netuid, high_tempo); // Run 2 blocks next_block(); next_block(); // set tempo to 2 blocks - SubtensorModule::set_tempo(netuid, 2); + SubtensorModule::set_tempo_unchecked(netuid, 2); // Run epoch if sparse { SubtensorModule::epoch(netuid, 1_000_000_000.into()); @@ -2077,7 +2077,7 @@ fn test_deregistered_miner_bonds() { assert!(bond_0_3 > 0); // Set tempo high so we don't automatically run epochs - SubtensorModule::set_tempo(netuid, high_tempo); + SubtensorModule::set_tempo_unchecked(netuid, high_tempo); // Run one more block next_block(); @@ -2137,7 +2137,7 @@ fn test_deregistered_miner_bonds() { ); // set tempo to 2 blocks - SubtensorModule::set_tempo(netuid, 2); + SubtensorModule::set_tempo_unchecked(netuid, 2); // Run epoch again. if sparse { SubtensorModule::epoch(netuid, 1_000_000_000.into()); @@ -2465,7 +2465,7 @@ fn test_blocks_since_last_step() { assert!(new_blocks > original_blocks); assert_eq!(new_blocks, 5); - let blocks_to_step: u16 = SubtensorModule::blocks_until_next_epoch( + let blocks_to_step: u16 = SubtensorModule::blocks_until_next_auto_epoch( netuid, tempo, SubtensorModule::get_current_block_as_u64(), @@ -2477,7 +2477,7 @@ fn test_blocks_since_last_step() { assert_eq!(post_blocks, 10); - let blocks_to_step: u16 = SubtensorModule::blocks_until_next_epoch( + let blocks_to_step: u16 = SubtensorModule::blocks_until_next_auto_epoch( netuid, tempo, SubtensorModule::get_current_block_as_u64(), @@ -3784,7 +3784,7 @@ fn test_epoch_does_not_mask_outside_window_but_masks_inside() { SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); SubtensorModule::set_max_allowed_validators(netuid, 1); - run_to_block(tempo as u64 + 1); + run_to_block(tempo as u64); /* first commit */ commit_dummy(v_hot, netuid); @@ -3801,7 +3801,7 @@ fn test_epoch_does_not_mask_outside_window_but_masks_inside() { /* let first commit expire for UID‑1 */ for _ in 0..(reveal + 1) { - run_to_block(System::block_number() + tempo as u64 + 1); + run_to_block(System::block_number() + tempo as u64); } /* second commit — will mask UID‑2 & UID‑3 */ diff --git a/pallets/subtensor/src/tests/locks.rs b/pallets/subtensor/src/tests/locks.rs index fc3e50f020..91b48b7881 100644 --- a/pallets/subtensor/src/tests/locks.rs +++ b/pallets/subtensor/src/tests/locks.rs @@ -50,7 +50,6 @@ fn setup_subnet_with_stake( amount, ::SwapInterface::max_price(), false, - false, ) .unwrap(); DecayingLock::::insert(coldkey, netuid, false); @@ -80,6 +79,7 @@ fn roll_forward_lock( owner_lock, perpetual_lock, ) + .0 } fn roll_forward_individual_lock( @@ -492,7 +492,6 @@ fn test_mixed_perpetual_and_decaying_non_owner_locks_same_hotkey_update_aggregat 100_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -897,6 +896,311 @@ fn test_available_to_unstake_fully_locked() { }); } +#[test] +fn test_stake_availability_for_coldkeys_empty_coldkeys() { + new_test_ext(1).execute_with(|| { + let result = SubtensorModule::get_stake_availability_for_coldkeys(Vec::new(), None); + assert!(result.is_empty()); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_empty_netuids() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(Vec::new())); + assert_eq!(result.len(), 1); + assert!(result.contains_key(&coldkey)); + assert!(result.get(&coldkey).unwrap().is_empty()); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_filters_empty_rows() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + 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 result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(vec![netuid])); + + assert_eq!(result.len(), 1); + assert!(result.contains_key(&coldkey)); + assert!(result.get(&coldkey).unwrap().is_empty()); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_stake_without_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + + let result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(vec![netuid])); + + assert_eq!(result.len(), 1); + let availability = result.get(&coldkey).unwrap().get(&netuid).unwrap(); + assert_eq!(availability.total(), total); + assert_eq!(availability.locked(), AlphaBalance::ZERO); + assert_eq!(availability.available(), total); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_partial_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let lock_amount = total / 2.into(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + let result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(vec![netuid])); + let availability = result.get(&coldkey).unwrap().get(&netuid).unwrap(); + + assert_eq!(availability.total(), total); + assert_eq!( + availability.locked(), + SubtensorModule::get_current_locked(&coldkey, netuid) + ); + assert_eq!(availability.available(), total - availability.locked()); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_fully_locked() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, total, + )); + + let result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(vec![netuid])); + let availability = result.get(&coldkey).unwrap().get(&netuid).unwrap(); + + assert_eq!(availability.total(), total); + assert_eq!(availability.locked(), total); + assert_eq!(availability.available(), AlphaBalance::ZERO); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_preserves_coldkey_grouping() { + new_test_ext(1).execute_with(|| { + let coldkey_a = U256::from(1); + let hotkey_a = U256::from(2); + let coldkey_b = U256::from(3); + let hotkey_b = U256::from(4); + let netuid_a = setup_subnet_with_stake(coldkey_a, hotkey_a, 100_000_000_000); + let netuid_b = setup_subnet_with_stake(coldkey_b, hotkey_b, 100_000_000_000); + + let result = SubtensorModule::get_stake_availability_for_coldkeys( + vec![coldkey_a, coldkey_b], + Some(vec![netuid_a, netuid_b]), + ); + + assert_eq!(result.len(), 2); + assert_eq!(result.get(&coldkey_a).unwrap().len(), 1); + assert!(result.get(&coldkey_a).unwrap().contains_key(&netuid_a)); + assert_eq!(result.get(&coldkey_b).unwrap().len(), 1); + assert!(result.get(&coldkey_b).unwrap().contains_key(&netuid_b)); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_none_netuids_uses_all_subnets() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let result = SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], None); + + assert_eq!(result.len(), 1); + assert!(result.get(&coldkey).unwrap().contains_key(&netuid)); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_one_coldkey_two_subnets() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey_a = U256::from(2); + let hotkey_b = U256::from(3); + let netuid_a = setup_subnet_with_stake(coldkey, hotkey_a, 100_000_000_000); + let netuid_b = setup_subnet_with_stake(coldkey, hotkey_b, 100_000_000_000); + let total_a = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid_a); + let total_b = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid_b); + + let result = SubtensorModule::get_stake_availability_for_coldkeys( + vec![coldkey], + Some(vec![netuid_a, netuid_b]), + ); + + assert_eq!(result.len(), 1); + let subnets = result.get(&coldkey).unwrap(); + assert_eq!(subnets.len(), 2); + assert!(subnets.contains_key(&netuid_a)); + assert!(subnets.contains_key(&netuid_b)); + + let row_a = subnets.get(&netuid_a).unwrap(); + assert_eq!(row_a.total(), total_a); + assert_eq!(row_a.locked(), AlphaBalance::ZERO); + assert_eq!(row_a.available(), total_a); + + let row_b = subnets.get(&netuid_b).unwrap(); + assert_eq!(row_b.total(), total_b); + assert_eq!(row_b.locked(), AlphaBalance::ZERO); + assert_eq!(row_b.available(), total_b); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_filters_to_requested_netuid() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey_a = U256::from(2); + let hotkey_b = U256::from(3); + let netuid_a = setup_subnet_with_stake(coldkey, hotkey_a, 100_000_000_000); + let netuid_b = setup_subnet_with_stake(coldkey, hotkey_b, 100_000_000_000); + + let result = SubtensorModule::get_stake_availability_for_coldkeys( + vec![coldkey], + Some(vec![netuid_b]), + ); + + assert_eq!(result.len(), 1); + let subnets = result.get(&coldkey).unwrap(); + assert_eq!(subnets.len(), 1); + assert!(subnets.contains_key(&netuid_b)); + assert!(!subnets.contains_key(&netuid_a)); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_dedups_netuids() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + let result = SubtensorModule::get_stake_availability_for_coldkeys( + vec![coldkey], + Some(vec![netuid, netuid]), + ); + + assert_eq!(result.len(), 1); + assert_eq!(result.get(&coldkey).unwrap().len(), 1); + assert!(result.get(&coldkey).unwrap().contains_key(&netuid)); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_skips_nonexistent_netuid() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let nonexistent = subtensor_runtime_common::NetUid::from(99); + + let result = SubtensorModule::get_stake_availability_for_coldkeys( + vec![coldkey], + Some(vec![nonexistent]), + ); + assert_eq!(result.len(), 1); + assert!(result.get(&coldkey).unwrap().is_empty()); + + // Mix real + fake requires at least two subnets on chain so len(requested) <= subnet_count. + let subnet_owner_coldkey = U256::from(2001); + let subnet_owner_hotkey = U256::from(2002); + let _other_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + let result = SubtensorModule::get_stake_availability_for_coldkeys( + vec![coldkey], + Some(vec![netuid, nonexistent]), + ); + assert_eq!(result.len(), 1); + let subnets = result.get(&coldkey).unwrap(); + assert_eq!(subnets.len(), 1); + assert!(subnets.contains_key(&netuid)); + assert!(!subnets.contains_key(&nonexistent)); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_rejects_oversized_netuid_list() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let subnet_count = SubtensorModule::get_all_subnet_netuids().len(); + let requested: Vec = (0..=subnet_count as u16) + .map(subtensor_runtime_common::NetUid::from) + .collect(); + + let result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(requested)); + assert_eq!(result.len(), 1); + assert!(result.contains_key(&coldkey)); + assert!(result.get(&coldkey).unwrap().is_empty()); + + let result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(vec![netuid])); + assert_eq!(result.get(&coldkey).unwrap().len(), 1); + assert!(result.get(&coldkey).unwrap().contains_key(&netuid)); + }); +} + +#[test] +fn test_stake_availability_for_coldkeys_uses_rolled_forward_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey, netuid); + let lock_amount = total / 2.into(); + + DecayingLock::::remove(coldkey, netuid); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + let raw_lock = Lock::::get((coldkey, netuid, hotkey)).unwrap(); + + step_block(1000); + + let result = + SubtensorModule::get_stake_availability_for_coldkeys(vec![coldkey], Some(vec![netuid])); + let availability = result.get(&coldkey).unwrap().get(&netuid).unwrap(); + let rolled_locked = SubtensorModule::get_current_locked(&coldkey, netuid); + + assert!(rolled_locked < raw_lock.locked_mass); + assert_eq!(availability.locked(), rolled_locked); + assert_eq!(availability.available(), total - rolled_locked); + }); +} + // ========================================================================= // GROUP 3: Incremental locks (top-up) // ========================================================================= @@ -1011,6 +1315,83 @@ fn test_lock_stake_topup_same_block() { }); } +#[test] +fn test_locking_coldkeys_added_once_by_lock_stake() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + 100u64.into(), + )); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + 50u64.into(), + )); + + assert!(LockingColdkeys::::contains_key(( + netuid, hotkey, coldkey + ))); + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, hotkey)).count(), + 1 + ); + }); +} + +#[test] +fn test_locking_coldkeys_removed_when_lock_is_fully_reduced() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let amount = 100u64.into(); + + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, netuid, &hotkey, amount + )); + assert!(LockingColdkeys::::contains_key(( + netuid, hotkey, coldkey + ))); + + SubtensorModule::force_reduce_lock(&coldkey, netuid, amount); + + assert!(Lock::::get((coldkey, netuid, hotkey)).is_none()); + assert!(!LockingColdkeys::::contains_key(( + netuid, hotkey, coldkey + ))); + }); +} + +#[test] +fn test_lock_state_is_zero_uses_dust_threshold() { + let below_threshold = LockState { + locked_mass: AlphaBalance::from(99u64), + conviction: U64F64::from_num(99), + last_update: 0, + }; + let locked_mass_at_threshold = LockState { + locked_mass: AlphaBalance::from(100u64), + conviction: U64F64::from_num(99), + last_update: 0, + }; + let conviction_at_threshold = LockState { + locked_mass: AlphaBalance::from(99u64), + conviction: U64F64::from_num(100), + last_update: 0, + }; + + assert!(below_threshold.is_zero()); + assert!(!locked_mass_at_threshold.is_zero()); + assert!(!conviction_at_threshold.is_zero()); +} + // ========================================================================= // GROUP 4: Lock rejection cases // ========================================================================= @@ -1173,7 +1554,8 @@ fn test_roll_forward_individual_lock_uses_lock_owner_and_decay_mode() { MaturityRate::::get(), true, false, - ); + ) + .0; assert_eq!(rolled, expected); }); @@ -1197,7 +1579,8 @@ fn test_roll_forward_hotkey_lock_uses_perpetual_general_mode() { MaturityRate::::get(), false, true, - ); + ) + .0; assert_eq!(rolled, expected); }); @@ -1221,7 +1604,8 @@ fn test_roll_forward_decaying_hotkey_lock_uses_decaying_general_mode() { MaturityRate::::get(), false, false, - ); + ) + .0; assert_eq!(rolled, expected); }); @@ -1496,6 +1880,23 @@ fn test_roll_forward_conviction_converges_to_zero() { }); } +#[test] +fn test_roll_forward_normalizes_dust_to_zero() { + new_test_ext(1).execute_with(|| { + let lock = LockState { + locked_mass: 99u64.into(), + conviction: U64F64::from_num(99), + last_update: 100, + }; + + let rolled = roll_forward_lock(lock, 100, false, false); + + assert_eq!(rolled.locked_mass, AlphaBalance::ZERO); + assert_eq!(rolled.conviction, U64F64::from_num(0)); + assert_eq!(rolled.last_update, 100); + }); +} + #[test] fn test_roll_forward_no_change_when_now_equals_last_update() { new_test_ext(1).execute_with(|| { @@ -1564,6 +1965,158 @@ fn test_unstake_allowed_up_to_available() { }); } +#[test] +fn test_unstake_rolls_forward_existing_lock() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); + let lock_amount = AlphaBalance::from(1_000_000_000u64); + + DecayingLock::::remove(coldkey, netuid); + let lock_block = SubtensorModule::get_current_block_as_u64(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey, + netuid, + &hotkey, + lock_amount, + )); + + step_block(100); + let now = SubtensorModule::get_current_block_as_u64(); + let expected = roll_forward_decaying_hotkey_lock( + LockState { + locked_mass: lock_amount, + conviction: U64F64::from_num(0), + last_update: lock_block, + }, + now, + ); + + assert_ok!(SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + lock_amount, + )); + + assert_eq!( + Lock::::get((coldkey, netuid, hotkey)).expect("lock should remain"), + expected + ); + let aggregate = + DecayingHotkeyLock::::get(netuid, hotkey).expect("aggregate should remain"); + assert_eq!(aggregate.locked_mass, expected.locked_mass); + assert_eq!(aggregate.last_update, now); + }); +} + +#[test] +fn test_unstake_roll_forward_collects_decaying_lock_dust_from_hotkey_aggregate() { + new_test_ext(1).execute_with(|| { + const ONE_ALPHA: u64 = 1_000_000_000; + const DUST_ALPHA: u64 = 100; + const STAKE_TAO_RAO: u64 = 1_000 * 1_000_000_000; + + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let coldkey_1 = U256::from(2001); + let coldkey_2 = U256::from(2002); + let hotkey_1 = U256::from(3001); + let hotkey_2 = U256::from(3002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + setup_reserves( + netuid, + (STAKE_TAO_RAO * 1_000).into(), + (STAKE_TAO_RAO * 10_000).into(), + ); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey_1, &hotkey_1 + )); + assert_ok!(SubtensorModule::create_account_if_non_existent( + &coldkey_1, &hotkey_2 + )); + + for coldkey in [coldkey_1, coldkey_2] { + add_balance_to_coldkey_account(&coldkey, STAKE_TAO_RAO.into()); + SubtensorModule::stake_into_subnet( + &hotkey_1, + &coldkey, + netuid, + STAKE_TAO_RAO.into(), + ::SwapInterface::max_price(), + false, + ) + .unwrap(); + } + + let lock_block = SubtensorModule::get_current_block_as_u64(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey_1, + netuid, + &hotkey_2, + ONE_ALPHA.into(), + )); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey_2, + netuid, + &hotkey_2, + DUST_ALPHA.into(), + )); + + assert_eq!( + DecayingHotkeyLock::::get(netuid, hotkey_2) + .expect("decaying aggregate should exist") + .locked_mass, + AlphaBalance::from(ONE_ALPHA + DUST_ALPHA) + ); + + step_block(100); + let now = SubtensorModule::get_current_block_as_u64(); + let rolled_large_lock = roll_forward_decaying_hotkey_lock( + LockState { + locked_mass: ONE_ALPHA.into(), + conviction: U64F64::from_num(0), + last_update: lock_block, + }, + now, + ); + + assert_ok!(SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey_1), + hotkey_1, + netuid, + ONE_ALPHA.into(), + )); + assert_eq!( + Lock::::get((coldkey_1, netuid, hotkey_2)).expect("coldkey1 lock should remain"), + rolled_large_lock + ); + assert_eq!( + DecayingHotkeyLock::::get(netuid, hotkey_2) + .expect("decaying aggregate should remain") + .locked_mass, + rolled_large_lock + .locked_mass + .saturating_add(AlphaBalance::from(DUST_ALPHA)) + ); + + assert_ok!(SubtensorModule::do_remove_stake( + RuntimeOrigin::signed(coldkey_2), + hotkey_1, + netuid, + ONE_ALPHA.into(), + )); + assert_eq!( + DecayingHotkeyLock::::get(netuid, hotkey_2) + .expect("decaying aggregate should remain") + .locked_mass, + rolled_large_lock.locked_mass + ); + }); +} + #[test] fn test_unstake_blocked_by_lock() { new_test_ext(1).execute_with(|| { @@ -1904,7 +2457,6 @@ fn test_lock_on_multiple_subnets() { 100_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); DecayingLock::::insert(coldkey, netuid_b, false); @@ -1973,7 +2525,6 @@ fn test_unstake_one_subnet_does_not_affect_other() { 100_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -2047,7 +2598,6 @@ fn test_hotkey_conviction_multiple_lockers() { 50_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -2101,7 +2651,6 @@ fn test_mixed_perpetual_owner_and_decaying_non_owner_locks_roll_forward() { 100_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -2171,7 +2720,6 @@ fn test_total_conviction_equals_sum_of_participating_aggregate_convictions() { 100_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -2231,7 +2779,6 @@ fn test_total_conviction_equals_sum_of_individual_lock_convictions_for_many_lock 50_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); lockers.push((coldkey, hotkey)); @@ -2310,7 +2857,6 @@ fn test_subnet_king_highest_conviction_wins() { 50_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -2527,6 +3073,7 @@ fn test_swap_hotkey_locks_moves_owner_hotkey_aggregate_to_owner_lock() { last_update: now, }, ); + SubtensorModule::add_locking_coldkey(&old_owner_hotkey, netuid, &locking_coldkey); OwnerLock::::insert( netuid, LockState { @@ -2546,6 +3093,16 @@ fn test_swap_hotkey_locks_moves_owner_hotkey_aggregate_to_owner_lock() { OwnerLock::::get(netuid).unwrap().locked_mass, 500u64.into() ); + assert!(!LockingColdkeys::::contains_key(( + netuid, + old_owner_hotkey, + locking_coldkey + ))); + assert!(LockingColdkeys::::contains_key(( + netuid, + new_owner_hotkey, + locking_coldkey + ))); }); } @@ -2649,8 +3206,8 @@ fn test_reduce_lock_partial_reduction() { let coldkey = U256::from(1); let hotkey = U256::from(2); let netuid = setup_subnet_with_stake(coldkey, hotkey, 100_000_000_000); - let lock_amount = AlphaBalance::from(100u64); - let reduce_amount = AlphaBalance::from(40u64); + let lock_amount = AlphaBalance::from(1_000u64); + let reduce_amount = AlphaBalance::from(400u64); let now = SubtensorModule::get_current_block_as_u64(); assert_ok!(SubtensorModule::do_lock_stake( @@ -2660,7 +3217,7 @@ fn test_reduce_lock_partial_reduction() { lock_amount, )); - let conviction = U64F64::from_num(100); + let conviction = U64F64::from_num(1_000); Lock::::insert( (coldkey, netuid, hotkey), LockState { @@ -2682,15 +3239,19 @@ fn test_reduce_lock_partial_reduction() { SubtensorModule::force_reduce_lock(&coldkey, netuid, reduce_amount); let lock = Lock::::get((coldkey, netuid, hotkey)).expect("lock should remain"); - assert_eq!(lock.locked_mass, 60u64.into()); - assert_abs_diff_eq!(lock.conviction.to_num::(), 60., epsilon = 0.0000000001); + assert_eq!(lock.locked_mass, 600u64.into()); + assert_abs_diff_eq!( + lock.conviction.to_num::(), + 600., + epsilon = 0.0000000001 + ); let hotkey_lock = HotkeyLock::::get(netuid, hotkey).expect("hotkey lock should remain"); - assert_eq!(hotkey_lock.locked_mass, 60u64.into()); + assert_eq!(hotkey_lock.locked_mass, 600u64.into()); assert_abs_diff_eq!( hotkey_lock.conviction.to_num::(), - 60., + 600., epsilon = 0.0000000001 ); }); @@ -2731,7 +3292,6 @@ fn test_reduce_lock_two_coldkeys() { 100_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); DecayingLock::::insert(coldkey2, netuid, false); @@ -2805,16 +3365,16 @@ fn test_force_reduce_lock_does_not_over_reduce_hotkey_lock() { Lock::::insert( (coldkey1, netuid, hotkey), LockState { - locked_mass: 1u64.into(), - conviction: U64F64::from_num(10), + locked_mass: 1_000u64.into(), + conviction: U64F64::from_num(1_000), last_update: now, }, ); Lock::::insert( (coldkey2, netuid, hotkey), LockState { - locked_mass: 50u64.into(), - conviction: U64F64::from_num(20), + locked_mass: 5_000u64.into(), + conviction: U64F64::from_num(2_000), last_update: now, }, ); @@ -2822,21 +3382,21 @@ fn test_force_reduce_lock_does_not_over_reduce_hotkey_lock() { netuid, hotkey, LockState { - locked_mass: 51u64.into(), - conviction: U64F64::from_num(30), + locked_mass: 6_000u64.into(), + conviction: U64F64::from_num(3_000), last_update: now, }, ); - SubtensorModule::force_reduce_lock(&coldkey1, netuid, 20u64.into()); + SubtensorModule::force_reduce_lock(&coldkey1, netuid, 2_000u64.into()); assert!(Lock::::get((coldkey1, netuid, hotkey)).is_none()); assert!(Lock::::get((coldkey2, netuid, hotkey)).is_some()); let hotkey_lock = HotkeyLock::::get(netuid, hotkey).expect("hotkey lock should remain"); - assert_eq!(hotkey_lock.locked_mass, 50u64.into()); - assert_eq!(hotkey_lock.conviction, U64F64::from_num(20)); + assert_eq!(hotkey_lock.locked_mass, 5_000u64.into()); + assert_eq!(hotkey_lock.conviction, U64F64::from_num(2_000)); }); } @@ -2872,8 +3432,12 @@ fn test_coldkey_swap_swaps_lock() { .next() .is_none() ); + assert!(!DecayingLock::::contains_key(old_coldkey, netuid)); // New coldkey now has the lock assert!(Lock::::get((new_coldkey, netuid, hotkey)).is_some()); + assert_eq!(DecayingLock::::get(new_coldkey, netuid), Some(false)); + assert!(HotkeyLock::::contains_key(netuid, hotkey)); + assert!(!DecayingHotkeyLock::::contains_key(netuid, hotkey)); }); } @@ -2927,8 +3491,8 @@ fn test_coldkey_swap_allows_destination_conviction_only_lock() { let new_hotkey = U256::from(20); let netuid = subtensor_runtime_common::NetUid::from(1); - let old_conviction = U64F64::from_num(77); - let new_conviction = U64F64::from_num(11); + let old_conviction = U64F64::from_num(777); + let new_conviction = U64F64::from_num(111); SubtensorModule::insert_lock_state( &old_coldkey, @@ -3111,7 +3675,7 @@ fn test_failed_coldkey_swap_extrinsic_rolls_back_state_changes() { netuid, &blocked_hotkey, LockState { - locked_mass: 1u64.into(), + locked_mass: 1_000u64.into(), conviction: U64F64::from_num(0), last_update: SubtensorModule::get_current_block_as_u64(), }, @@ -3167,6 +3731,13 @@ fn test_hotkey_swap_swaps_locks_and_convictions() { &old_hotkey, 5000u64.into(), )); + assert!(LockingColdkeys::::contains_key(( + netuid, old_hotkey, coldkey + ))); + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, old_hotkey)).count(), + 1 + ); // Mock a non-zero conviction let mut lock = Lock::::get((coldkey, netuid, old_hotkey)).unwrap(); @@ -3190,6 +3761,12 @@ fn test_hotkey_swap_swaps_locks_and_convictions() { let lock = Lock::::get((coldkey, netuid, new_hotkey)).unwrap(); assert_eq!(lock.locked_mass, 5000u64.into()); assert!(lock.conviction > U64F64::from_num(0)); + assert!(!LockingColdkeys::::contains_key(( + netuid, old_hotkey, coldkey + ))); + assert!(LockingColdkeys::::contains_key(( + netuid, new_hotkey, coldkey + ))); // Hotkey lock data also updated, conviction is not reset let hotkey_lock = HotkeyLock::::get(netuid, new_hotkey).unwrap(); @@ -3415,7 +3992,6 @@ fn test_clear_small_nomination_checks_lock() { 50_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -3485,7 +4061,6 @@ fn test_clear_small_nomination_reduces_only_tiny_amount_from_lock_state() { large_tao, ::SwapInterface::max_price(), false, - false, ) .unwrap(); SubtensorModule::stake_into_subnet( @@ -3495,7 +4070,6 @@ fn test_clear_small_nomination_reduces_only_tiny_amount_from_lock_state() { tiny_tao, ::SwapInterface::max_price(), false, - false, ) .unwrap(); DecayingLock::::insert(nominator, netuid, false); @@ -3624,7 +4198,7 @@ fn test_epoch_distribution_auto_locks_owner_cut() { let subnet_tempo = 10; let stake = 100_000_000_000u64; - SubtensorModule::set_tempo(netuid, subnet_tempo); + SubtensorModule::set_tempo_unchecked(netuid, subnet_tempo); SubtensorModule::set_ck_burn(0); setup_reserves(netuid, (stake * 10_000).into(), (stake * 10_000).into()); @@ -3688,7 +4262,7 @@ fn test_epoch_distribution_auto_locks_owner_cut() { ); // Advance to the next epoch so owner cut is distributed and auto-locked. - step_block(subnet_tempo); + step_epochs(1, netuid); let owner_stake_after = get_alpha(&subnet_owner_hotkey, &subnet_owner_coldkey, netuid); let owner_cut_locked = owner_stake_after - owner_stake_before; @@ -3901,7 +4475,6 @@ fn test_moving_partial_lock() { 50_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); DecayingLock::::insert(coldkey2, netuid, false); @@ -3986,7 +4559,6 @@ fn test_moving_partial_lock_same_owners() { 50_000_000_000u64.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); DecayingLock::::insert(coldkey2, netuid, false); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 0ab260ebc3..ec24697522 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -1321,6 +1321,130 @@ fn test_migrate_remove_add_stake_burn_rate_limit() { }); } +#[test] +fn test_migrate_populate_locking_coldkeys() { + new_test_ext(1).execute_with(|| { + const MIGRATION_NAME: &[u8] = b"migrate_populate_locking_coldkeys"; + + let netuid = NetUid::from(1); + let coldkey_1 = U256::from(1001); + let coldkey_2 = U256::from(1002); + let hotkey = U256::from(2001); + let expired_hotkey = U256::from(2002); + + Lock::::insert( + (coldkey_1, netuid, hotkey), + LockState { + locked_mass: AlphaBalance::from(1_000_u64), + conviction: U64F64::from_num(0), + last_update: 1, + }, + ); + Lock::::insert( + (coldkey_2, netuid, hotkey), + LockState { + locked_mass: AlphaBalance::from(2_000_u64), + conviction: U64F64::from_num(0), + last_update: 1, + }, + ); + Lock::::insert( + (coldkey_1, netuid, expired_hotkey), + LockState { + locked_mass: AlphaBalance::ZERO, + conviction: U64F64::from_num(1), + last_update: 1, + }, + ); + + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, hotkey)).count(), + 0 + ); + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, expired_hotkey)).count(), + 0 + ); + assert!(!HasMigrationRun::::get(MIGRATION_NAME.to_vec())); + + let weight = + crate::migrations::migrate_populate_locking_coldkeys::migrate_populate_locking_coldkeys::(); + + assert!(!weight.is_zero(), "migration weight should be non-zero"); + assert!(LockingColdkeys::::contains_key(( + netuid, hotkey, coldkey_1 + ))); + assert!(LockingColdkeys::::contains_key(( + netuid, hotkey, coldkey_2 + ))); + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, hotkey)).count(), + 2 + ); + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, expired_hotkey)).count(), + 0 + ); + assert!(Lock::::get((coldkey_1, netuid, expired_hotkey)).is_none()); + assert!(HasMigrationRun::::get(MIGRATION_NAME.to_vec())); + + let _ = LockingColdkeys::::clear_prefix((netuid, hotkey), u32::MAX, None); + let second_weight = + crate::migrations::migrate_populate_locking_coldkeys::migrate_populate_locking_coldkeys::(); + + assert_eq!( + second_weight, + ::DbWeight::get().reads(1), + "second run should only read the migration flag" + ); + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, hotkey)).count(), + 0 + ); + }); +} + +#[test] +fn test_migrate_populate_locking_coldkeys_removes_dust_from_aggregate() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + let coldkey_1 = U256::from(1101); + let coldkey_2 = U256::from(1102); + let hotkey = U256::from(2101); + let dust_lock = LockState { + locked_mass: AlphaBalance::from(60_u64), + conviction: U64F64::from_num(0), + last_update: 1, + }; + + DecayingLock::::insert(coldkey_1, netuid, false); + DecayingLock::::insert(coldkey_2, netuid, false); + Lock::::insert((coldkey_1, netuid, hotkey), dust_lock.clone()); + Lock::::insert((coldkey_2, netuid, hotkey), dust_lock); + HotkeyLock::::insert( + netuid, + hotkey, + LockState { + locked_mass: AlphaBalance::from(120_u64), + conviction: U64F64::from_num(0), + last_update: 1, + }, + ); + + crate::migrations::migrate_populate_locking_coldkeys::migrate_populate_locking_coldkeys::< + Test, + >(); + + assert!(Lock::::get((coldkey_1, netuid, hotkey)).is_none()); + assert!(Lock::::get((coldkey_2, netuid, hotkey)).is_none()); + assert!(HotkeyLock::::get(netuid, hotkey).is_none()); + assert_eq!( + LockingColdkeys::::iter_prefix((netuid, hotkey)).count(), + 0 + ); + }); +} + #[test] fn test_migrate_fix_staking_hot_keys() { new_test_ext(1).execute_with(|| { @@ -2800,9 +2924,11 @@ fn test_migrate_reset_unactive_sn() { PendingRootAlphaDivs::::get(netuid), AlphaBalance::ZERO ); - assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( - netuid - )); + assert_eq!( + // not modified + RAORecycledForRegistration::::get(netuid), + *rao_recycled_before.get(&netuid).unwrap() + ); assert_eq!(PendingOwnerCut::::get(netuid), AlphaBalance::ZERO); assert_ne!(SubnetTAO::::get(netuid), initial_tao); assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); @@ -2884,9 +3010,6 @@ fn test_migrate_reset_unactive_sn() { SubnetAlphaOutEmission::::get(netuid), AlphaBalance::ZERO ); - assert!(pallet_subtensor_swap::AlphaSqrtPrice::::contains_key( - netuid - )); assert_ne!(PendingOwnerCut::::get(netuid), AlphaBalance::ZERO); assert_ne!(SubnetTAO::::get(netuid), initial_tao); assert_ne!(SubnetAlphaIn::::get(netuid), initial_alpha); @@ -3052,6 +3175,54 @@ fn test_migrate_remove_unknown_neuron_axon_cert_prom() { } } +// cargo test --package pallet-subtensor --lib -- tests::migration::test_migrate_cleanup_swap_v3 --exact --nocapture +#[test] +fn test_migrate_cleanup_swap_v3() { + use crate::migrations::migrate_cleanup_swap_v3::deprecated_swap_maps; + use substrate_fixed::types::U64F64; + + new_test_ext(1).execute_with(|| { + let migration = crate::migrations::migrate_cleanup_swap_v3::migrate_cleanup_swap_v3::; + + const MIGRATION_NAME: &str = "migrate_cleanup_swap_v3"; + + let provided: u64 = 9876; + let reserves: u64 = 1_000_000; + + SubnetTAO::::insert(NetUid::from(1), TaoBalance::from(reserves)); + SubnetAlphaIn::::insert(NetUid::from(1), AlphaBalance::from(reserves)); + + // Insert deprecated maps values + deprecated_swap_maps::SubnetTaoProvided::::insert( + NetUid::from(1), + TaoBalance::from(provided), + ); + deprecated_swap_maps::SubnetAlphaInProvided::::insert( + NetUid::from(1), + AlphaBalance::from(provided), + ); + + // Run migration + let weight = migration(); + + // Test that values are removed from state + assert!(!deprecated_swap_maps::SubnetTaoProvided::::contains_key(NetUid::from(1)),); + assert!( + !deprecated_swap_maps::SubnetAlphaInProvided::::contains_key(NetUid::from(1)), + ); + + // Provided got added to reserves + assert_eq!( + u64::from(SubnetTAO::::get(NetUid::from(1))), + reserves + provided + ); + assert_eq!( + u64::from(SubnetAlphaIn::::get(NetUid::from(1))), + reserves + provided + ); + }); +} + #[test] fn test_migrate_coldkey_swap_scheduled_to_announcements() { new_test_ext(1000).execute_with(|| { @@ -4647,3 +4818,119 @@ fn test_migrate_reset_tnet_conviction_locks() { ); }); } + +#[test] +fn test_migrate_dynamic_tempo_aligns_first_post_upgrade_fire() { + new_test_ext(1).execute_with(|| { + const MIGRATION_NAME: &str = "dynamic_tempo_v1"; + let netuid = NetUid::from(7u16); + let tempo: u16 = 360; + + add_network(netuid, tempo, 0); + let current_block = 1234u64; + run_to_block(current_block); + + // Compute next-fire block + let netuid_plus_one = (u16::from(netuid) as u64) + 1; + let tempo_plus_one = (tempo as u64) + 1; + let adjusted = current_block + netuid_plus_one; + let remainder = adjusted % tempo_plus_one; + let legacy_blocks_until_next = (tempo as u64) - remainder; + let expected_next_fire = current_block + legacy_blocks_until_next; + + crate::migrations::migrate_dynamic_tempo::migrate_dynamic_tempo::(); + + // New formula: next fire = LastEpochBlock + tempo. + let last_epoch = LastEpochBlock::::get(netuid); + assert_eq!( + last_epoch + tempo as u64, + expected_next_fire, + "back-fill should make new scheduler fire at the same block as legacy modulo" + ); + assert!(HasMigrationRun::::get( + MIGRATION_NAME.as_bytes().to_vec() + )); + }); +} + +#[test] +fn test_migrate_dynamic_tempo_preserves_non_standard_tempo() { + new_test_ext(1).execute_with(|| { + // Three subnets — one standard, two with non-standard tempo + // (simulates the 2 mainnet subnets root configured outside MIN/MAX bounds). + let standard = NetUid::from(1u16); + let small = NetUid::from(2u16); + let large = NetUid::from(3u16); + + add_network(standard, 360, 0); + add_network(small, 10, 0); // < MIN_TEMPO (360) + add_network(large, 60_000, 0); // > MAX_TEMPO (50_400) + + crate::migrations::migrate_dynamic_tempo::migrate_dynamic_tempo::(); + + // Tempo values preserved as-is — no clamp. + assert_eq!(Tempo::::get(standard), 360); + assert_eq!(Tempo::::get(small), 10); + assert_eq!(Tempo::::get(large), 60_000); + + // All non-zero tempos got LastEpochBlock seeded. + assert!(LastEpochBlock::::contains_key(standard)); + assert!(LastEpochBlock::::contains_key(small)); + assert!(LastEpochBlock::::contains_key(large)); + }); +} + +#[test] +fn test_migrate_dynamic_tempo_activity_cutoff_round_trips_production_values() { + new_test_ext(1).execute_with(|| { + // (cutoff_blocks, tempo) combinations from production data. + let cases: [(u16, u16); 6] = [ + (5000, 360), + (6000, 360), + (7200, 360), + (12000, 360), + (1000, 360), + (360, 360), + ]; + + for (i, &(cutoff, tempo)) in cases.iter().enumerate() { + let netuid = NetUid::from((i + 1) as u16); + add_network(netuid, tempo, 0); + ActivityCutoff::::insert(netuid, cutoff); + } + + crate::migrations::migrate_dynamic_tempo::migrate_dynamic_tempo::(); + + for (i, &(cutoff, _)) in cases.iter().enumerate() { + let netuid = NetUid::from((i + 1) as u16); + // get_activity_cutoff_blocks = factor * tempo / 1000 must equal original cutoff exactly. + assert_eq!( + crate::Pallet::::get_activity_cutoff_blocks(netuid), + cutoff as u64, + "ceiling division must round-trip cutoff exactly for netuid {}", + u16::from(netuid) + ); + } + }); +} + +#[test] +fn test_migrate_dynamic_tempo_idempotent() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + add_network(netuid, 360, 0); + + crate::migrations::migrate_dynamic_tempo::migrate_dynamic_tempo::(); + let last_epoch_first = LastEpochBlock::::get(netuid); + + // Mutate state to verify second run is a no-op. + run_to_block(crate::Pallet::::get_current_block_as_u64() + 100); + crate::migrations::migrate_dynamic_tempo::migrate_dynamic_tempo::(); + + assert_eq!( + LastEpochBlock::::get(netuid), + last_epoch_first, + "second migration call must be a no-op" + ); + }); +} diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 277162dde4..0e3347ec89 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -9,7 +9,7 @@ use core::num::NonZeroU64; use crate::utils::rate_limiting::TransactionType; use crate::*; pub use frame_support::traits::Imbalance; -use frame_support::traits::{Contains, Everything, InherentBuilder, InsideBoth, InstanceFilter}; +use frame_support::traits::{Contains, Everything, InsideBoth, InstanceFilter}; use frame_support::weights::Weight; use frame_support::weights::constants::RocksDbWeight; use frame_support::{PalletId, derive_impl}; @@ -18,7 +18,7 @@ use frame_support::{ traits::{Hooks, PrivilegeCmp}, }; use frame_system as system; -use frame_system::{EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase}; +use frame_system::{EnsureRoot, RawOrigin, limits}; use pallet_subtensor_proxy as pallet_proxy; use pallet_subtensor_utility as pallet_utility; use share_pool::SafeFloat; @@ -157,7 +157,14 @@ impl system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; - type DispatchExtension = crate::CheckColdkeySwap; + type DispatchExtension = ( + crate::CheckColdkeySwap, + crate::CheckWeights, + crate::CheckRateLimits, + crate::CheckDelegateTake, + crate::CheckServingEndpoints, + crate::CheckEvmKeyAssociation, + ); } parameter_types! { @@ -214,6 +221,10 @@ parameter_types! { 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 MinTempo: u16 = crate::MIN_TEMPO; + pub const MaxTempo: u16 = crate::MAX_TEMPO; + pub const MinActivityCutoffFactorMilli: u32 = crate::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const MaxActivityCutoffFactorMilli: u32 = crate::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const InitialValidatorPruneLen: u64 = 0; pub const InitialScalingLawPower: u16 = 50; pub const InitialMaxAllowedValidators: u16 = 100; @@ -253,6 +264,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = 10; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const MaxEpochsPerBlock: u8 = 32; } impl crate::Config for Test { @@ -301,6 +313,10 @@ impl crate::Config for Test { type InitialMinStake = InitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = MinTempo; + type MaxTempo = MaxTempo; + type MinActivityCutoffFactorMilli = MinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = MaxActivityCutoffFactorMilli; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; @@ -332,6 +348,7 @@ impl crate::Config for Test { type AuthorshipProvider = MockAuthorshipProvider; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = MaxEpochsPerBlock; type WeightInfo = (); } @@ -339,7 +356,6 @@ impl crate::Config for Test { 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(); } @@ -351,7 +367,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = TaoBalanceReserve; type AlphaReserve = AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); @@ -564,28 +579,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), ())) + UncheckedExtrinsic::new_bare(call) } } @@ -696,9 +695,9 @@ pub(crate) fn next_block_no_epoch(netuid: NetUid) -> u64 { let high_tempo: u16 = u16::MAX - 1; let old_tempo: u16 = SubtensorModule::get_tempo(netuid); - SubtensorModule::set_tempo(netuid, high_tempo); + SubtensorModule::set_tempo_unchecked(netuid, high_tempo); let new_block = next_block(); - SubtensorModule::set_tempo(netuid, old_tempo); + SubtensorModule::set_tempo_unchecked(netuid, old_tempo); new_block } @@ -709,27 +708,27 @@ pub(crate) fn run_to_block_no_epoch(netuid: NetUid, n: u64) { let high_tempo: u16 = u16::MAX - 1; let old_tempo: u16 = SubtensorModule::get_tempo(netuid); - SubtensorModule::set_tempo(netuid, high_tempo); + SubtensorModule::set_tempo_unchecked(netuid, high_tempo); run_to_block(n); - SubtensorModule::set_tempo(netuid, old_tempo); + SubtensorModule::set_tempo_unchecked(netuid, old_tempo); } #[allow(dead_code)] pub(crate) 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() - )); + const STEP_EPOCHS_MAX_BLOCKS: u32 = 50_000; + + // Advance block-by-block until exactly `count` more epoch slots have been + // consumed for `netuid`, observed via the `SubnetEpochIndex` counter. Robust + // to any tempo (including `tempo == 1`) and to the per-block epoch cap. + let target = crate::SubnetEpochIndex::::get(netuid) + count as u64; + let mut blocks_advanced: u32 = 0; + while crate::SubnetEpochIndex::::get(netuid) < target { step_block(1); + blocks_advanced += 1; + assert!( + blocks_advanced < STEP_EPOCHS_MAX_BLOCKS, + "step_epochs: epoch counter never advanced (tempo == 0?)" + ); } } @@ -756,8 +755,7 @@ pub fn register_ok_neuron( SubtensorModule::set_burn(netuid, TaoBalance::from(0)); let reserve: u64 = 1_000_000_000_000; let tao_reserve = SubnetTAO::::get(netuid); - let alpha_reserve = - SubnetAlphaIn::::get(netuid) + SubnetAlphaInProvided::::get(netuid); + let alpha_reserve = SubnetAlphaIn::::get(netuid); if tao_reserve.is_zero() && alpha_reserve.is_zero() { setup_reserves(netuid, reserve.into(), reserve.into()); @@ -996,7 +994,6 @@ pub fn increase_stake_on_coldkey_hotkey_account( tao_staked, ::SwapInterface::max_price(), false, - false, ) .unwrap(); } @@ -1016,10 +1013,6 @@ pub fn increase_stake_on_hotkey_account(hotkey: &U256, increment: TaoBalance, ne ); } -pub(crate) fn remove_stake_rate_limit_for_tests(hotkey: &U256, coldkey: &U256, netuid: NetUid) { - StakingOperationRateLimiter::::remove((hotkey, coldkey, netuid)); -} - pub(crate) fn setup_reserves(netuid: NetUid, tao: TaoBalance, alpha: AlphaBalance) { SubnetTAO::::set(netuid, tao); SubnetAlphaIn::::set(netuid, alpha); diff --git a/pallets/subtensor/src/tests/mock_high_ed.rs b/pallets/subtensor/src/tests/mock_high_ed.rs index 5f19edf455..6f5c69a89c 100644 --- a/pallets/subtensor/src/tests/mock_high_ed.rs +++ b/pallets/subtensor/src/tests/mock_high_ed.rs @@ -7,13 +7,13 @@ use core::num::NonZeroU64; use crate::*; -use frame_support::traits::{Everything, InherentBuilder, InstanceFilter}; +use frame_support::traits::{Everything, 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 frame_system::{EnsureRoot, limits}; use pallet_subtensor_proxy as pallet_proxy; use sp_core::{ConstU64, H256, U256, offchain::KeyTypeId}; use sp_runtime::Perbill; @@ -117,7 +117,14 @@ impl system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = u64; type Block = Block; - type DispatchExtension = crate::CheckColdkeySwap; + type DispatchExtension = ( + crate::CheckColdkeySwap, + crate::CheckWeights, + crate::CheckRateLimits, + crate::CheckDelegateTake, + crate::CheckServingEndpoints, + crate::CheckEvmKeyAssociation, + ); } parameter_types! { @@ -174,6 +181,10 @@ parameter_types! { 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 MinTempo: u16 = crate::MIN_TEMPO; + pub const MaxTempo: u16 = crate::MAX_TEMPO; + pub const MinActivityCutoffFactorMilli: u32 = crate::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const MaxActivityCutoffFactorMilli: u32 = crate::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const InitialValidatorPruneLen: u64 = 0; pub const InitialScalingLawPower: u16 = 50; pub const InitialMaxAllowedValidators: u16 = 100; @@ -213,6 +224,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = 10; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const MaxEpochsPerBlock: u8 = 32; } impl crate::Config for Test { @@ -261,6 +273,10 @@ impl crate::Config for Test { type InitialMinStake = InitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = MinTempo; + type MaxTempo = MaxTempo; + type MinActivityCutoffFactorMilli = MinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = MaxActivityCutoffFactorMilli; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; @@ -292,6 +308,7 @@ impl crate::Config for Test { type AuthorshipProvider = MockAuthorshipProvider; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = MaxEpochsPerBlock; type WeightInfo = (); } @@ -299,7 +316,6 @@ impl crate::Config for Test { 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(); } @@ -311,7 +327,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = TaoBalanceReserve; type AlphaReserve = AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); @@ -493,28 +508,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - Some(UncheckedExtrinsic::new_signed(call, nonce.into(), (), ())) + UncheckedExtrinsic::new_bare(call) } } diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index be37a9227b..2a9f71e021 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -32,7 +32,7 @@ mod swap_coldkey; mod swap_hotkey; mod swap_hotkey_with_subnet; mod tao; -mod transaction_extension_pays_no; +mod tempo_control; mod uids; mod voting_power; mod weights; diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs index a991df20a5..8265ec5e9d 100644 --- a/pallets/subtensor/src/tests/move_stake.rs +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -36,7 +36,6 @@ fn test_do_move_success() { stake_amount, ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -114,7 +113,6 @@ fn test_do_move_different_subnets() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -183,7 +181,6 @@ fn test_do_move_nonexistent_subnet() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -290,7 +287,6 @@ fn test_do_move_nonexistent_destination_hotkey() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -356,7 +352,6 @@ fn test_do_move_partial_stake() { total_stake.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -427,7 +422,6 @@ fn test_do_move_multiple_times() { initial_stake.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = @@ -439,7 +433,6 @@ fn test_do_move_multiple_times() { let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey1, &coldkey, netuid, ); - remove_stake_rate_limit_for_tests(&hotkey1, &coldkey, netuid); assert_ok!(SubtensorModule::do_move_stake( RuntimeOrigin::signed(coldkey), hotkey1, @@ -451,7 +444,6 @@ fn test_do_move_multiple_times() { let alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey2, &coldkey, netuid, ); - remove_stake_rate_limit_for_tests(&hotkey2, &coldkey, netuid); assert_ok!(SubtensorModule::do_move_stake( RuntimeOrigin::signed(coldkey), hotkey2, @@ -502,7 +494,6 @@ fn test_do_move_wrong_origin() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -570,7 +561,6 @@ fn test_do_move_same_hotkey_fails() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = @@ -622,7 +612,6 @@ fn test_do_move_event_emission() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -633,8 +622,9 @@ fn test_do_move_event_emission() { // Move stake and capture events System::reset_events(); - let current_price = - ::SwapInterface::current_alpha_price(netuid.into()); + let current_price = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); let tao_equivalent = (current_price * U96F32::from_num(alpha)).to_num::(); // no fee conversion assert_ok!(SubtensorModule::do_move_stake( RuntimeOrigin::signed(coldkey), @@ -684,7 +674,6 @@ fn test_do_move_storage_updates() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -752,7 +741,6 @@ fn test_move_full_amount_same_netuid() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -802,8 +790,8 @@ fn test_do_move_max_values() { let coldkey = U256::from(1); let origin_hotkey = U256::from(2); let destination_hotkey = U256::from(3); - let max_stake = u64::MAX; let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let max_stake = 20_000_000_000_000_000_u64; // Set up initial stake with maximum value let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); @@ -811,7 +799,7 @@ fn test_do_move_max_values() { add_balance_to_coldkey_account(&coldkey, max_stake.into()); // Add lots of liquidity to bypass low liquidity check - let reserve = u64::MAX / 1000; + let reserve = max_stake / 1000; mock::setup_reserves(netuid, reserve.into(), reserve.into()); SubtensorModule::stake_into_subnet( @@ -821,7 +809,6 @@ fn test_do_move_max_values() { max_stake.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -885,7 +872,6 @@ fn test_moving_too_little_unstakes() { (amount.to_u64() + fee * 2).into() )); - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, netuid); assert_err!( SubtensorModule::move_stake( RuntimeOrigin::signed(coldkey_account_id), @@ -925,7 +911,6 @@ fn test_do_transfer_success() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1035,7 +1020,6 @@ fn test_do_transfer_insufficient_stake() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1076,7 +1060,6 @@ fn test_do_transfer_wrong_origin() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1115,7 +1098,6 @@ fn test_do_transfer_minimum_stake_check() { stake_amount, ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1163,7 +1145,6 @@ fn test_do_transfer_different_subnets() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1230,7 +1211,6 @@ fn test_do_swap_success() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1339,7 +1319,6 @@ fn test_do_swap_insufficient_stake() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1375,7 +1354,6 @@ fn test_do_swap_wrong_origin() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1414,7 +1392,6 @@ fn test_do_swap_minimum_stake_check() { total_stake, ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1451,7 +1428,6 @@ fn test_do_swap_same_subnet() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1497,7 +1473,6 @@ fn test_do_swap_partial_stake() { total_stake_tao.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let total_stake_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1550,7 +1525,6 @@ fn test_do_swap_storage_updates() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1611,7 +1585,6 @@ fn test_do_swap_multiple_times() { initial_stake.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -1621,7 +1594,6 @@ fn test_do_swap_multiple_times() { &hotkey, &coldkey, netuid1, ); if !alpha1.is_zero() { - remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid1); assert_ok!(SubtensorModule::do_swap_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -1637,7 +1609,6 @@ fn test_do_swap_multiple_times() { let (tao_equivalent, _) = mock::swap_alpha_to_tao_ext(netuid2, alpha2, true); // we do this in the loop, because we need the value before the swap expected_alpha = mock::swap_tao_to_alpha(netuid1, tao_equivalent).0; - remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid2); assert_ok!(SubtensorModule::do_swap_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -1683,7 +1654,6 @@ fn test_do_swap_allows_non_owned_hotkey() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1775,7 +1745,6 @@ fn test_move_stake_specific_stake_into_subnet_fail() { // Move stake to destination subnet let (tao_equivalent, _) = mock::swap_alpha_to_tao_ext(origin_netuid, alpha_to_move, true); let (expected_value, _) = mock::swap_tao_to_alpha(netuid, tao_equivalent); - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, origin_netuid); assert_ok!(SubtensorModule::move_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -1808,57 +1777,11 @@ fn test_move_stake_specific_stake_into_subnet_fail() { } #[test] -fn test_transfer_stake_rate_limited() { - new_test_ext(1).execute_with(|| { - 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 origin_coldkey = U256::from(1); - let destination_coldkey = U256::from(2); - let hotkey = U256::from(3); - let stake_amount = DefaultMinStake::::get().to_u64() * 10; - - let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); - let _ = SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); - add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); - SubtensorModule::stake_into_subnet( - &hotkey, - &origin_coldkey, - netuid, - stake_amount.into(), - ::SwapInterface::max_price(), - true, - false, - ) - .unwrap(); - let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &origin_coldkey, - netuid, - ); - - assert_err!( - SubtensorModule::do_transfer_stake( - RuntimeOrigin::signed(origin_coldkey), - destination_coldkey, - hotkey, - netuid, - netuid, - alpha - ), - Error::::StakingOperationRateLimitExceeded - ); - }); -} - -#[test] -fn test_transfer_stake_doesnt_limit_destination_coldkey() { +fn test_transfer_stake_same_netuid_not_rate_limited() { new_test_ext(1).execute_with(|| { 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 netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let origin_coldkey = U256::from(1); let destination_coldkey = U256::from(2); @@ -1875,7 +1798,6 @@ fn test_transfer_stake_doesnt_limit_destination_coldkey() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1884,25 +1806,39 @@ fn test_transfer_stake_doesnt_limit_destination_coldkey() { netuid, ); + // add_stake set the limiter for (hotkey, origin_coldkey, netuid), but a same-netuid + // transfer performs no AMM swap (no price impact), so it is NOT rate limited assert_ok!(SubtensorModule::do_transfer_stake( RuntimeOrigin::signed(origin_coldkey), destination_coldkey, hotkey, netuid, - netuid2, + netuid, alpha - ),); + )); - assert!(!StakingOperationRateLimiter::::contains_key(( - hotkey, - destination_coldkey, - netuid2 - ))); + // The whole position was moved to the destination coldkey on the same subnet. + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &origin_coldkey, + netuid + ), + AlphaBalance::ZERO + ); + assert_ne!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &destination_coldkey, + netuid + ), + AlphaBalance::ZERO + ); }); } #[test] -fn test_swap_stake_limits_destination_netuid() { +fn test_transfer_stake_doesnt_limit_destination_coldkey() { new_test_ext(1).execute_with(|| { let subnet_owner_coldkey = U256::from(1001); let subnet_owner_hotkey = U256::from(1002); @@ -1910,10 +1846,12 @@ fn test_swap_stake_limits_destination_netuid() { let netuid2 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let origin_coldkey = U256::from(1); + let destination_coldkey = U256::from(2); let hotkey = U256::from(3); let stake_amount = DefaultMinStake::::get().to_u64() * 10; let _ = SubtensorModule::create_account_if_non_existent(&origin_coldkey, &hotkey); + let _ = SubtensorModule::create_account_if_non_existent(&destination_coldkey, &hotkey); add_balance_to_coldkey_account(&origin_coldkey, stake_amount.into()); SubtensorModule::stake_into_subnet( &hotkey, @@ -1922,7 +1860,6 @@ fn test_swap_stake_limits_destination_netuid() { stake_amount.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1931,24 +1868,13 @@ fn test_swap_stake_limits_destination_netuid() { netuid, ); - assert_ok!(SubtensorModule::do_swap_stake( + assert_ok!(SubtensorModule::do_transfer_stake( RuntimeOrigin::signed(origin_coldkey), + destination_coldkey, hotkey, netuid, netuid2, alpha ),); - - assert!(!StakingOperationRateLimiter::::contains_key(( - hotkey, - origin_coldkey, - netuid - ))); - - assert!(StakingOperationRateLimiter::::contains_key(( - hotkey, - origin_coldkey, - netuid2 - ))); }); } diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index d1be5f13f4..a967761ef5 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -267,8 +267,9 @@ fn dissolve_owner_cut_refund_logic() { // Use the current alpha price to estimate the TAO equivalent. let owner_emission_tao = { - let price: U96F32 = - ::SwapInterface::current_alpha_price(net.into()); + let price: U96F32 = U96F32::saturating_from_num( + ::SwapInterface::current_alpha_price(net.into()), + ); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -278,6 +279,8 @@ fn dissolve_owner_cut_refund_logic() { let expected_refund: TaoBalance = lock.saturating_sub(owner_emission_tao); + println!("expected_refund = {:?}", expected_refund); + let before = SubtensorModule::get_coldkey_balance(&oc); assert_ok!(SubtensorModule::do_dissolve_network(net)); let after = SubtensorModule::get_coldkey_balance(&oc); @@ -384,8 +387,6 @@ fn dissolve_clears_all_per_subnet_storages() { // Token / price / provided reserves TokenSymbol::::insert(net, b"XX".to_vec()); SubnetMovingPrice::::insert(net, substrate_fixed::types::I96F32::from_num(1)); - SubnetTaoProvided::::insert(net, TaoBalance::from(1)); - SubnetAlphaInProvided::::insert(net, AlphaBalance::from(1)); // TAO Flow SubnetTaoFlow::::insert(net, 0i64); @@ -548,8 +549,6 @@ fn dissolve_clears_all_per_subnet_storages() { // Token / price / provided reserves assert!(!TokenSymbol::::contains_key(net)); assert!(!SubnetMovingPrice::::contains_key(net)); - assert!(!SubnetTaoProvided::::contains_key(net)); - assert!(!SubnetAlphaInProvided::::contains_key(net)); // Subnet locks assert!(!TransferToggle::::contains_key(net)); @@ -976,6 +975,91 @@ fn destroy_alpha_out_multiple_stakers_pro_rata() { }); } +#[test] +fn destroy_alpha_in_out_stakes_cleans_locking_coldkeys() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(10); + let owner_hot = U256::from(20); + let netuid = add_dynamic_network(&owner_hot, &owner_cold); + remove_owner_registration_stake(netuid); + + let coldkey = U256::from(111); + let hotkey = U256::from(222); + let other_netuid = NetUid::from(u16::from(netuid) + 1); + let lock = LockState { + locked_mass: 10u64.into(), + conviction: U64F64::from_num(1), + last_update: 1, + }; + + Lock::::insert((coldkey, netuid, hotkey), lock.clone()); + LockingColdkeys::::insert((netuid, hotkey, coldkey), ()); + Lock::::insert((coldkey, other_netuid, hotkey), lock); + LockingColdkeys::::insert((other_netuid, hotkey, coldkey), ()); + + assert_ok!(SubtensorModule::destroy_alpha_in_out_stakes(netuid)); + + assert!(!Lock::::contains_key((coldkey, netuid, hotkey))); + assert!(!LockingColdkeys::::contains_key(( + netuid, hotkey, coldkey + ))); + assert!(Lock::::contains_key((coldkey, other_netuid, hotkey))); + assert!(LockingColdkeys::::contains_key(( + other_netuid, + hotkey, + coldkey + ))); + }); +} + +#[test] +fn destroy_alpha_in_out_stakes_cleans_all_lock_aggregates() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(10); + let owner_hot = U256::from(20); + let netuid = add_dynamic_network(&owner_hot, &owner_cold); + remove_owner_registration_stake(netuid); + + let coldkey = U256::from(111); + let hotkey = U256::from(222); + let other_netuid = NetUid::from(u16::from(netuid) + 1); + let lock = LockState { + locked_mass: 10u64.into(), + conviction: U64F64::from_num(1), + last_update: 1, + }; + + HotkeyLock::::insert(netuid, hotkey, lock.clone()); + DecayingHotkeyLock::::insert(netuid, hotkey, lock.clone()); + OwnerLock::::insert(netuid, lock.clone()); + DecayingOwnerLock::::insert(netuid, lock.clone()); + DecayingLock::::insert(coldkey, netuid, false); + + HotkeyLock::::insert(other_netuid, hotkey, lock.clone()); + DecayingHotkeyLock::::insert(other_netuid, hotkey, lock.clone()); + OwnerLock::::insert(other_netuid, lock.clone()); + DecayingOwnerLock::::insert(other_netuid, lock); + DecayingLock::::insert(coldkey, other_netuid, false); + + assert_ok!(SubtensorModule::destroy_alpha_in_out_stakes(netuid)); + + assert!(!HotkeyLock::::contains_key(netuid, hotkey)); + assert!(!DecayingHotkeyLock::::contains_key(netuid, hotkey)); + assert!(!OwnerLock::::contains_key(netuid)); + assert!(!DecayingOwnerLock::::contains_key(netuid)); + assert!(!DecayingLock::::contains_key(coldkey, netuid)); + + assert!(HotkeyLock::::contains_key(other_netuid, hotkey)); + assert!(DecayingHotkeyLock::::contains_key( + other_netuid, + hotkey + )); + assert!(OwnerLock::::contains_key(other_netuid)); + assert!(DecayingOwnerLock::::contains_key(other_netuid)); + assert!(DecayingLock::::contains_key(coldkey, other_netuid)); + }); +} + #[allow(clippy::indexing_slicing)] #[test] fn destroy_alpha_out_many_stakers_complex_distribution() { @@ -1083,8 +1167,9 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { let owner_emission_tao: u64 = { // Fallback matches the pallet's fallback - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); + let price: U96F32 = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -1164,8 +1249,9 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { .saturating_to_num::(); let owner_emission_tao_u64 = { - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); + let price: U96F32 = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); U96F32::from_num(owner_alpha_u64) .saturating_mul(price) .floor() @@ -2245,8 +2331,8 @@ fn massive_dissolve_refund_and_reregistration_flow_is_lossless_and_cleans_state( "subnet {net:?} still exists" ); assert!( - !pallet_subtensor_swap::SwapV3Initialized::::get(net), - "SwapV3Initialized still set" + !pallet_subtensor_swap::PalSwapInitialized::::get(net), + "PalSwapInitialized still set" ); } @@ -2462,10 +2548,13 @@ fn dissolve_clears_all_lock_maps_for_removed_network() { // --- Lock: (coldkey, netuid, hotkey) Lock::::insert((cold_1, net, hot_1), lock_a.clone()); + LockingColdkeys::::insert((net, hot_1, cold_1), ()); Lock::::insert((cold_2, net, hot_2), lock_b.clone()); + LockingColdkeys::::insert((net, hot_2, cold_2), ()); // Same cold/hot on another net should survive. Lock::::insert((cold_1, other_net, hot_1), lock_a.clone()); + LockingColdkeys::::insert((other_net, hot_1, cold_1), ()); // --- HotkeyLock HotkeyLock::::insert(net, hot_1, lock_a.clone()); @@ -2489,6 +2578,8 @@ fn dissolve_clears_all_lock_maps_for_removed_network() { // Sanity checks before dissolve assert!(Lock::::contains_key((cold_1, net, hot_1))); assert!(Lock::::contains_key((cold_2, net, hot_2))); + assert!(LockingColdkeys::::contains_key((net, hot_1, cold_1))); + assert!(LockingColdkeys::::contains_key((net, hot_2, cold_2))); assert!(HotkeyLock::::contains_key(net, hot_1)); assert!(HotkeyLock::::contains_key(net, hot_2)); @@ -2503,6 +2594,9 @@ fn dissolve_clears_all_lock_maps_for_removed_network() { // Sanity: other net keys are present before dissolve. assert!(Lock::::contains_key((cold_1, other_net, hot_1))); + assert!(LockingColdkeys::::contains_key(( + other_net, hot_1, cold_1 + ))); assert!(HotkeyLock::::contains_key(other_net, hot_1)); assert!(DecayingHotkeyLock::::contains_key(other_net, hot_1)); assert!(OwnerLock::::contains_key(other_net)); @@ -2514,6 +2608,8 @@ fn dissolve_clears_all_lock_maps_for_removed_network() { // Ensure removed assert!(!Lock::::contains_key((cold_1, net, hot_1))); assert!(!Lock::::contains_key((cold_2, net, hot_2))); + assert!(!LockingColdkeys::::contains_key((net, hot_1, cold_1))); + assert!(!LockingColdkeys::::contains_key((net, hot_2, cold_2))); assert!(!HotkeyLock::::contains_key(net, hot_1)); assert!(!HotkeyLock::::contains_key(net, hot_2)); @@ -2534,6 +2630,9 @@ fn dissolve_clears_all_lock_maps_for_removed_network() { // Ensure other_net is untouched assert!(Lock::::contains_key((cold_1, other_net, hot_1))); + assert!(LockingColdkeys::::contains_key(( + other_net, hot_1, cold_1 + ))); assert!(HotkeyLock::::contains_key(other_net, hot_1)); assert!(DecayingHotkeyLock::::contains_key(other_net, hot_1)); assert!(OwnerLock::::contains_key(other_net)); @@ -2541,13 +2640,13 @@ fn dissolve_clears_all_lock_maps_for_removed_network() { }); } -fn owner_alpha_from_lock_and_price(lock_cost_u64: u64, price: U96F32) -> u64 { - let alpha = (U96F32::from_num(lock_cost_u64) +fn owner_alpha_from_lock_and_price(lock_cost_u64: u64, price: U64F64) -> u64 { + let alpha = (U64F64::from_num(lock_cost_u64) .checked_div(price) .unwrap_or_default()) .floor(); - if alpha > U96F32::from_num(u64::MAX) { + if alpha > U64F64::from_num(u64::MAX) { u64::MAX } else { alpha.to_num::() @@ -2557,7 +2656,7 @@ fn owner_alpha_from_lock_and_price(lock_cost_u64: u64, price: U96F32) -> u64 { #[test] fn median_subnet_alpha_price_returns_one_when_no_eligible_subnet_prices() { new_test_ext(0).execute_with(|| { - let one = U96F32::from_num(1u64); + let one = U64F64::from_num(1u64); // Empty state. assert_eq!(SubtensorModule::get_median_subnet_alpha_price(), one); @@ -2573,7 +2672,7 @@ fn median_subnet_alpha_price_returns_one_when_no_eligible_subnet_prices() { setup_reserves(zero_netuid, TaoBalance::ZERO, AlphaBalance::from(100u64)); assert_eq!( ::SwapInterface::current_alpha_price(zero_netuid.into()), - U96F32::from_num(0u64) + U64F64::from_num(0u64) ); assert_eq!(SubtensorModule::get_median_subnet_alpha_price(), one); @@ -2749,11 +2848,6 @@ fn register_network_seeds_first_subnet_from_fallback_price_one_and_keeps_lock_in RAORecycledForRegistration::::get(new_netuid), expected_recycled ); - assert_eq!(SubnetTaoProvided::::get(new_netuid), TaoBalance::ZERO); - assert_eq!( - SubnetAlphaInProvided::::get(new_netuid), - AlphaBalance::ZERO - ); assert_eq!( ::SwapInterface::current_alpha_price(new_netuid.into()), diff --git a/pallets/subtensor/src/tests/serving.rs b/pallets/subtensor/src/tests/serving.rs index 2979d4438c..f63dcd5f81 100644 --- a/pallets/subtensor/src/tests/serving.rs +++ b/pallets/subtensor/src/tests/serving.rs @@ -2,17 +2,14 @@ use super::mock::*; use crate::Error; -use crate::extensions::SubtensorTransactionExtension; use crate::*; use frame_support::assert_noop; use frame_support::{ assert_ok, dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, }; -use frame_system::{Config, RawOrigin}; +use frame_system::Config; use sp_core::U256; -use sp_runtime::traits::{DispatchInfoOf, TransactionExtension, TxBaseImplication}; -use subtensor_runtime_common::CustomTransactionError; mod test { use std::net::{Ipv4Addr, Ipv6Addr}; @@ -1171,74 +1168,3 @@ fn test_set_subnet_identity_dispatch_info_ok() { assert_eq!(dispatch_info.pays_fee, Pays::Yes); }); } - -// cargo test --package pallet-subtensor --lib -- tests::serving::test_serve_axon_validate --exact --show-output -#[test] -fn test_serve_axon_validate() { - // Testing the signed extension validate function - // correctly filters the `serve_axon` transaction. - - new_test_ext(0).execute_with(|| { - let hotkey = U256::from(1); - let netuid = NetUid::from(1); - let version: u32 = 2; - let ip: u128 = 1676056785; - let port: u16 = 128; - let ip_type: u8 = 4; - let protocol: u8 = 0; - let placeholder1: u8 = 0; - let placeholder2: u8 = 0; - - // Serve axon bad call - let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_axon { - netuid, - version, - ip, - port, - ip_type, - protocol, - placeholder1, - placeholder2, - }); - - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - - let extension = SubtensorTransactionExtension::::new(); - // Submit to the signed extension validate function - let result_bad = extension.validate( - RawOrigin::Signed(hotkey).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - - // Should fail due to insufficient stake - assert_eq!( - result_bad.unwrap_err(), - CustomTransactionError::HotKeyNotRegisteredInNetwork.into() - ); - - // Register the hotkey in the subnet and try again - let coldkey = U256::from(1); - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, coldkey, 0); - - // Submit to the signed extension validate function - let result_ok = extension.validate( - RawOrigin::Signed(hotkey).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - - // Now the call passes - assert_ok!(result_ok); - }); -} diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 0fe951a29b..660b6957d7 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -6,7 +6,6 @@ use frame_support::dispatch::{DispatchClass, GetDispatchInfo, Pays}; use frame_support::sp_runtime::DispatchError; 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}; @@ -557,13 +556,7 @@ fn test_add_stake_partial_below_min_stake_fails() { mock::setup_reserves(netuid, (amount * 10).into(), (amount * 10).into()); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid, - TaoBalance::ZERO, - 1_000_000_000_000_u64.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid, None); // Get the current price let current_price = @@ -679,6 +672,7 @@ fn test_remove_stake_total_balance_no_change() { // Set fee rate to 0 so that alpha fee is not moved to block producer pallet_subtensor_swap::FeeRate::::insert(netuid, 0); + let fee: u64 = 0; // Clear any implicit existing stake so the test is deterministic let existing = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -707,10 +701,14 @@ fn test_remove_stake_total_balance_no_change() { amount.into(), ); - // Ensure SubnetTAO / TotalStake can pay out on remove - let (amount_tao, fee) = mock::swap_alpha_to_tao(netuid, amount.into()); - SubnetTAO::::mutate(netuid, |v| *v += amount_tao + fee.into()); - TotalStake::::mutate(|v| *v += amount_tao + fee.into()); + // Add subnet TAO for the equivalent amount added at price + let amount_tao = U96F32::from_num(amount) + * U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); + let amount_tao: TaoBalance = amount_tao.to_num::().into(); + SubnetTAO::::mutate(netuid, |v| *v += amount_tao); + TotalStake::::mutate(|v| *v += amount_tao); // Remove stake assert_ok!(SubtensorModule::remove_stake( @@ -741,8 +739,8 @@ fn test_remove_stake_total_balance_no_change() { ); assert_abs_diff_eq!( - total_balance_after.saturating_sub(total_balance_before), - amount_tao.saturating_sub(fee.into()), + total_balance_after - total_balance_before, + amount_tao - fee.into(), epsilon = TaoBalance::from(amount) / 1000.into() ); }); @@ -793,7 +791,7 @@ fn test_add_stake_insufficient_liquidity_one_side_ok() { add_balance_to_coldkey_account(&coldkey, amount_staked.into()); // Set the liquidity at lowest possible value so that all staking requests fail - let reserve_alpha = u64::from(mock::SwapMinimumReserve::get()); + let reserve_alpha = 1_000_000_000_u64; let reserve_tao = u64::from(mock::SwapMinimumReserve::get()) - 1; mock::setup_reserves(netuid, reserve_tao.into(), reserve_alpha.into()); @@ -863,7 +861,6 @@ fn test_remove_stake_insufficient_liquidity() { amount_staked.into(), ::SwapInterface::max_price(), false, - false, ) .unwrap(); @@ -877,9 +874,9 @@ fn test_remove_stake_insufficient_liquidity() { Error::::InsufficientLiquidity ); - // Mock provided liquidity - remove becomes successful - SubnetTaoProvided::::insert(netuid, TaoBalance::from(amount_staked + 1)); - SubnetAlphaInProvided::::insert(netuid, AlphaBalance::from(1)); + // Mock more liquidity - remove becomes successful + SubnetTAO::::insert(netuid, TaoBalance::from(amount_staked + 1)); + SubnetAlphaIn::::insert(netuid, AlphaBalance::from(1)); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey), hotkey, @@ -934,8 +931,6 @@ fn test_remove_stake_total_issuance_no_change() { netuid, ); - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, netuid); - assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -1038,7 +1033,6 @@ fn test_remove_prev_epoch_stake() { netuid, ); - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, netuid); let fee = mock::swap_alpha_to_tao(netuid, stake).1 + fee; assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), @@ -1103,7 +1097,7 @@ fn test_staking_sets_div_variables() { ); // Wait for 1 epoch - step_block(tempo + 1); + step_epochs(1, netuid); // Verify that divident variables have been set let stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -1694,7 +1688,6 @@ fn test_clear_small_nominations() { SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold1, netuid); let unstake_amount1 = AlphaBalance::from(alpha_stake1.to_u64() * 997 / 1000); let small1 = alpha_stake1 - unstake_amount1; - remove_stake_rate_limit_for_tests(&hot1, &cold1, netuid); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold1), hot1, @@ -1718,7 +1711,6 @@ fn test_clear_small_nominations() { SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hot1, &cold2, netuid); let unstake_amount2 = AlphaBalance::from(alpha_stake2.to_u64() * 997 / 1000); let small2 = alpha_stake2 - unstake_amount2; - remove_stake_rate_limit_for_tests(&hot1, &cold2, netuid); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(cold2), hot1, @@ -2201,18 +2193,17 @@ fn test_get_total_delegated_stake_after_unstaking() { &delegator, netuid, ); - remove_stake_rate_limit_for_tests(&delegator, &delegate_hotkey, netuid); // Unstake part of the delegation let unstake_amount_alpha = delegated_alpha / 2.into(); - remove_stake_rate_limit_for_tests(&delegate_hotkey, &delegator, netuid); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(delegator), delegate_hotkey, netuid, unstake_amount_alpha.into() )); - let current_price = - ::SwapInterface::current_alpha_price(netuid.into()); + let current_price = U96F32::from_num( + ::SwapInterface::current_alpha_price(netuid.into()), + ); // Calculate the expected delegated stake let unstake_amount = @@ -2714,13 +2705,12 @@ fn test_stake_overflow() { let coldkey_account_id = U256::from(435445); let hotkey_account_id = U256::from(54544); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let ed = u64::from(ExistentialDeposit::get()); - // Maximum possible: Max TAO supply less locked balance less ED (that's on owner's coldkey) - let amount = - 21_000_000_000_000_000_u64 - u64::from(SubtensorModule::get_network_last_lock()) - ed; register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123); + // Maximum possible: Max TAO supply less already-issued balance. + let amount = 21_000_000_000_000_000_u64 - u64::from(Balances::total_issuance()); + // Give it some $$$ in his coldkey balance add_balance_to_coldkey_account(&coldkey_account_id, amount.into()); @@ -2760,13 +2750,13 @@ fn test_max_amount_add_root() { // 0 price on root => max is 0 assert_eq!( SubtensorModule::get_max_amount_add(NetUid::ROOT, TaoBalance::ZERO), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 0.999999... price on root => max is 0 assert_eq!( SubtensorModule::get_max_amount_add(NetUid::ROOT, TaoBalance::from(999_999_999)), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 1.0 price on root => max is u64::MAX @@ -2798,13 +2788,13 @@ fn test_max_amount_add_stable() { // 0 price => max is 0 assert_eq!( SubtensorModule::get_max_amount_add(netuid, TaoBalance::ZERO), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 0.999999... price => max is 0 assert_eq!( SubtensorModule::get_max_amount_add(netuid, TaoBalance::from(999_999_999)), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 1.0 price => max is u64::MAX @@ -2888,8 +2878,15 @@ fn test_max_amount_add_dynamic() { pallet_subtensor_swap::Error::::PriceLimitExceeded, )), ), - (150_000_000_000, 100_000_000_000, 1_500_000_000, Ok(5)), - (150_000_000_000, 100_000_000_000, 1_500_000_001, Ok(51)), + ( + 150_000_000_000, + 100_000_000_000, + 1_500_000_000, + Err(DispatchError::from( + pallet_subtensor_swap::Error::::PriceLimitExceeded, + )), + ), + (150_000_000_000, 100_000_000_000, 1_500_000_001, Ok(49)), ( 150_000_000_000, 100_000_000_000, @@ -2912,13 +2909,7 @@ fn test_max_amount_add_dynamic() { SubnetAlphaIn::::insert(netuid, alpha_in); // Force the swap to initialize - SubtensorModule::swap_tao_for_alpha( - netuid, - TaoBalance::ZERO, - 1_000_000_000_000_u64.into(), - false, - ) - .unwrap(); + ::SwapInterface::init_swap(netuid, None); if !alpha_in.is_zero() { let expected_price = U96F32::from_num(tao_in) / U96F32::from_num(alpha_in); @@ -2975,13 +2966,13 @@ fn test_max_amount_remove_root() { // 1.000...001 price on root => max is 0 assert_eq!( SubtensorModule::get_max_amount_remove(NetUid::ROOT, TaoBalance::from(1_000_000_001)), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 2.0 price on root => max is 0 assert_eq!( SubtensorModule::get_max_amount_remove(NetUid::ROOT, TaoBalance::from(2_000_000_000)), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); }); } @@ -3013,13 +3004,13 @@ fn test_max_amount_remove_stable() { // 1.000...001 price => max is 0 assert_eq!( SubtensorModule::get_max_amount_remove(netuid, TaoBalance::from(1_000_000_001)), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 2.0 price => max is 0 assert_eq!( SubtensorModule::get_max_amount_remove(netuid, TaoBalance::from(2_000_000_000)), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); }); } @@ -3054,13 +3045,16 @@ fn test_max_amount_remove_dynamic() { (10_000_000_000, 10_000_000_000, 0, Ok(u64::MAX)), // Low bounds (numbers are empirical, it is only important that result // is sharply decreasing when limit price increases) - (1_000, 1_000, 0, Ok(4_308_000_000_000)), - (1_001, 1_001, 0, Ok(4_310_000_000_000)), - (1_001, 1_001, 1, Ok(31_750_000)), - (1_001, 1_001, 2, Ok(22_500_000)), - (1_001, 1_001, 1_001, Ok(1_000_000)), - (1_001, 1_001, 10_000, Ok(316_000)), - (1_001, 1_001, 100_000, Ok(100_000)), + (1_000, 1_000, 0, Ok(u64::MAX)), + (1_001, 1_001, 0, Ok(u64::MAX)), + (1_001, 1_001, 1, Ok(17_472)), + (1_001, 1_001, 2, Ok(17_472)), + (1_001, 1_001, 1_001, Ok(17_472)), + (1_001, 1_001, 10_000, Ok(17_472)), + (1_001, 1_001, 100_000, Ok(17_472)), + (1_001, 1_001, 1_000_000, Ok(17_472)), + (1_001, 1_001, 10_000_000, Ok(9_013)), + (1_001, 1_001, 100_000_000, Ok(2_165)), // Basic math (1_000_000, 1_000_000, 250_000_000, Ok(1_010_000)), (1_000_000, 1_000_000, 62_500_000, Ok(3_030_000)), @@ -3107,7 +3101,7 @@ fn test_max_amount_remove_dynamic() { 21_000_000_000_000_000, 1_000_000, 21_000_000_000_000_000, - Ok(30_700_000), + Ok(17_455_533), ), (21_000_000_000_000_000, 1_000_000, u64::MAX, Ok(67_000)), ( @@ -3145,7 +3139,7 @@ fn test_max_amount_remove_dynamic() { SubnetAlphaIn::::insert(netuid, alpha_in); if !alpha_in.is_zero() { - let expected_price = I96F32::from_num(tao_in) / I96F32::from_num(alpha_in); + let expected_price = U64F64::from_num(tao_in) / U64F64::from_num(alpha_in); assert_eq!( ::SwapInterface::current_alpha_price(netuid.into()), expected_price @@ -3217,7 +3211,7 @@ fn test_max_amount_move_root_root() { NetUid::ROOT, TaoBalance::from(1_000_000_001) ), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 2.0 price on (root, root) => max is 0 @@ -3227,7 +3221,7 @@ fn test_max_amount_move_root_root() { NetUid::ROOT, TaoBalance::from(2_000_000_000) ), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); }); } @@ -3282,7 +3276,7 @@ fn test_max_amount_move_root_stable() { netuid, TaoBalance::from(1_000_000_001) ), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); // 2.0 price on (root, stable) => max is 0 @@ -3292,7 +3286,7 @@ fn test_max_amount_move_root_stable() { netuid, TaoBalance::from(2_000_000_000) ), - Err(Error::::ZeroMaxStakeAmount.into()) + Ok(0u64.into()) ); }); } @@ -3334,7 +3328,7 @@ fn test_max_amount_move_stable_dynamic() { dynamic_netuid, TaoBalance::from(2_000_000_000) ), - Err(Error::::ZeroMaxStakeAmount.into()) + Err(pallet_subtensor_swap::Error::::PriceLimitExceeded.into()) ); // 3.0 price => max is 0 @@ -3710,29 +3704,27 @@ fn test_max_amount_move_dynamic_dynamic() { expected_max_swappable, precision, )| { - let alpha_in_1 = AlphaBalance::from(alpha_in_1); - let alpha_in_2 = AlphaBalance::from(alpha_in_2); let expected_max_swappable = AlphaBalance::from(expected_max_swappable); // Forse-set alpha in and tao reserve to achieve relative price of subnets SubnetTAO::::insert(origin_netuid, TaoBalance::from(tao_in_1)); - SubnetAlphaIn::::insert(origin_netuid, alpha_in_1); + SubnetAlphaIn::::insert(origin_netuid, AlphaBalance::from(alpha_in_1)); SubnetTAO::::insert(destination_netuid, TaoBalance::from(tao_in_2)); - SubnetAlphaIn::::insert(destination_netuid, alpha_in_2); + SubnetAlphaIn::::insert(destination_netuid, AlphaBalance::from(alpha_in_2)); if !alpha_in_1.is_zero() && !alpha_in_2.is_zero() { - let origin_price = - I96F32::from_num(tao_in_1) / I96F32::from_num(u64::from(alpha_in_1)); - let dest_price = - I96F32::from_num(tao_in_2) / I96F32::from_num(u64::from(alpha_in_2)); - if dest_price != 0 { + let origin_price = tao_in_1 as f64 / alpha_in_1 as f64; + let dest_price = tao_in_2 as f64 / alpha_in_2 as f64; + if dest_price != 0. { let expected_price = origin_price / dest_price; - assert_eq!( - ::SwapInterface::current_alpha_price( + assert_abs_diff_eq!( + (::SwapInterface::current_alpha_price( origin_netuid.into() ) / ::SwapInterface::current_alpha_price( destination_netuid.into() - ), - expected_price + )) + .to_num::(), + expected_price, + epsilon = 0.000_000_001 ); } } @@ -3868,7 +3860,7 @@ fn test_add_stake_limit_fill_or_kill() { ); // Lower the amount and it should succeed now - let amount_ok = TaoBalance::from(450_000_000_000_u64); // fits the maximum + let amount_ok = TaoBalance::from(150_000_000_000_u64); // fits the maximum assert_ok!(SubtensorModule::add_stake_limit( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -3956,7 +3948,6 @@ fn test_remove_stake_limit_ok() { let fee: u64 = (expected_alpha_reduction as f64 * 0.003) as u64; // Remove stake with slippage safety - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, netuid); assert_ok!(SubtensorModule::remove_stake_limit( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -4151,8 +4142,6 @@ fn test_remove_99_9991_per_cent_stake_works_precisely() { let coldkey_balance_before_remove = SubtensorModule::get_coldkey_balance(&coldkey_account_id); - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, netuid); - let remove_amount = AlphaBalance::from( (U64F64::from_num(alpha) * U64F64::from_num(0.999991)).to_num::(), ); @@ -4223,7 +4212,6 @@ fn test_remove_99_9989_per_cent_stake_leaves_a_little() { )); // Remove 99.9989% stake - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, netuid); let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_account_id, &coldkey_account_id, @@ -4305,13 +4293,9 @@ fn test_move_stake_limit_partial() { SubnetTAO::::insert(origin_netuid, tao_reserve); SubnetAlphaIn::::insert(origin_netuid, alpha_in); - SubnetTaoProvided::::insert(origin_netuid, TaoBalance::from(0_u64)); - SubnetAlphaInProvided::::insert(origin_netuid, AlphaBalance::from(0_u64)); SubnetTAO::::insert(destination_netuid, tao_reserve * 100_000.into()); SubnetAlphaIn::::insert(destination_netuid, alpha_in * 100_000.into()); - SubnetTaoProvided::::insert(destination_netuid, TaoBalance::from(0_u64)); - SubnetAlphaInProvided::::insert(destination_netuid, AlphaBalance::from(0_u64)); let origin_price = ::SwapInterface::current_alpha_price(origin_netuid.into()); @@ -4464,8 +4448,6 @@ fn test_unstake_all_alpha_works() { stake_amount )); - remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - // Setup the pool so that removing all the TAO will keep liq above min mock::setup_reserves( netuid, @@ -4519,8 +4501,6 @@ fn test_unstake_all_works() { stake_amount * 10.into(), u64::from(stake_amount * 100.into()).into(), ); - remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - // Unstake all alpha to free balance assert_ok!(SubtensorModule::unstake_all( RuntimeOrigin::signed(coldkey), @@ -4566,14 +4546,14 @@ fn test_stake_into_subnet_ok() { )); // Add stake with slippage safety and check if the result is ok - add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); + let large_balance = 20_000_000_000_000_000_u64; + add_balance_to_coldkey_account(&coldkey, large_balance.into()); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, netuid, amount.into(), - TaoBalance::MAX, - false, + large_balance.into(), false, )); let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 @@ -4621,23 +4601,25 @@ fn test_stake_into_subnet_low_amount() { )); // Add stake with slippage safety and check if the result is ok - add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); + let large_balance = 20_000_000_000_000_000_u64; + add_balance_to_coldkey_account(&coldkey, large_balance.into()); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, netuid, amount.into(), - TaoBalance::MAX, - false, + large_balance.into(), false, )); - let expected_stake = AlphaBalance::from(((amount as f64) * 0.997 / current_price) as u64); + let expected_stake = (amount as f64) * 0.997 / current_price; // Check if stake has increased assert_abs_diff_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + u64::from(SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, &coldkey, netuid + )) as f64, expected_stake, - epsilon = 1.into() + epsilon = expected_stake / 100. ); }); } @@ -4670,14 +4652,14 @@ fn test_unstake_from_subnet_low_amount() { )); // Add stake and check if the result is ok - add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); + let large_balance = 20_000_000_000_000_000_u64; + add_balance_to_coldkey_account(&coldkey, large_balance.into()); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, netuid, amount.into(), - TaoBalance::MAX, - false, + large_balance.into(), false, )); @@ -4796,7 +4778,6 @@ fn test_unstake_from_subnet_prohibitive_limit() { amount.into(), TaoBalance::MAX, false, - false, )); // Remove stake @@ -4872,7 +4853,6 @@ fn test_unstake_full_amount() { amount.into(), TaoBalance::MAX, false, - false, )); // Remove stake @@ -4914,34 +4894,9 @@ fn test_unstake_full_amount() { }); } -fn price_to_tick(price: f64) -> TickIndex { - let price_sqrt: U64F64 = U64F64::from_num(price.sqrt()); - // Handle potential errors in the conversion - match TickIndex::try_from_sqrt_price(price_sqrt) { - Ok(mut tick) => { - // Ensure the tick is within bounds - if tick > TickIndex::MAX { - tick = TickIndex::MAX; - } else if tick < TickIndex::MIN { - tick = TickIndex::MIN; - } - tick - } - // Default to a reasonable value when conversion fails - Err(_) => { - if price > 1.0 { - TickIndex::MAX - } else { - TickIndex::MIN - } - } - } -} - /// Test correctness of swap fees: /// 1. TAO is not minted or burned /// 2. Fees match FeeRate -/// #[test] fn test_swap_fees_tao_correctness() { new_test_ext(1).execute_with(|| { @@ -4957,7 +4912,6 @@ fn test_swap_fees_tao_correctness() { let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); add_balance_to_coldkey_account(&owner_coldkey, owner_balance_before); add_balance_to_coldkey_account(&coldkey, user_balance_before); - pallet_subtensor_swap::EnabledUserLiquidity::::insert(NetUid::from(netuid), true); // Forse-set alpha in and tao reserve to make price equal 0.25 let tao_reserve = TaoBalance::from(100_000_000_000_u64); @@ -4986,18 +4940,6 @@ fn test_swap_fees_tao_correctness() { .to_num::() + 0.0001; let limit_price = current_price + 0.01; - let tick_low = price_to_tick(current_price); - let tick_high = price_to_tick(limit_price); - let liquidity = amount; - - assert_ok!(::SwapInterface::do_add_liquidity( - netuid.into(), - &owner_coldkey, - &owner_hotkey, - tick_low, - tick_high, - u64::from(liquidity), - )); // Limit-buy and then sell all alpha for user to hit owner liquidity assert_ok!(SubtensorModule::add_stake_limit( @@ -5014,7 +4956,6 @@ fn test_swap_fees_tao_correctness() { &coldkey, netuid, ); - remove_stake_rate_limit_for_tests(&owner_hotkey, &coldkey, netuid); assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey), owner_hotkey, @@ -5275,7 +5216,6 @@ fn test_default_min_stake_sufficiency() { let fee_stake = (fee_rate * u64::from(amount) as f64) as u64; let current_price_after_stake = ::SwapInterface::current_alpha_price(netuid.into()); - remove_stake_rate_limit_for_tests(&owner_hotkey, &coldkey, netuid); let user_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &owner_hotkey, &coldkey, @@ -5299,50 +5239,30 @@ fn test_default_min_stake_sufficiency() { } #[test] -fn test_stake_rate_limits() { - new_test_ext(0).execute_with(|| { - // Create subnet and accounts. - let subnet_owner_coldkey = U256::from(10); - let subnet_owner_hotkey = U256::from(20); - let hot1 = U256::from(1); - let cold1 = U256::from(3); - let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - let amount = DefaultMinStake::::get() * 10.into(); - let fee = DefaultMinStake::::get(); - let init_balance = amount + fee + ExistentialDeposit::get(); +fn test_large_swap() { + new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(1); + let owner_coldkey = U256::from(2); + let coldkey = U256::from(100); - register_ok_neuron(netuid, hot1, cold1, 0); - Delegates::::insert(hot1, SubtensorModule::get_min_delegate_take()); - assert_eq!(SubtensorModule::get_owning_coldkey_for_hotkey(&hot1), cold1); + // add network + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_000_u64.into()); + let tao = TaoBalance::from(100_000_000u64); + let alpha = AlphaBalance::from(1_000_000_000_000_000_u64); + SubnetTAO::::insert(netuid, tao); + SubnetAlphaIn::::insert(netuid, alpha); - add_balance_to_coldkey_account(&cold1, init_balance); + // Force the swap to initialize + ::SwapInterface::init_swap(netuid, None); + + let swap_amount = TaoBalance::from(100_000_000_000_000_u64); assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(cold1), - hot1, + RuntimeOrigin::signed(coldkey), + owner_hotkey, netuid, - (amount + fee).into() + swap_amount, )); - - assert_err!( - SubtensorModule::remove_stake( - RuntimeOrigin::signed(cold1), - hot1, - netuid, - AlphaBalance::from(amount.to_u64()) - ), - Error::::StakingOperationRateLimitExceeded - ); - - // Test limit clear each block - assert!(StakingOperationRateLimiter::::contains_key(( - hot1, cold1, netuid - ))); - - next_block(); - - assert!(!StakingOperationRateLimiter::::contains_key(( - hot1, cold1, netuid - ))); }); } @@ -5494,14 +5414,14 @@ fn test_staking_records_flow() { .unwrap(); // Add stake with slippage safety and check if the result is ok - add_balance_to_coldkey_account(&coldkey, TaoBalance::MAX); + let large_balance = 20_000_000_000_000_000_u64; + add_balance_to_coldkey_account(&coldkey, large_balance.into()); assert_ok!(SubtensorModule::stake_into_subnet( &hotkey, &coldkey, netuid, amount.into(), - TaoBalance::MAX, - false, + large_balance.into(), false, )); let fee_rate = pallet_subtensor_swap::FeeRate::::get(NetUid::from(netuid)) as f64 diff --git a/pallets/subtensor/src/tests/subnet.rs b/pallets/subtensor/src/tests/subnet.rs index 12b23c74e4..be1487b4ec 100644 --- a/pallets/subtensor/src/tests/subnet.rs +++ b/pallets/subtensor/src/tests/subnet.rs @@ -633,8 +633,6 @@ fn test_subtoken_enable_trading_ok_with_enable() { stake_amount )); - remove_stake_rate_limit_for_tests(&hotkey_account_id, &coldkey_account_id, netuid); - assert_ok!(SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -665,8 +663,6 @@ fn test_subtoken_enable_trading_ok_with_enable() { unstake_amount, )); - remove_stake_rate_limit_for_tests(&hotkey_account_2_id, &coldkey_account_id, netuid); - assert_ok!(SubtensorModule::transfer_stake( RuntimeOrigin::signed(coldkey_account_id), hotkey_account_id, @@ -742,63 +738,6 @@ fn test_subtoken_enable_ok_for_burn_register_before_enable() { }); } -// #[test] -// fn test_user_liquidity_access_control() { -// new_test_ext(1).execute_with(|| { -// let owner_hotkey = U256::from(1); -// let owner_coldkey = U256::from(2); -// let not_owner = U256::from(999); // arbitrary non-owner - -// // add network -// let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - -// // Not owner, not root: should fail -// assert_noop!( -// Swap::toggle_user_liquidity(RuntimeOrigin::signed(not_owner), netuid, true), -// DispatchError::BadOrigin -// ); - -// // Subnet owner can enable -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::signed(owner_coldkey), -// netuid, -// true -// )); -// assert!(pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); - -// // Root can disable -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid, -// false -// )); -// assert!(!pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); - -// // Root can enable again -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid, -// true -// )); -// assert!(pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); - -// // Subnet owner cannot disable (only root can disable) -// assert_noop!( -// Swap::toggle_user_liquidity(RuntimeOrigin::signed(owner_coldkey), netuid, false), -// DispatchError::BadOrigin -// ); -// assert!(pallet_subtensor_swap::EnabledUserLiquidity::::get( -// NetUid::from(netuid) -// )); -// }); -// } - // cargo test --package pallet-subtensor --lib -- tests::subnet::test_no_duplicates_in_symbol_static --exact --show-output #[test] fn test_no_duplicates_in_symbol_static() { diff --git a/pallets/subtensor/src/tests/subnet_emissions.rs b/pallets/subtensor/src/tests/subnet_emissions.rs index 61af8b0cc7..9218eabe76 100644 --- a/pallets/subtensor/src/tests/subnet_emissions.rs +++ b/pallets/subtensor/src/tests/subnet_emissions.rs @@ -151,147 +151,6 @@ fn inplace_pow_normalize_fractional_exponent() { }) } -/// Configure a dynamic subnet with a given EMA price and miner-burned proportion so -/// `get_shares` can be exercised. Also seeds a large root stake with full TAO weight so -/// that, with zero alpha issuance on the test subnets, `root_proportion` is 1 and the -/// root-proportion factor in `get_shares` is neutral (isolating the price/burn weighting). -fn set_price_and_burn(netuid: NetUid, price: f64, burned: f64) { - SubnetTAO::::insert( - NetUid::ROOT, - TaoBalance::from(1_000_000_000_000_000_000_u64), - ); - SubtensorModule::set_tao_weight(u64::MAX); - SubnetMechanism::::insert(netuid, 1); - SubnetMovingPrice::::insert(netuid, i96f32(price)); - MinerBurned::::insert(netuid, U96F32::from_num(burned)); -} - -/// With no miner emission burned anywhere, `get_shares` is exactly the price-based -/// share: e_i = p_i / sum(p_j). -#[test] -fn get_shares_no_burn_matches_price_shares() { - new_test_ext(1).execute_with(|| { - let n1 = NetUid::from(1); - let n2 = NetUid::from(2); - let n3 = NetUid::from(3); - set_price_and_burn(n1, 1.0, 0.0); - set_price_and_burn(n2, 2.0, 0.0); - set_price_and_burn(n3, 3.0, 0.0); - - let shares = SubtensorModule::get_shares(&[n1, n2, n3]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - let s3 = shares.get(&n3).unwrap().to_num::(); - - assert_abs_diff_eq!(s1, 1.0 / 6.0, epsilon = 1e-9); - assert_abs_diff_eq!(s2, 2.0 / 6.0, epsilon = 1e-9); - assert_abs_diff_eq!(s3, 3.0 / 6.0, epsilon = 1e-9); - assert_abs_diff_eq!(s1 + s2 + s3, 1.0, epsilon = 1e-9); - }); -} - -/// A partial burn reallocates emission away from the burning subnet and toward the -/// non-burning one, while shares still sum to 1. -#[test] -fn get_shares_partial_burn_reallocates_away_from_burner() { - new_test_ext(1).execute_with(|| { - let n1 = NetUid::from(1); - let n2 = NetUid::from(2); - // Equal prices so the price side is neutral; n1 burns 50% of its miner emission. - set_price_and_burn(n1, 1.0, 0.5); - set_price_and_burn(n2, 1.0, 0.0); - - // weighted: n1 = 0.5 * (1 - 0.5) = 0.25, n2 = 0.5 * 1 = 0.5; total = 0.75 - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - assert_abs_diff_eq!(s1, 1.0 / 3.0, epsilon = 1e-9); - assert_abs_diff_eq!(s2, 2.0 / 3.0, epsilon = 1e-9); - assert_abs_diff_eq!(s1 + s2, 1.0, epsilon = 1e-9); - assert!( - s2 > s1, - "non-burning subnet should receive more: s1={s1}, s2={s2}" - ); - }); -} - -/// A subnet burning 100% of its miner emission receives zero chain emission; the rest -/// goes entirely to the non-burning subnet. -#[test] -fn get_shares_full_burn_gets_zero_emission() { - new_test_ext(1).execute_with(|| { - let n1 = NetUid::from(1); - let n2 = NetUid::from(2); - set_price_and_burn(n1, 1.0, 1.0); - set_price_and_burn(n2, 1.0, 0.0); - - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - assert_abs_diff_eq!(s1, 0.0, epsilon = 1e-9); - assert_abs_diff_eq!(s2, 1.0, epsilon = 1e-9); - }); -} - -/// When every subnet burns all of its miner emission, the reweighting would zero the -/// total, so `get_shares` falls back to unweighted price shares (emission is not -/// stranded). -#[test] -fn get_shares_all_full_burn_falls_back_to_price_shares() { - new_test_ext(1).execute_with(|| { - let n1 = NetUid::from(1); - let n2 = NetUid::from(2); - set_price_and_burn(n1, 1.0, 1.0); - set_price_and_burn(n2, 3.0, 1.0); - - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - // Fallback: price-proportional (1:3), not zeroed. - assert_abs_diff_eq!(s1, 1.0 / 4.0, epsilon = 1e-9); - assert_abs_diff_eq!(s2, 3.0 / 4.0, epsilon = 1e-9); - assert_abs_diff_eq!(s1 + s2, 1.0, epsilon = 1e-9); - }); -} - -/// With equal price and no burn, the root_proportion factor reallocates emission toward -/// the newer subnet (lower alpha issuance => higher root_proportion) and away from the -/// older one (higher alpha issuance => lower root_proportion). -#[test] -fn get_shares_root_proportion_favors_newer_subnets() { - new_test_ext(1).execute_with(|| { - let n1 = NetUid::from(1); - let n2 = NetUid::from(2); - // Equal price, no burn; root proportion factor is the only differentiator. - set_price_and_burn(n1, 1.0, 0.0); - set_price_and_burn(n2, 1.0, 0.0); - - // tao_weight = 1.0 (u64::MAX), so tao_weight term = root_tao. Set root_tao = 1000 - // and per-subnet alpha issuance to make root_proportion deterministic: - // n1: issuance 1000 => root_prop = 1000 / (1000 + 1000) = 0.5 - // n2: issuance 3000 => root_prop = 1000 / (1000 + 3000) = 0.25 - SubnetTAO::::insert(NetUid::ROOT, TaoBalance::from(1_000_u64)); - SubnetAlphaOut::::insert(n1, AlphaBalance::from(1_000_u64)); - SubnetAlphaOut::::insert(n2, AlphaBalance::from(3_000_u64)); - - // weighted: n1 = 0.5(price) * 0.5(root) = 0.25, n2 = 0.5 * 0.25 = 0.125; total 0.375 - let shares = SubtensorModule::get_shares(&[n1, n2]); - let s1 = shares.get(&n1).unwrap().to_num::(); - let s2 = shares.get(&n2).unwrap().to_num::(); - - assert_abs_diff_eq!(s1, 2.0 / 3.0, epsilon = 1e-6); - assert_abs_diff_eq!(s2, 1.0 / 3.0, epsilon = 1e-6); - assert_abs_diff_eq!(s1 + s2, 1.0, epsilon = 1e-9); - assert!( - s1 > s2, - "newer subnet (higher root_prop) should get more: s1={s1}, s2={s2}" - ); - }); -} - // /// Normal (moderate, non-zero) EMA flows across 3 subnets. // /// Expect: shares sum to ~1 and are monotonic with flows. // #[test] diff --git a/pallets/subtensor/src/tests/subnet_info.rs b/pallets/subtensor/src/tests/subnet_info.rs index fcf597f4ff..25079a9a7b 100644 --- a/pallets/subtensor/src/tests/subnet_info.rs +++ b/pallets/subtensor/src/tests/subnet_info.rs @@ -20,6 +20,7 @@ const EXPECTED_V3_NAMES: &[&[u8]] = &[ b"weights_version", b"weights_rate_limit", b"activity_cutoff", + b"activity_cutoff_factor", b"registration_allowed", b"target_regs_per_interval", b"min_burn", @@ -43,6 +44,7 @@ const EXPECTED_V3_NAMES: &[&[u8]] = &[ b"user_liquidity_enabled", b"owner_cut_enabled", b"owner_cut_auto_lock_enabled", + b"min_childkey_take", ]; fn find<'a>(params: &'a [HyperparamEntry], name: &[u8]) -> &'a HyperparamValue { @@ -100,10 +102,12 @@ fn test_get_subnet_hyperparams_v3_values_reflect_storage() { SubtensorModule::set_kappa(netuid, 12); SubtensorModule::set_immunity_period(netuid, 13); SubtensorModule::set_min_allowed_weights(netuid, 14); - SubtensorModule::set_tempo(netuid, 16); + SubtensorModule::set_tempo_unchecked(netuid, 16); SubtensorModule::set_weights_version_key(netuid, 19); SubtensorModule::set_weights_set_rate_limit(netuid, 20); - SubtensorModule::set_activity_cutoff(netuid, 22); + // `activity_cutoff` is derived: factor_milli * tempo / 1000. With tempo=16, + // factor 1375 yields 1375 * 16 / 1000 = 22 effective cutoff blocks. + SubtensorModule::set_activity_cutoff_factor_milli(netuid, 1375); SubtensorModule::set_network_registration_allowed(netuid, false); SubtensorModule::set_target_registrations_per_interval(netuid, 24); SubtensorModule::set_min_burn(netuid, TaoBalance::from(25u64)); @@ -121,6 +125,8 @@ fn test_get_subnet_hyperparams_v3_values_reflect_storage() { SubtensorModule::set_bonds_reset(netuid, true); SubtensorModule::set_owner_cut_enabled_flag(netuid, true); SubtensorModule::set_owner_cut_auto_lock_enabled(netuid, true); + SubtensorModule::set_min_childkey_take(31); + SubtensorModule::set_min_childkey_take_for_subnet(netuid, 32); let result = SubtensorModule::get_subnet_hyperparams_v3(netuid).unwrap(); let p = &result; @@ -161,7 +167,11 @@ fn test_get_subnet_hyperparams_v3_values_reflect_storage() { assert_eq!(find(p, b"tempo"), &HyperparamValue::U16(Compact(16))); assert_eq!( find(p, b"activity_cutoff"), - &HyperparamValue::U16(Compact(22)) + &HyperparamValue::U64(Compact(22)) + ); + assert_eq!( + find(p, b"activity_cutoff_factor"), + &HyperparamValue::U32(Compact(1375)) ); assert_eq!( find(p, b"target_regs_per_interval"), @@ -180,6 +190,11 @@ fn test_get_subnet_hyperparams_v3_values_reflect_storage() { &HyperparamValue::U16(Compact(30)) ); assert_eq!(find(p, b"yuma_version"), &HyperparamValue::U16(Compact(3))); + // Effective min childkey take = max(global, per-subnet). + assert_eq!( + find(p, b"min_childkey_take"), + &HyperparamValue::U16(Compact(32)) + ); // U64 variants assert_eq!( diff --git a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs index 212df1e48a..1e200aaedd 100644 --- a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs +++ b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs @@ -2928,7 +2928,7 @@ fn test_swap_hotkey_root_claims_unchanged_if_not_root() { let netuid = add_dynamic_network(&neuron_hotkey, &owner_coldkey); let new_hotkey = U256::from(10030); - add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, 20_000_000_000_000_000_u64.into()); SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000_000u64; @@ -3014,7 +3014,7 @@ fn test_swap_hotkey_root_claims_changed_if_root() { // Use neuron_hotkey as subnet creator so it receives root dividends let netuid_1 = add_dynamic_network(&neuron_hotkey, &owner_coldkey); - add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, 20_000_000_000_000_000_u64.into()); SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000_000u64; @@ -3103,7 +3103,7 @@ fn test_swap_hotkey_root_claims_changed_if_all_subnets() { // Use neuron_hotkey as subnet creator so it receives root dividends let netuid_1 = add_dynamic_network(&neuron_hotkey, &owner_coldkey); - add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, 20_000_000_000_000_000_u64.into()); SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 let root_stake = 2_000_000_000u64; @@ -3184,7 +3184,7 @@ fn test_swap_hotkey_auto_parent_delegation_transferred_on_root() { let new_hotkey = U256::from(1005); let _ = add_dynamic_network(&old_hotkey, &owner_coldkey); - add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, 20_000_000_000_000_000_u64.into()); // Opt out of auto parent delegation on the old hotkey. AutoParentDelegationEnabled::::insert(old_hotkey, false); @@ -3225,7 +3225,7 @@ fn test_swap_hotkey_auto_parent_delegation_transferred_on_all_subnets() { NetworksAdded::::insert(NetUid::ROOT, true); let _ = add_dynamic_network(&old_hotkey, &owner_coldkey); - add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, 20_000_000_000_000_000_u64.into()); AutoParentDelegationEnabled::::insert(old_hotkey, false); @@ -3257,7 +3257,7 @@ fn test_swap_hotkey_auto_parent_delegation_not_transferred_on_non_root() { let new_hotkey = U256::from(1005); let netuid = add_dynamic_network(&old_hotkey, &owner_coldkey); - add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + add_balance_to_coldkey_account(&owner_coldkey, 20_000_000_000_000_000_u64.into()); AutoParentDelegationEnabled::::insert(old_hotkey, false); diff --git a/pallets/subtensor/src/tests/tempo_control.rs b/pallets/subtensor/src/tests/tempo_control.rs new file mode 100644 index 0000000000..25d3abc691 --- /dev/null +++ b/pallets/subtensor/src/tests/tempo_control.rs @@ -0,0 +1,245 @@ +#![allow(clippy::expect_used)] +use frame_support::{assert_noop, assert_ok}; +use frame_system::Config; +use sp_core::U256; +use subtensor_runtime_common::NetUid; + +use super::mock::*; +use crate::{ + ActivityCutoffFactorMilli, AdminFreezeWindow, CommitRevealWeightsEnabled, LastEpochBlock, + PendingEpochAt, SubnetOwner, SubtokenEnabled, Tempo, +}; + +const DEFAULT_TEMPO: u16 = 360; +const NEW_TEMPO: u16 = 720; + +fn setup_subnet(owner: U256) -> NetUid { + let netuid = NetUid::from(1); + add_network(netuid, DEFAULT_TEMPO, 0); + SubnetOwner::::insert(netuid, owner); + SubtokenEnabled::::insert(netuid, true); + crate::Pallet::::set_admin_freeze_window(0); + netuid +} + +#[test] +fn do_set_tempo_works_with_commit_reveal_enabled() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + // CR is enabled by default; `set_tempo` is no longer blocked for CR + // subnets — CR timing keys off the stateful `SubnetEpochIndex` counter. + assert!(CommitRevealWeightsEnabled::::get(netuid)); + + assert_ok!(crate::Pallet::::do_set_tempo( + <::RuntimeOrigin>::signed(owner), + netuid, + NEW_TEMPO, + )); + + assert_eq!(Tempo::::get(netuid), NEW_TEMPO); + }); +} + +#[test] +fn do_trigger_epoch_blocked_with_commit_reveal_enabled() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + // CR enabled by default; an out-of-band epoch would desync the CRv3 reveal + // window from the Drand schedule and drop committed weights, so it is blocked. + assert!(CommitRevealWeightsEnabled::::get(netuid)); + AdminFreezeWindow::::set(5); + + assert_noop!( + crate::Pallet::::do_trigger_epoch( + <::RuntimeOrigin>::signed(owner), + netuid, + ), + crate::Error::::DynamicTempoBlockedByCommitReveal + ); + + // No pending epoch was scheduled. + assert_eq!(PendingEpochAt::::get(netuid), 0); + }); +} + +#[test] +fn do_trigger_epoch_works_with_commit_reveal_disabled() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + // With CR disabled there is no reveal window to protect, so the trigger fires. + CommitRevealWeightsEnabled::::insert(netuid, false); + AdminFreezeWindow::::set(5); + + assert_ok!(crate::Pallet::::do_trigger_epoch( + <::RuntimeOrigin>::signed(owner), + netuid, + )); + + let now = crate::Pallet::::get_current_block_as_u64(); + assert_eq!(PendingEpochAt::::get(netuid), now + 5); + }); +} + +#[test] +fn do_set_activity_cutoff_factor_works_for_root_bypassing_freeze_window() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + // Engage the admin freeze window so an owner-call would fail. + Tempo::::insert(netuid, 10u16); + LastEpochBlock::::insert(netuid, 1u64); + AdminFreezeWindow::::set(8); + run_to_block(5); + + // Owner cannot bypass the freeze window. + assert_noop!( + crate::Pallet::::do_set_activity_cutoff_factor( + <::RuntimeOrigin>::signed(owner), + netuid, + 5_000u32, + ), + crate::Error::::AdminActionProhibitedDuringWeightsWindow + ); + + // Root bypasses both freeze window and rate limit. + assert_ok!(crate::Pallet::::do_set_activity_cutoff_factor( + <::RuntimeOrigin>::root(), + netuid, + 5_000u32, + )); + assert_eq!(ActivityCutoffFactorMilli::::get(netuid), 5_000u32); + }); +} + +#[test] +fn do_trigger_epoch_rejects_when_auto_epoch_already_imminent() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + // Disable CR so the trigger reaches the imminent-auto-epoch check rather than + // being short-circuited by the commit-reveal guard. + CommitRevealWeightsEnabled::::insert(netuid, false); + + // Make the next auto epoch closer than AdminFreezeWindow. + // remaining = (LastEpochBlock + tempo) - now = (1 + 10) - 5 = 6, window = 8 => reject. + Tempo::::insert(netuid, 10u16); + LastEpochBlock::::insert(netuid, 1u64); + AdminFreezeWindow::::set(8); + run_to_block(5); + + assert_noop!( + crate::Pallet::::do_trigger_epoch( + <::RuntimeOrigin>::signed(owner), + netuid, + ), + crate::Error::::AutoEpochAlreadyImminent + ); + + // Nothing was scheduled. + assert_eq!(PendingEpochAt::::get(netuid), 0); + }); +} + +#[test] +fn get_next_epoch_start_block_returns_none_when_tempo_zero() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + Tempo::::insert(netuid, 0); + + assert_eq!( + crate::Pallet::::get_next_epoch_start_block(netuid), + None + ); + }); +} + +#[test] +fn get_next_epoch_start_block_uses_last_epoch_block_plus_tempo() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + LastEpochBlock::::insert(netuid, 100u64); + Tempo::::insert(netuid, 50u16); + PendingEpochAt::::insert(netuid, 0u64); + + // last (100) + tempo (50) = 150 + assert_eq!( + crate::Pallet::::get_next_epoch_start_block(netuid), + Some(150) + ); + }); +} + +#[test] +fn get_next_epoch_start_block_returns_pending_when_pending_is_earlier() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + LastEpochBlock::::insert(netuid, 100u64); + Tempo::::insert(netuid, 50u16); + // Owner-triggered manual fire scheduled before automatic next. + PendingEpochAt::::insert(netuid, 120u64); + + // min(150, 120) = 120 + assert_eq!( + crate::Pallet::::get_next_epoch_start_block(netuid), + Some(120) + ); + }); +} + +#[test] +fn get_next_epoch_start_block_ignores_pending_when_auto_is_earlier() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + LastEpochBlock::::insert(netuid, 100u64); + Tempo::::insert(netuid, 50u16); + // Pending scheduled after the next automatic fire. + PendingEpochAt::::insert(netuid, 200u64); + + // min(150, 200) = 150 + assert_eq!( + crate::Pallet::::get_next_epoch_start_block(netuid), + Some(150) + ); + }); +} + +#[test] +fn get_next_epoch_start_block_reflects_set_tempo_cycle_reset() { + new_test_ext(1).execute_with(|| { + let owner = U256::from(1); + let netuid = setup_subnet(owner); + + run_to_block(10); + let new_tempo: u16 = 720; + + assert_ok!(crate::Pallet::::do_set_tempo( + <::RuntimeOrigin>::signed(owner), + netuid, + new_tempo, + )); + + let now = crate::Pallet::::get_current_block_as_u64(); + // apply_tempo_with_cycle_reset sets LastEpochBlock = now; + // next fire is now + tempo. + assert_eq!( + crate::Pallet::::get_next_epoch_start_block(netuid), + Some(now + new_tempo as u64) + ); + }); +} diff --git a/pallets/subtensor/src/tests/transaction_extension_pays_no.rs b/pallets/subtensor/src/tests/transaction_extension_pays_no.rs deleted file mode 100644 index fd8c8ccc85..0000000000 --- a/pallets/subtensor/src/tests/transaction_extension_pays_no.rs +++ /dev/null @@ -1,773 +0,0 @@ -//! Transaction extension coverage for extrinsics handled by [`crate::extensions::SubtensorTransactionExtension`]. - -#![allow(clippy::unwrap_used)] - -use super::mock::*; -use crate::extensions::SubtensorTransactionExtension; -use crate::*; -use codec::Compact; -use frame_support::dispatch::GetDispatchInfo; -use frame_support::{BoundedVec, assert_ok, traits::ConstU32}; -use frame_system::RawOrigin; -use pallet_drand::LastStoredRound; -use sp_core::H256; -use sp_core::U256; -use sp_runtime::traits::{DispatchInfoOf, TransactionExtension, TxBaseImplication}; -use sp_runtime::transaction_validity::{TransactionSource, TransactionValidityError}; -use subtensor_runtime_common::{CustomTransactionError, MechId, NetUid, TaoBalance}; - -fn dispatch_info() -> sp_runtime::traits::DispatchInfoOf<::RuntimeCall> -{ - DispatchInfoOf::<::RuntimeCall>::default() -} - -fn validate_signed( - signer: U256, - call: &RuntimeCall, -) -> Result { - SubtensorTransactionExtension::::new() - .validate( - RawOrigin::Signed(signer).into(), - call, - &dispatch_info(), - 0, - (), - &TxBaseImplication(()), - TransactionSource::External, - ) - .map(|(v, _, _)| v) -} - -#[test] -fn extension_set_weights_rejects_stake_too_low() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network_disable_commit_reveal(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_stake_threshold(1_000_000_000_000u64); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::set_weights { - netuid, - dests: vec![1], - weights: vec![1], - version_key: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); - }); -} - -#[test] -fn extension_set_mechanism_weights_rejects_stake_too_low() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network_disable_commit_reveal(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_stake_threshold(1_000_000_000_000u64); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::set_mechanism_weights { - netuid, - mecid: MechId::MAIN, - dests: vec![1], - weights: vec![1], - version_key: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); - }); -} - -#[test] -fn extension_batch_set_weights_rejects_mismatched_lengths() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_set_weights { - netuids: vec![Compact(netuid)], - weights: vec![], - version_keys: vec![Compact(0_u64)], - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::InputLengthsUnequal.into()); - }); -} - -#[test] -fn extension_batch_set_weights_rejects_stake_too_low() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network_disable_commit_reveal(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_stake_threshold(1_000_000_000_000u64); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_set_weights { - netuids: vec![Compact(netuid)], - weights: vec![vec![(Compact(0u16), Compact(1u16))]], - version_keys: vec![Compact(0u64)], - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); - }); -} - -#[test] -fn extension_commit_weights_rejects_stake_too_low() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::set_stake_threshold(1_000_000_000_000u64); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_weights { - netuid, - commit_hash: H256::zero(), - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); - }); -} - -#[test] -fn extension_commit_mechanism_weights_rejects_stake_too_low() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::set_stake_threshold(1_000_000_000_000u64); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_mechanism_weights { - netuid, - mecid: MechId::MAIN, - commit_hash: H256::zero(), - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); - }); -} - -#[test] -fn extension_batch_commit_weights_rejects_mismatched_lengths() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_commit_weights { - netuids: vec![Compact(netuid)], - commit_hashes: vec![], - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::InputLengthsUnequal.into()); - }); -} - -#[test] -fn extension_reveal_weights_rejects_stake_too_low() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - add_network(netuid, 1, 0); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::set_stake_threshold(1_000_000_000_000u64); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_weights { - netuid, - uids: vec![0], - values: vec![1], - salt: vec![1], - version_key: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::StakeAmountTooLow.into()); - }); -} - -#[test] -fn extension_reveal_weights_rejects_commit_not_found() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::set_stake_threshold(0); - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - TaoBalance::from(500_000_000_000_u64) - )); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_weights { - netuid, - uids: vec![0], - values: vec![1], - salt: vec![1], - version_key: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::CommitNotFound.into()); - }); -} - -#[test] -fn extension_reveal_mechanism_weights_rejects_commit_not_found() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::set_stake_threshold(0); - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - TaoBalance::from(500_000_000_000_u64) - )); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_mechanism_weights { - netuid, - mecid: MechId::MAIN, - uids: vec![0], - values: vec![1], - salt: vec![1], - version_key: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::CommitNotFound.into()); - }); -} - -#[test] -fn extension_reveal_mechanism_weights_accepts_valid_commit() { - assert_reveal_mechanism_weights_accepts_valid_commit(MechId::MAIN, None); -} - -#[test] -fn extension_reveal_mechanism_weights_accepts_valid_non_main_mechanism_commit() { - assert_reveal_mechanism_weights_accepts_valid_commit( - MechId::from(1u8), - Some(MechId::from(2u8)), - ); -} - -fn assert_reveal_mechanism_weights_accepts_valid_commit( - mecid: MechId, - mechanism_count: Option, -) { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - let uids = vec![0]; - let values = vec![1]; - let salt = vec![1]; - let version_key = 0; - add_network(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - if let Some(mechanism_count) = mechanism_count { - MechanismCountCurrent::::insert(netuid, mechanism_count); - } - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - SubtensorModule::set_stake_threshold(0); - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - TaoBalance::from(500_000_000_000_u64) - )); - - let commit_hash = SubtensorModule::get_commit_hash( - &hotkey, - SubtensorModule::get_mechanism_storage_index(netuid, mecid), - &uids, - &values, - &salt, - version_key, - ); - assert_ok!(SubtensorModule::commit_mechanism_weights( - RuntimeOrigin::signed(hotkey), - netuid, - mecid, - commit_hash - )); - step_epochs(1, netuid); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_mechanism_weights { - netuid, - mecid, - uids, - values, - salt, - version_key, - }); - assert_ok!(validate_signed(hotkey, &call)); - }); -} - -#[test] -fn extension_batch_reveal_weights_rejects_mismatched_vector_lengths() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - add_network(netuid, 1, 0); - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - SubtensorModule::set_stake_threshold(0); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_reveal_weights { - netuid, - uids_list: vec![vec![0]], - values_list: vec![], - salts_list: vec![vec![1]], - version_keys: vec![0], - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::InputLengthsUnequal.into()); - }); -} - -#[test] -fn extension_commit_timelocked_weights_rejects_invalid_reveal_round() { - new_test_ext(0).execute_with(|| { - LastStoredRound::::put(1_000_u64); - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - add_network(netuid, 1, 0); - SubtensorModule::set_stake_threshold(0); - - let commit = - BoundedVec::>::try_from(vec![0u8]).unwrap(); - let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_weights { - netuid, - commit, - reveal_round: 500, - commit_reveal_version: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::InvalidRevealRound.into()); - }); -} - -#[test] -fn extension_commit_timelocked_mechanism_weights_rejects_invalid_reveal_round() { - new_test_ext(0).execute_with(|| { - LastStoredRound::::put(2_000_u64); - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - add_network(netuid, 1, 0); - SubtensorModule::set_stake_threshold(0); - - let commit = - BoundedVec::>::try_from(vec![1u8]).unwrap(); - let call = - RuntimeCall::SubtensorModule(SubtensorCall::commit_timelocked_mechanism_weights { - netuid, - mecid: MechId::MAIN, - commit, - reveal_round: 100, - commit_reveal_version: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::InvalidRevealRound.into()); - }); -} - -#[test] -fn extension_commit_crv3_mechanism_weights_rejects_invalid_reveal_round() { - new_test_ext(0).execute_with(|| { - LastStoredRound::::put(500u64); - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - add_network(netuid, 1, 0); - SubtensorModule::set_stake_threshold(0); - - let commit = - BoundedVec::>::try_from(vec![2u8]).unwrap(); - let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_crv3_mechanism_weights { - netuid, - mecid: MechId::MAIN, - commit, - reveal_round: 100, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::InvalidRevealRound.into()); - }); -} - -#[test] -fn extension_decrease_take_rejects_non_owner_coldkey() { - new_test_ext(0).execute_with(|| { - let owner_ck = U256::from(1); - let other_ck = U256::from(2); - let hotkey = U256::from(3); - crate::Owner::::insert(hotkey, owner_ck); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::decrease_take { - hotkey, - take: MinDelegateTake::::get(), - }); - let err = validate_signed(other_ck, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::NonAssociatedColdKey.into()); - }); -} - -#[test] -fn extension_decrease_take_rejects_missing_hotkey_owner() { - new_test_ext(0).execute_with(|| { - let coldkey = U256::from(1); - let hotkey = U256::from(99); - let call = RuntimeCall::SubtensorModule(SubtensorCall::decrease_take { - hotkey, - take: MinDelegateTake::::get(), - }); - let err = validate_signed(coldkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::HotkeyAccountDoesntExist.into()); - }); -} - -#[test] -fn extension_increase_take_rejects_non_owner_coldkey() { - new_test_ext(0).execute_with(|| { - let owner_ck = U256::from(10); - let other_ck = U256::from(11); - let hotkey = U256::from(12); - crate::Owner::::insert(hotkey, owner_ck); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::increase_take { - hotkey, - take: SubtensorModule::get_min_delegate_take(), - }); - let err = validate_signed(other_ck, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::NonAssociatedColdKey.into()); - }); -} - -#[test] -fn extension_increase_take_validates_take_bounds() { - new_test_ext(0).execute_with(|| { - let coldkey = U256::from(13); - let hotkey = U256::from(14); - crate::Owner::::insert(hotkey, coldkey); - let min_take = SubtensorModule::get_min_delegate_take(); - let max_take = SubtensorModule::get_max_delegate_take(); - let increase_take_call = - |take| RuntimeCall::SubtensorModule(SubtensorCall::increase_take { hotkey, take }); - - let too_low_call = increase_take_call(min_take - 1); - let err = validate_signed(coldkey, &too_low_call).unwrap_err(); - assert_eq!(err, CustomTransactionError::DelegateTakeTooLow.into()); - - let in_scope_call = increase_take_call(min_take); - assert_ok!(validate_signed(coldkey, &in_scope_call)); - - let too_high_call = increase_take_call(max_take + 1); - let err = validate_signed(coldkey, &too_high_call).unwrap_err(); - assert_eq!(err, CustomTransactionError::DelegateTakeTooHigh.into()); - }); -} - -#[test] -fn extension_serve_axon_rejects_unregistered_hotkey() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(40); - let ip = u128::from(u32::from_be_bytes([8, 8, 8, 8])); - let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_axon { - netuid, - version: 1, - ip, - port: 1, - ip_type: 4, - protocol: 0, - placeholder1: 0, - placeholder2: 0, - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!( - err, - CustomTransactionError::HotKeyNotRegisteredInNetwork.into() - ); - }); -} - -#[test] -fn extension_serve_axon_tls_rejects_unregistered_hotkey() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(41); - let ip = u128::from(u32::from_be_bytes([8, 8, 8, 8])); - let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_axon_tls { - netuid, - version: 1, - ip, - port: 1, - ip_type: 4, - protocol: 0, - placeholder1: 0, - placeholder2: 0, - certificate: vec![], - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!( - err, - CustomTransactionError::HotKeyNotRegisteredInNetwork.into() - ); - }); -} - -#[test] -fn extension_serve_prometheus_rejects_unregistered_hotkey() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(99); - let ip = u128::from(u32::from_be_bytes([8, 8, 8, 8])); - let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_prometheus { - netuid, - version: 1, - ip, - port: 1, - ip_type: 4, - }); - let info = call.get_dispatch_info(); - assert_eq!(info.pays_fee, frame_support::dispatch::Pays::No); - - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!( - err, - CustomTransactionError::HotKeyNotRegisteredInNetwork.into() - ); - }); -} - -#[test] -fn extension_associate_evm_key_rejects_uid_not_found() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - add_network(netuid, 1, 0); - let hotkey = U256::from(50); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::associate_evm_key { - netuid, - evm_key: sp_core::H160::zero(), - block_number: 0, - signature: sp_core::ecdsa::Signature::from_raw([0u8; 65]), - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::UidNotFound.into()); - }); -} - -#[test] -fn extension_register_network_rejects_global_rate_limit() { - new_test_ext(0).execute_with(|| { - let limit = 50u64; - NetworkRateLimit::::put(limit); - System::set_block_number(200u64.into()); - SubtensorModule::set_network_last_lock_block(170); - - let coldkey = U256::from(70); - let hotkey = U256::from(71); - let call = RuntimeCall::SubtensorModule(SubtensorCall::register_network { hotkey }); - let err = validate_signed(coldkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::RateLimitExceeded.into()); - }); -} - -#[test] -fn extension_register_network_accepts_after_global_cooldown() { - new_test_ext(0).execute_with(|| { - let limit = 50u64; - NetworkRateLimit::::put(limit); - System::set_block_number(200u64.into()); - SubtensorModule::set_network_last_lock_block(150); - - let coldkey = U256::from(72); - let hotkey = U256::from(73); - let call = RuntimeCall::SubtensorModule(SubtensorCall::register_network { hotkey }); - assert!(validate_signed(coldkey, &call).is_ok()); - }); -} - -#[test] -fn extension_associate_evm_key_rejects_associate_rate_limit() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let tempo: u16 = 2; - let modality: u16 = 2; - add_network(netuid, tempo, modality); - - let coldkey = U256::from(80); - let hotkey = U256::from(81); - let _ = SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); - register_ok_neuron(netuid, hotkey, coldkey, 0); - - let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); - System::set_block_number(300u64.into()); - let now = SubtensorModule::get_current_block_as_u64(); - AssociatedEvmAddress::::insert(netuid, uid, (sp_core::H160::zero(), now)); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::associate_evm_key { - netuid, - evm_key: sp_core::H160::zero(), - block_number: 0, - signature: sp_core::ecdsa::Signature::from_raw([0u8; 65]), - }); - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!( - err, - CustomTransactionError::EvmKeyAssociateRateLimitExceeded.into() - ); - }); -} - -// ============================================================ -// GHSA-2026-006 regression test — security audit (June 2026) -// Fails on the vulnerable code; passes with the fix in this PR. -// ============================================================ -use frame_support::assert_err; - -#[test] -fn ghsa_2026_006_set_weights_paysno_validate_omits_ratelimit() { - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey = U256::from(1); - let coldkey = U256::from(2); - - // Subnet with commit-reveal disabled so do_set_weights runs the - // per-neuron SetWeightsRateLimit check (weights.rs step 9). - add_network_disable_commit_reveal(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - // Register a real neuron (uid 0) so it exists on-network and its - // LastUpdate vector is sized for set_last_update_for_uid below. - register_ok_neuron(netuid, hotkey, coldkey, 0); - let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); - - // Drop the min-stake threshold to 0 so the ONLY thing that validate() - // could reject for is the rate limit. This isolates the fix: the - // min-stake mempool gate passes, and the rate-limit gate must now also - // be enforced in validate(). - SubtensorModule::set_stake_threshold(0); - assert!(SubtensorModule::check_weights_min_stake(&hotkey, netuid)); - - // Configure a non-zero per-neuron rate limit and mark this neuron as - // having "just" set weights at the current block, so the next - // set_weights is over-rate. - SubtensorModule::set_weights_set_rate_limit(netuid, 100); - System::set_block_number(10u64.into()); - let current_block = SubtensorModule::get_current_block_as_u64(); - let netuid_index = SubtensorModule::get_mechanism_storage_index(netuid, MechId::MAIN); - SubtensorModule::set_last_update_for_uid(netuid_index, uid, current_block); - - // Sanity: the in-dispatch rate-limit helper now reports over-rate. - assert!(!SubtensorModule::check_rate_limit( - netuid_index, - uid, - current_block - )); - - // Self-weight call (uids/weights == [uid]/[1]) avoids needing a - // validator permit, so dispatch reaches the rate-limit gate. - let call = RuntimeCall::SubtensorModule(SubtensorCall::set_weights { - netuid, - dests: vec![uid], - weights: vec![1], - version_key: 0, - }); - - // (a) set_weights is declared Pays::No -> if validate accepted it, it - // would be included into a block for free. - let info = call.get_dispatch_info(); - assert_eq!(info.pays_fee, frame_support::dispatch::Pays::No); - - // (b) THE FIX: SubtensorTransactionExtension::validate now enforces the - // per-neuron SetWeightsRateLimit. An over-rate set_weights is - // rejected pre-dispatch with RateLimitExceeded, so it can never be - // admitted to the mempool / included for free. - let err = validate_signed(hotkey, &call).unwrap_err(); - assert_eq!(err, CustomTransactionError::RateLimitExceeded.into()); - - // (c) The dispatch path still enforces the rate limit as the - // authoritative check (defence in depth for any tx that slips past - // the pool-level filter, e.g. two over-rate txs in the same block). - assert_err!( - SubtensorModule::set_weights( - RuntimeOrigin::signed(hotkey), - netuid, - vec![uid], - vec![1], - 0, - ), - Error::::SettingWeightsTooFast - ); - }); -} diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index f9afd96033..23318eca4a 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -3,27 +3,23 @@ use ark_serialize::CanonicalDeserialize; use ark_serialize::CanonicalSerialize; use codec::Compact; -use frame_support::dispatch::DispatchInfo; use frame_support::{ assert_err, assert_ok, dispatch::{DispatchClass, DispatchResult, GetDispatchInfo, Pays}, }; -use frame_system::RawOrigin; use pallet_drand::types::Pulse; use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng}; use scale_info::prelude::collections::HashMap; use sha2::Digest; use sp_core::Encode; -use sp_core::{Get, H256, U256}; -use sp_runtime::traits::{DispatchInfoOf, TransactionExtension}; +use sp_core::{H256, U256}; use sp_runtime::{ BoundedVec, DispatchError, - traits::{BlakeTwo256, ConstU32, Hash, TxBaseImplication}, + traits::{BlakeTwo256, ConstU32, Hash}, }; use sp_std::collections::vec_deque::VecDeque; use substrate_fixed::types::I32F32; -use subtensor_runtime_common::{CustomTransactionError, NetUidStorageIndex, TaoBalance}; -use subtensor_swap_interface::SwapHandler; +use subtensor_runtime_common::NetUidStorageIndex; use tle::{ curves::drand::TinyBLS381, ibe::fullident::Identity, @@ -32,10 +28,8 @@ use tle::{ }; use w3f_bls::EngineBLS; -use super::mock; use super::mock::*; use crate::coinbase::reveal_commits::{LegacyWeightsTlockPayload, WeightsTlockPayload}; -use crate::extensions::SubtensorTransactionExtension; use crate::*; /*************************** pub fn set_weights() tests @@ -88,118 +82,6 @@ fn test_commit_weights_dispatch_info_ok() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_commit_weights_validate --exact --show-output --nocapture -#[test] -fn test_commit_weights_validate() { - // Testing the signed extension validate function - // correctly filters this transaction. - - new_test_ext(0).execute_with(|| { - let dests = vec![1, 1]; - let weights = vec![1, 1]; - let netuid = NetUid::from(1); - let salt: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8]; - let version_key: u64 = 0; - let coldkey = U256::from(0); - let hotkey: U256 = U256::from(1); // Add the hotkey field - assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - - let who = hotkey; // The hotkey signs this transaction - - let commit_hash: H256 = - BlakeTwo256::hash_of(&(hotkey, netuid, dests, weights, salt, version_key)); - - let call = RuntimeCall::SubtensorModule(SubtensorCall::commit_weights { - netuid, - commit_hash, - }); - - // Create netuid - add_network(netuid, 1, 0); - // Register the hotkey - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - - let min_stake = 500_000_000_000_u64; - let reserve = min_stake * 1000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Stake some TAO and read what get_total_stake_for_hotkey it gets - // It will be a different value due to the slippage - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - min_stake.into() - )); - let min_stake_with_slippage = SubtensorModule::get_total_stake_for_hotkey(&hotkey); - - // Set the minimum stake above what hotkey has - SubtensorModule::set_stake_threshold(min_stake_with_slippage.to_u64() + 1); - - // Submit to the signed extension validate function - let info = DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - // Submit to the signed extension validate function - let result_no_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result_no_stake.unwrap_err(), - CustomTransactionError::StakeAmountTooLow.into() - ); - - // Set the minimum stake equal to what hotkey has - SubtensorModule::set_stake_threshold(min_stake_with_slippage.into()); - - // Submit to the signed extension validate function - let result_min_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Now the call should pass - assert_ok!(result_min_stake); - - // Try with more stake than minimum - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - DefaultMinStake::::get() * 10.into() - )); - - // Verify stake is more than minimum - assert!(SubtensorModule::get_total_stake_for_hotkey(&hotkey) > min_stake_with_slippage); - - let result_more_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // The call should still pass - assert_ok!(result_more_stake); - }); -} - // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_reveal_weights_dispatch_info_ok --exact --show-output --nocapture #[test] fn test_reveal_weights_dispatch_info_ok() { @@ -224,484 +106,6 @@ fn test_reveal_weights_dispatch_info_ok() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_set_weights_validate --exact --show-output --nocapture -#[test] -fn test_set_weights_validate() { - // Testing the signed extension validate function - // correctly filters the `set_weights` transaction. - - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let coldkey = U256::from(0); - let hotkey: U256 = U256::from(1); - assert_ne!(hotkey, coldkey); - - let who = hotkey; // The hotkey signs this transaction - - let call = RuntimeCall::SubtensorModule(SubtensorCall::set_weights { - netuid, - dests: vec![1, 1], - weights: vec![1, 1], - version_key: 0, - }); - - // Create netuid (commit-reveal off so `set_weights` matches extension / extrinsic) - add_network_disable_commit_reveal(netuid, 1, 0); - setup_reserves( - netuid, - 1_000_000_000_000_u64.into(), - 1_000_000_000_000_u64.into(), - ); - // Register the hotkey - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); - - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - - let min_stake = TaoBalance::from(500_000_000_000_u64); - - // Set the minimum stake - SubtensorModule::set_stake_threshold(min_stake.into()); - - // Verify stake is less than minimum - assert!(SubtensorModule::get_total_stake_for_hotkey(&hotkey) < min_stake); - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - - let extension = SubtensorTransactionExtension::::new(); - // Submit to the signed extension validate function - let result_no_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail due to insufficient stake - assert_eq!( - result_no_stake.unwrap_err(), - CustomTransactionError::StakeAmountTooLow.into() - ); - - // Increase the stake and make it to be equal to the minimum threshold - let fee = - ::SwapInterface::approx_fee_amount(netuid.into(), min_stake); - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - min_stake + fee - )); - let min_stake_with_slippage = SubtensorModule::get_total_stake_for_hotkey(&hotkey); - - // Set the minimum stake to what the hotkey has - SubtensorModule::set_stake_threshold(min_stake_with_slippage.into()); - - // Submit to the signed extension validate function - let result_min_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Now the call should pass - assert_ok!(result_min_stake); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_reveal_weights_validate --exact --show-output --nocapture -#[test] -fn test_reveal_weights_validate() { - // Testing the signed extension validate function - // correctly filters this transaction. - - new_test_ext(0).execute_with(|| { - let dests = vec![1, 1]; - let weights = vec![1, 1]; - let netuid = NetUid::from(1); - let salt: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8]; - let version_key: u64 = 0; - let coldkey = U256::from(0); - let hotkey: U256 = U256::from(1); // Add the hotkey field - let hotkey2: U256 = U256::from(2); - let tempo = 1; - assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - let fee: u64 = 0; // FIXME: DefaultStakingFee is deprecated - - let who = hotkey; // The hotkey signs this transaction - - let call = RuntimeCall::SubtensorModule(SubtensorCall::reveal_weights { - netuid, - uids: dests.clone(), - values: weights.clone(), - salt: salt.clone(), - version_key, - }); - - let commit_hash: H256 = SubtensorModule::get_commit_hash( - &who, - NetUidStorageIndex::from(netuid), - &dests, - &weights, - &salt, - version_key, - ); - let commit_block = SubtensorModule::get_current_block_as_u64(); - let (first_reveal_block, last_reveal_block) = - SubtensorModule::get_reveal_blocks(netuid, commit_block); - - // Create netuid - add_network(netuid, tempo, 0); - // Register the hotkey - SubtensorModule::append_neuron(netuid, &hotkey, 0); - SubtensorModule::append_neuron(netuid, &hotkey2, 0); - crate::Owner::::insert(hotkey, coldkey); - crate::Owner::::insert(hotkey2, coldkey); - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - - let min_stake = TaoBalance::from(500_000_000_000_u64); - // Set the minimum stake - SubtensorModule::set_stake_threshold(min_stake.into()); - - // Verify stake is less than minimum - assert!(SubtensorModule::get_total_stake_for_hotkey(&hotkey) < min_stake); - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - - let extension = SubtensorTransactionExtension::::new(); - // Submit to the signed extension validate function - let result_no_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result_no_stake.unwrap_err(), - CustomTransactionError::StakeAmountTooLow.into() - ); - - // Increase the stake to be equal to the minimum - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - min_stake + fee.into() - )); - - // Verify stake is equal to minimum - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey), - min_stake - ); - - // Try to reveal weights without a commit - let result_no_commit = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - assert_eq!( - result_no_commit.unwrap_err(), - CustomTransactionError::CommitNotFound.into() - ); - - // Add the commit to the hotkey - WeightCommits::::mutate(NetUidStorageIndex::from(netuid), hotkey, |maybe_commits| { - let mut commits: VecDeque<(H256, u64, u64, u64)> = - maybe_commits.take().unwrap_or_default(); - commits.push_back(( - commit_hash, - commit_block, - first_reveal_block, - last_reveal_block, - )); - *maybe_commits = Some(commits); - }); - - // Try to reveal weights in wrong epoch - let result_invalid_epoch = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - assert_eq!( - result_invalid_epoch.unwrap_err(), - CustomTransactionError::CommitBlockNotInRevealRange.into() - ); - - System::set_block_number(commit_block + 2 * tempo as u64); - - // Submit to the signed extension validate function - let result_valid_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Now the call should pass - assert_ok!(result_valid_stake); - - // Try with more stake than minimum - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - DefaultMinStake::::get() * 10.into() - )); - - // Verify stake is more than minimum - assert!(SubtensorModule::get_total_stake_for_hotkey(&hotkey) > min_stake); - - let result_more_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // The call should still pass - assert_ok!(result_more_stake); - - System::set_block_number(commit_block + 10 * tempo as u64); - - // Submit to the signed extension validate function - let result_too_late = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - - assert_eq!( - result_too_late.unwrap_err(), - CustomTransactionError::CommitBlockNotInRevealRange.into() - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_batch_reveal_weights_validate --exact --show-output --nocapture -#[test] -fn test_batch_reveal_weights_validate() { - // Testing the signed extension validate function - // correctly filters batch_reveal_weights transaction for all error conditions. - - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let coldkey = U256::from(0); - let hotkey: U256 = U256::from(1); - let hotkey2: U256 = U256::from(2); - let tempo = 1; - assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - - let who = hotkey; // The hotkey signs this transaction - - // Create test data for batch operations - let uids_list: Vec> = vec![vec![0, 1], vec![1, 0]]; - let values_list: Vec> = vec![vec![10, 20], vec![30, 40]]; - let salts_list: Vec> = - vec![vec![1, 2, 3, 4, 5, 6, 7, 8], vec![8, 7, 6, 5, 4, 3, 2, 1]]; - let version_keys: Vec = vec![0, 0]; - - // Create the batch reveal call - let call = RuntimeCall::SubtensorModule(SubtensorCall::batch_reveal_weights { - netuid, - uids_list: uids_list.clone(), - values_list: values_list.clone(), - salts_list: salts_list.clone(), - version_keys: version_keys.clone(), - }); - - // Create netuid - add_network(netuid, tempo, 0); - // Register the hotkeys - SubtensorModule::append_neuron(netuid, &hotkey, 0); - SubtensorModule::append_neuron(netuid, &hotkey2, 0); - crate::Owner::::insert(hotkey, coldkey); - crate::Owner::::insert(hotkey2, coldkey); - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - - let min_stake = TaoBalance::from(500_000_000_000_u64); - // Set the minimum stake - SubtensorModule::set_stake_threshold(min_stake.into()); - - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - - // Test 1: StakeAmountTooLow - Verify stake is less than minimum - assert!(SubtensorModule::get_total_stake_for_hotkey(&hotkey) < min_stake); - - let result_no_stake = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail with StakeAmountTooLow - assert_eq!( - result_no_stake.unwrap_err(), - CustomTransactionError::StakeAmountTooLow.into() - ); - - // Increase the stake to be equal to the minimum - assert_ok!(SubtensorModule::do_add_stake( - RuntimeOrigin::signed(hotkey), - hotkey, - netuid, - min_stake - )); - - // Verify stake is now sufficient - assert!(SubtensorModule::get_total_stake_for_hotkey(&hotkey) >= min_stake); - - // Test 2: InputLengthsUnequal - Test unequal input lengths - let call_unequal_lengths = - RuntimeCall::SubtensorModule(SubtensorCall::batch_reveal_weights { - netuid, - uids_list: vec![vec![0, 1], vec![1, 0], vec![2, 3]], // Extra element - values_list: values_list.clone(), - salts_list: salts_list.clone(), - version_keys: version_keys.clone(), - }); - - let result_unequal_lengths = extension.validate( - RawOrigin::Signed(who).into(), - &call_unequal_lengths, - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - - assert_eq!( - result_unequal_lengths.unwrap_err(), - CustomTransactionError::InputLengthsUnequal.into() - ); - - // Should fail - but this error is checked in do_batch_reveal_weights, - // so the signed extension should pass but the actual call should fail - // We'll test the actual error in the direct function call below - - // Test 3: CommitNotFound - Try to reveal without any commits - let result = SubtensorModule::do_batch_reveal_weights( - RuntimeOrigin::signed(hotkey), - netuid, - uids_list.clone(), - values_list.clone(), - salts_list.clone(), - version_keys.clone(), - ); - assert_err!(result, Error::::NoWeightsCommitFound); - - // Now create commits for testing reveal range errors - let commit_hashes: Vec = uids_list - .iter() - .zip(values_list.iter()) - .zip(salts_list.iter().zip(version_keys.iter())) - .map(|((uids, values), (salt, version_key))| { - BlakeTwo256::hash_of(&( - hotkey, - netuid, - uids.clone(), - values.clone(), - salt.clone(), - *version_key, - )) - }) - .collect(); - - // Commit weights for each hash - for commit_hash in &commit_hashes { - assert_ok!(SubtensorModule::commit_weights( - RuntimeOrigin::signed(hotkey), - netuid, - *commit_hash - )); - } - - let commit_block = SubtensorModule::get_current_block_as_u64(); - - // Test 5: CommitBlockNotInRevealRange - Try to reveal too early - let result_too_early = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - assert_eq!( - result_too_early.unwrap_err(), - CustomTransactionError::CommitBlockNotInRevealRange.into() - ); - - // Move to valid reveal period - System::set_block_number(commit_block + 2 * tempo as u64); - - // Now the call should pass the signed extension validation - let result_valid_time = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - assert_ok!(result_valid_time); - - // Test 6: CommitBlockNotInRevealRange - Try to reveal too late - System::set_block_number(commit_block + 10 * tempo as u64); - - let result_too_late = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - assert_eq!( - result_too_late.unwrap_err(), - CustomTransactionError::CommitBlockNotInRevealRange.into() - ); - }); -} - // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_set_weights_is_root_error --exact --show-output --nocapture #[test] fn test_set_weights_is_root_error() { @@ -783,7 +187,7 @@ fn test_set_stake_threshold_failed() { add_network_disable_commit_reveal(netuid, 1, 0); register_ok_neuron(netuid, hotkey, coldkey, 2143124); SubtensorModule::set_stake_threshold(20_000_000_000_000); - add_balance_to_coldkey_account(&hotkey, u64::MAX.into()); + add_balance_to_coldkey_account(&hotkey, 20_000_000_000_000_000_u64.into()); // Check the signed extension function. assert_eq!(SubtensorModule::get_stake_threshold(), 20_000_000_000_000); @@ -2231,7 +1635,7 @@ fn test_tempo_change_during_commit_reveal_process() { let tempo_before_next_reveal: u16 = 200; log::info!("Changing tempo to {tempo_before_next_reveal}"); - SubtensorModule::set_tempo(netuid, tempo_before_next_reveal); + SubtensorModule::set_tempo_unchecked(netuid, tempo_before_next_reveal); step_epochs(1, netuid); log::info!( @@ -2264,7 +1668,7 @@ fn test_tempo_change_during_commit_reveal_process() { let tempo: u16 = 150; log::info!("Changing tempo to {tempo}"); - SubtensorModule::set_tempo(netuid, tempo); + SubtensorModule::set_tempo_unchecked(netuid, tempo); step_epochs(1, netuid); log::info!( @@ -2287,7 +1691,7 @@ fn test_tempo_change_during_commit_reveal_process() { let tempo: u16 = 1050; log::info!("Changing tempo to {tempo}"); - SubtensorModule::set_tempo(netuid, tempo); + SubtensorModule::set_tempo_unchecked(netuid, tempo); assert_ok!(SubtensorModule::commit_weights( RuntimeOrigin::signed(hotkey), @@ -2301,7 +1705,7 @@ fn test_tempo_change_during_commit_reveal_process() { let tempo: u16 = 805; log::info!("Changing tempo to {tempo}"); - SubtensorModule::set_tempo(netuid, tempo); + SubtensorModule::set_tempo_unchecked(netuid, tempo); step_epochs(1, netuid); log::info!( @@ -3149,7 +2553,7 @@ fn test_tempo_and_reveal_period_change_during_commit_reveal_process() { // Step 2: Change tempo and reveal period after commit let new_tempo: u16 = 50; let new_reveal_period: u64 = 2; - SubtensorModule::set_tempo(netuid, new_tempo); + SubtensorModule::set_tempo_unchecked(netuid, new_tempo); assert_ok!(SubtensorModule::set_reveal_period(netuid, new_reveal_period)); log::info!( "Changed tempo to {new_tempo} and reveal period to {new_reveal_period}" @@ -3203,7 +2607,7 @@ fn test_tempo_and_reveal_period_change_during_commit_reveal_process() { // Step 4: Change tempo and reveal period again after reveal let new_tempo_after_reveal: u16 = 200; let new_reveal_period_after_reveal: u64 = 1; - SubtensorModule::set_tempo(netuid, new_tempo_after_reveal); + SubtensorModule::set_tempo_unchecked(netuid, new_tempo_after_reveal); assert_ok!(SubtensorModule::set_reveal_period( netuid, new_reveal_period_after_reveal @@ -3427,49 +2831,31 @@ fn test_reveal_at_exact_block() { commit_hash )); - let commit_block = SubtensorModule::get_current_block_as_u64(); - let commit_epoch = SubtensorModule::get_epoch_index(netuid, commit_block); - let reveal_epoch = commit_epoch.saturating_add(reveal_period); + // Epoch the commit was tagged with (counter is the canonical index). + let commit_epoch = + crate::WeightCommits::::get(NetUidStorageIndex::from(netuid), hotkey) + .and_then(|q| q.back().map(|(_, e, _, _)| *e)) + .expect("commit stored"); - // Calculate the block number where the reveal epoch starts - let tempo_plus_one = (tempo as u64).saturating_add(1); - let netuid_plus_one = (u16::from(netuid) as u64).saturating_add(1); - let reveal_epoch_start_block = reveal_epoch - .saturating_mul(tempo_plus_one) - .saturating_sub(netuid_plus_one); - - // Attempt to reveal before the reveal epoch starts - let current_block = SubtensorModule::get_current_block_as_u64(); - if current_block < reveal_epoch_start_block { - // Advance to one block before the reveal epoch starts - let blocks_to_advance = reveal_epoch_start_block - current_block; - if blocks_to_advance > 1 { - // Advance to one block before the reveal epoch - let new_block_number = current_block + blocks_to_advance - 1; - System::set_block_number(new_block_number); - } - - // Attempt to reveal too early - assert_err!( - SubtensorModule::reveal_weights( - RuntimeOrigin::signed(hotkey), - netuid, - uids.clone(), - weight_values.clone(), - salt.clone(), - version_key - ), - Error::::RevealTooEarly - ); + // Attempt to reveal before the reveal epoch — too early. + assert_err!( + SubtensorModule::reveal_weights( + RuntimeOrigin::signed(hotkey), + netuid, + uids.clone(), + weight_values.clone(), + salt.clone(), + version_key + ), + Error::::RevealTooEarly + ); - // Advance one more block to reach the exact reveal epoch start block - System::set_block_number(reveal_epoch_start_block); - } else { - // If we're already at or past the reveal epoch start block - System::set_block_number(reveal_epoch_start_block); - } + // Advance the epoch counter into the reveal epoch; pin the scheduler. + SubnetEpochIndex::::insert(netuid, commit_epoch + reveal_period); + LastEpochBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); + PendingEpochAt::::insert(netuid, 0); - // Reveal at the exact allowed block + // Reveal at the exact allowed epoch assert_ok!(SubtensorModule::reveal_weights( RuntimeOrigin::signed(hotkey), netuid, @@ -3508,18 +2894,13 @@ fn test_reveal_at_exact_block() { new_commit_hash )); - // Advance blocks to after the commit expires - let commit_block = SubtensorModule::get_current_block_as_u64(); - let commit_epoch = SubtensorModule::get_epoch_index(netuid, commit_block); - let reveal_epoch = commit_epoch.saturating_add(reveal_period); - let expiration_epoch = reveal_epoch.saturating_add(1); - let expiration_epoch_start_block = expiration_epoch * tempo_plus_one - netuid_plus_one; - - let current_block = SubtensorModule::get_current_block_as_u64(); - if current_block < expiration_epoch_start_block { - // Advance to the block where the commit expires - System::set_block_number(expiration_epoch_start_block); - } + // Advance the epoch counter past the reveal epoch — commit expired. + let new_commit_epoch = + crate::WeightCommits::::get(NetUidStorageIndex::from(netuid), hotkey) + .and_then(|q| q.back().map(|(_, e, _, _)| *e)) + .expect("commit stored"); + SubnetEpochIndex::::insert(netuid, new_commit_epoch + reveal_period + 1); + LastEpochBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); // Attempt to reveal after the commit has expired assert_err!( @@ -4272,7 +3653,7 @@ fn test_highly_concurrent_commits_and_reveals_with_multiple_hotkeys() { } // ==== Modify Network Parameters During Commits ==== - SubtensorModule::set_tempo(netuid, 150); + SubtensorModule::set_tempo_unchecked(netuid, 150); assert_ok!(SubtensorModule::set_reveal_period(netuid, 7)); log::info!("Changed tempo to 150 and reveal_period to 7 during commits."); @@ -4318,7 +3699,7 @@ fn test_highly_concurrent_commits_and_reveals_with_multiple_hotkeys() { } // ==== Change Network Parameters Again ==== - SubtensorModule::set_tempo(netuid, 200); + SubtensorModule::set_tempo_unchecked(netuid, 200); assert_ok!(SubtensorModule::set_reveal_period(netuid, 10)); log::info!("Changed tempo to 200 and reveal_period to 10 after initial reveals."); @@ -4421,146 +3802,6 @@ fn test_highly_concurrent_commits_and_reveals_with_multiple_hotkeys() { }) } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_get_reveal_blocks --exact --show-output --nocapture -#[test] -fn test_get_reveal_blocks() { - new_test_ext(1).execute_with(|| { - // **1. Define Test Parameters** - let netuid = NetUid::from(1); - let uids: Vec = vec![0, 1]; - let weight_values: Vec = vec![10, 10]; - let salt: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8]; - let version_key: u64 = 0; - let hotkey: U256 = U256::from(1); - - // **2. Generate the Commit Hash** - let commit_hash: H256 = BlakeTwo256::hash_of(&( - hotkey, - netuid, - uids.clone(), - weight_values.clone(), - salt.clone(), - version_key, - )); - - // **3. Initialize the Block Number to 0** - System::set_block_number(0); - - // **4. Define Network Parameters** - let tempo: u16 = 5; - add_network(netuid, tempo, 0); - - // **5. Register Neurons and Configure the Network** - register_ok_neuron(netuid, U256::from(3), U256::from(4), 300_000); - register_ok_neuron(netuid, U256::from(1), U256::from(2), 100_000); - SubtensorModule::set_stake_threshold(0); - SubtensorModule::set_weights_set_rate_limit(netuid, 5); - SubtensorModule::set_validator_permit_for_uid(netuid, 0, true); - SubtensorModule::set_validator_permit_for_uid(netuid, 1, true); - SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); - add_balance_to_coldkey_account(&U256::from(0), 1.into()); - add_balance_to_coldkey_account(&U256::from(1), 1.into()); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &(U256::from(0)), - &(U256::from(0)), - netuid, - 1.into(), - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &(U256::from(1)), - &(U256::from(1)), - netuid, - 1.into(), - ); - - // **6. Commit Weights at Block 0** - assert_ok!(SubtensorModule::commit_weights( - RuntimeOrigin::signed(hotkey), - netuid, - commit_hash - )); - - // **7. Retrieve the Reveal Blocks Using `get_reveal_blocks`** - let (first_reveal_block, last_reveal_block) = SubtensorModule::get_reveal_blocks(netuid, 0); - - // **8. Assert Correct Calculation of Reveal Blocks** - // With tempo=5, netuid=1, reveal_period=1: - // commit_epoch = (0 + 2) / 6 = 0 - // reveal_epoch = 0 + 1 = 1 - // first_reveal_block = 1 * 6 - 2 = 4 - // last_reveal_block = 4 + 5 = 9 - assert_eq!(first_reveal_block, 4); - assert_eq!(last_reveal_block, 9); - - // **9. Attempt to Reveal Before `first_reveal_block` (Block 3)** - step_block(3); // Advance to block 3 - let result = SubtensorModule::reveal_weights( - RuntimeOrigin::signed(hotkey), - netuid, - uids.clone(), - weight_values.clone(), - salt.clone(), - version_key, - ); - assert_err!(result, Error::::RevealTooEarly); - - // **10. Advance to `first_reveal_block` (Block 4)** - step_block(1); // Advance to block 4 - let result = SubtensorModule::reveal_weights( - RuntimeOrigin::signed(hotkey), - netuid, - uids.clone(), - weight_values.clone(), - salt.clone(), - version_key, - ); - assert_ok!(result); - - // **11. Attempt to Reveal Again at Block 4 (Should Fail)** - let result = SubtensorModule::reveal_weights( - RuntimeOrigin::signed(hotkey), - netuid, - uids.clone(), - weight_values.clone(), - salt.clone(), - version_key, - ); - assert_err!(result, Error::::NoWeightsCommitFound); - - // **12. Advance to After `last_reveal_block` (Block 10)** - step_block(6); // Advance from block 4 to block 10 - - // **13. Attempt to Reveal at Block 10 (Should Fail)** - let result = SubtensorModule::reveal_weights( - RuntimeOrigin::signed(hotkey), - netuid, - uids.clone(), - weight_values.clone(), - salt.clone(), - version_key, - ); - assert_err!(result, Error::::NoWeightsCommitFound); - - // **14. Attempt to Reveal Outside of Any Reveal Window (No Commit)** - let result = SubtensorModule::reveal_weights( - RuntimeOrigin::signed(hotkey), - netuid, - uids.clone(), - weight_values.clone(), - salt.clone(), - version_key, - ); - assert_err!(result, Error::::NoWeightsCommitFound); - - // **15. Verify that All Commits Have Been Removed from Storage** - let commits = crate::WeightCommits::::get(NetUidStorageIndex::from(netuid), hotkey); - assert!( - commits.is_none(), - "Commits should be cleared after successful reveal" - ); - }) -} - // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::weights::test_commit_weights_rate_limit --exact --show-output --nocapture #[test] fn test_commit_weights_rate_limit() { @@ -5946,8 +5187,13 @@ fn test_reveal_crv3_commits_removes_past_epoch_commits() { // --------------------------------------------------------------------- // Put dummy commits into the two epochs immediately *before* current. // --------------------------------------------------------------------- + // Establish a non-zero epoch counter and pin the scheduler so the reveal + // pass sees exactly this epoch (no look-ahead increment). + let cur_epoch: u64 = 10; + SubnetEpochIndex::::insert(netuid, cur_epoch); + LastEpochBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); + PendingEpochAt::::insert(netuid, 0); let cur_block = SubtensorModule::get_current_block_as_u64(); - let cur_epoch = SubtensorModule::get_epoch_index(netuid, cur_block); let past_epoch = cur_epoch.saturating_sub(2); // definitely < reveal_epoch let reveal_epoch = cur_epoch.saturating_sub(1); // == cur_epoch - reveal_period @@ -6226,18 +5472,16 @@ fn test_reveal_crv3_commits_max_neurons() { }); } +// `get_first_block_of_epoch` is a legacy modulo helper — NOT used by live +// commit-reveal logic #[test] fn test_get_first_block_of_epoch_epoch_zero() { new_test_ext(1).execute_with(|| { let netuid: NetUid = NetUid::from(1); - let tempo: u16 = 10; - add_network(netuid, tempo, 0); - - let first_block = SubtensorModule::get_first_block_of_epoch(netuid, 0); - assert_eq!(first_block, 0); + add_network(netuid, 10, 0); - // Cross-check: epoch at block 0 should be 0 - assert_eq!(SubtensorModule::get_epoch_index(netuid, 0), 0); + // 0 * 11 - 2, saturating at 0. + assert_eq!(SubtensorModule::get_first_block_of_epoch(netuid, 0), 0); }); } @@ -6245,15 +5489,10 @@ fn test_get_first_block_of_epoch_epoch_zero() { fn test_get_first_block_of_epoch_small_epoch() { new_test_ext(1).execute_with(|| { let netuid: NetUid = NetUid::from(0); - let tempo: u16 = 1; - add_network(netuid, tempo, 0); - - let first_block = SubtensorModule::get_first_block_of_epoch(netuid, 1); - assert_eq!(first_block, 1); // 1 * 2 - 1 = 1 + add_network(netuid, 1, 0); - // Cross-check - assert_eq!(SubtensorModule::get_epoch_index(netuid, 1), 1); - assert_eq!(SubtensorModule::get_epoch_index(netuid, 0), 0); + // 1 * 2 - 1 = 1. + assert_eq!(SubtensorModule::get_first_block_of_epoch(netuid, 1), 1); }); } @@ -6261,15 +5500,10 @@ fn test_get_first_block_of_epoch_small_epoch() { fn test_get_first_block_of_epoch_with_offset() { new_test_ext(1).execute_with(|| { let netuid: NetUid = NetUid::from(1); - let tempo: u16 = 10; - add_network(netuid, tempo, 0); + add_network(netuid, 10, 0); - let first_block = SubtensorModule::get_first_block_of_epoch(netuid, 1); - assert_eq!(first_block, 9); // 1 * 11 - 2 = 9 - - // Cross-check - assert_eq!(SubtensorModule::get_epoch_index(netuid, 9), 1); - assert_eq!(SubtensorModule::get_epoch_index(netuid, 8), 0); + // 1 * 11 - 2 = 9. + assert_eq!(SubtensorModule::get_first_block_of_epoch(netuid, 1), 9); }); } @@ -6277,61 +5511,14 @@ fn test_get_first_block_of_epoch_with_offset() { fn test_get_first_block_of_epoch_large_epoch() { new_test_ext(1).execute_with(|| { let netuid: NetUid = NetUid::from(0); - let tempo: u16 = 100; - add_network(netuid, tempo, 0); + add_network(netuid, 100, 0); let epoch: u64 = 1000; - let first_block = SubtensorModule::get_first_block_of_epoch(netuid, epoch); - assert_eq!(first_block, epoch * 101 - 1); // No overflow for this size - - // Cross-check (simulate, as large block not runnable, but math holds) - assert_eq!(first_block + 1, epoch * 101); - }); -} - -#[test] -fn test_get_first_block_of_epoch_step_blocks_and_assert_with_until_next() { - new_test_ext(1).execute_with(|| { - let netuid: NetUid = NetUid::from(1); - let tempo: u16 = 10; - add_network(netuid, tempo, 0); - - let mut current_block: u64 = 0; - for expected_epoch in 0..10u64 { - let expected_first = SubtensorModule::get_first_block_of_epoch(netuid, expected_epoch); - - // Step blocks until we reach the start of this epoch - while current_block < expected_first { - run_to_block(current_block + 1); - current_block += 1; - } - - // Assert we are at the first block of the epoch - assert_eq!(current_block, expected_first); - assert_eq!( - SubtensorModule::get_epoch_index(netuid, current_block), - expected_epoch - ); - - // From here, blocks_until_next_epoch should point to the start of next epoch - let until_next = SubtensorModule::blocks_until_next_epoch(netuid, tempo, current_block); - let next_first = SubtensorModule::get_first_block_of_epoch(netuid, expected_epoch + 1); - assert_eq!(current_block + until_next + 1, next_first); // +1 since until is blocks to end, +1 to start next - - // Advance to near end of this epoch - let last_block = next_first.saturating_sub(1); - run_to_block(last_block); - current_block = System::block_number(); - assert_eq!( - SubtensorModule::get_epoch_index(netuid, current_block), - expected_epoch - ); - - // Until next from near end - let until_next_end = - SubtensorModule::blocks_until_next_epoch(netuid, tempo, current_block); - assert_eq!(current_block + until_next_end + 1, next_first); - } + // 1000 * 101 - 1. + assert_eq!( + SubtensorModule::get_first_block_of_epoch(netuid, epoch), + epoch * 101 - 1 + ); }); } @@ -6671,11 +5858,14 @@ fn test_reveal_crv3_commits_retry_on_missing_pulse() { .map(|(e, _)| e) .expect("commit stored"); - // first block of reveal epoch (commit_epoch + RP) - let first_reveal_epoch = stored_epoch + SubtensorModule::get_reveal_period(netuid); - let first_reveal_block = - SubtensorModule::get_first_block_of_epoch(netuid, first_reveal_epoch); - run_to_block_no_epoch(netuid, first_reveal_block); + // Place the subnet's epoch counter at the commit's reveal epoch + // (`commit_epoch + reveal_period`). The counter is the canonical epoch + // index; pin `LastEpochBlock`/`PendingEpochAt` so `should_run_epoch` stays + // false and the look-ahead does not skip past the reveal epoch. + let reveal_epoch = stored_epoch + SubtensorModule::get_reveal_period(netuid); + SubnetEpochIndex::::insert(netuid, reveal_epoch); + LastEpochBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); + PendingEpochAt::::insert(netuid, 0); // run *one* block inside reveal epoch without pulse → commit should stay queued step_block(1); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index a1c0309b24..7e62e4c7ec 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -54,13 +54,18 @@ impl Pallet { /// Returns true if the current block is within the terminal freeze window of the tempo for the /// given subnet. During this window, admin ops are prohibited to avoid interference with - /// validator weight submissions. + /// validator weight submissions. Engages immediately on a pending manual trigger (so the trigger + /// arms the freeze for the entire countdown to `PendingEpochAt`). pub fn is_in_admin_freeze_window(netuid: NetUid, current_block: u64) -> bool { let tempo = Self::get_tempo(netuid); if tempo == 0 { return false; } - let remaining = Self::blocks_until_next_epoch(netuid, tempo, current_block); + let pending = PendingEpochAt::::get(netuid); + if pending > 0 && pending > current_block { + return true; + } + let remaining = Self::blocks_until_next_auto_epoch(netuid, tempo, current_block); let window = AdminFreezeWindow::::get() as u64; remaining < window } @@ -102,10 +107,23 @@ impl Pallet { // ======================== // ==== Global Setters ==== // ======================== - pub fn set_tempo(netuid: NetUid, tempo: u16) { + /// Unchecked tempo write used by tests, precompiles, and internal helpers. + /// Does NOT reset `LastEpochBlock` — that is the responsibility of the owner-side + /// `set_tempo` extrinsic and `sudo_set_tempo` (root), both of which perform the cycle + /// reset explicitly. + pub fn set_tempo_unchecked(netuid: NetUid, tempo: u16) { Tempo::::insert(netuid, tempo); Self::deposit_event(Event::TempoSet(netuid, tempo)); } + + /// Sets `Tempo` and resets the state-based scheduler anchor `LastEpochBlock` + /// to the current block + pub fn apply_tempo_with_cycle_reset(netuid: NetUid, tempo: u16) { + Self::set_tempo_unchecked(netuid, tempo); + let now = Self::get_current_block_as_u64(); + LastEpochBlock::::insert(netuid, now); + } + pub fn set_last_adjustment_block(netuid: NetUid, last_adjustment_block: u64) { LastAdjustmentBlock::::insert(netuid, last_adjustment_block); } @@ -359,6 +377,15 @@ impl Pallet { // ========= Sudo ========= // ======================== + // Per-block epoch cap (dynamic tempo throttle) + pub fn get_max_epochs_per_block() -> u8 { + MaxEpochsPerBlock::::get() + } + pub fn set_max_epochs_per_block(max_epochs_per_block: u8) { + MaxEpochsPerBlock::::put(max_epochs_per_block); + Self::deposit_event(Event::MaxEpochsPerBlockSet(max_epochs_per_block)); + } + // Configure tx rate limiting pub fn get_tx_rate_limit() -> u64 { TxRateLimit::::get() @@ -582,6 +609,30 @@ impl Pallet { Self::deposit_event(Event::ActivityCutoffSet(netuid, activity_cutoff)); } + /// Effective activity cutoff in blocks, derived from `ActivityCutoffFactorMilli` and `Tempo`. + /// `cutoff_blocks = (factor × tempo) / 1000`, clamped to ≥ 1. + pub fn get_activity_cutoff_blocks(netuid: NetUid) -> u64 { + let factor_milli = ActivityCutoffFactorMilli::::get(netuid) as u64; + let tempo = Self::get_tempo(netuid) as u64; + factor_milli + .saturating_mul(tempo) + .checked_div(1000) + .unwrap_or(0) + .max(1) + } + + pub fn get_activity_cutoff_factor_milli(netuid: NetUid) -> u32 { + ActivityCutoffFactorMilli::::get(netuid) + } + + pub fn set_activity_cutoff_factor_milli(netuid: NetUid, factor_milli: u32) { + ActivityCutoffFactorMilli::::insert(netuid, factor_milli); + Self::deposit_event(Event::ActivityCutoffFactorMilliSet { + netuid, + factor_milli, + }); + } + // Registration Toggle utils pub fn get_network_registration_allowed(netuid: NetUid) -> bool { NetworkRegistrationAllowed::::get(netuid) diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index e9559f2c6d..7b93d620ac 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -17,6 +17,7 @@ pub enum TransactionType { MechanismEmission, MaxUidsTrimming, AddStakeBurn, + TempoUpdate, } impl TransactionType { @@ -46,6 +47,7 @@ impl TransactionType { } Self::SetSNOwnerHotkey => DefaultSetSNOwnerHotkeyRateLimit::::get(), Self::AddStakeBurn => Tempo::::get(netuid) as u64, + Self::TempoUpdate => MIN_TEMPO as u64, _ => self.rate_limit::(), } @@ -144,6 +146,7 @@ impl From for u16 { TransactionType::MechanismEmission => 8, TransactionType::MaxUidsTrimming => 9, TransactionType::AddStakeBurn => 10, + TransactionType::TempoUpdate => 11, } } } @@ -162,6 +165,7 @@ impl From for TransactionType { 8 => TransactionType::MechanismEmission, 9 => TransactionType::MaxUidsTrimming, 10 => TransactionType::AddStakeBurn, + 11 => TransactionType::TempoUpdate, _ => TransactionType::Unknown, } } @@ -206,6 +210,8 @@ pub enum Hyperparameter { BurnIncreaseMult = 27, SubnetEmissionEnabled = 28, MinChildkeyTake = 29, + ActivityCutoffFactorMilli = 30, + TriggerEpoch = 31, } impl Pallet { diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs index 4876a830a0..478c37ad7c 100644 --- a/pallets/subtensor/src/weights.rs +++ b/pallets/subtensor/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_subtensor` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-06-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervm3jyl0`, CPU: `AMD EPYC 9V74 80-Core Processor` +//! HOSTNAME: `runnervm7b5n9`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.iVvhDcFf4i +// --output=/tmp/tmp.hYIH73ZUrd // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -92,6 +92,16 @@ pub trait WeightInfo { fn set_pending_childkey_cooldown() -> Weight; fn lock_stake() -> Weight; fn move_lock() -> Weight; + fn associate_evm_key() -> Weight; + fn set_tempo() -> Weight; + fn set_activity_cutoff_factor() -> Weight; + fn trigger_epoch() -> Weight; + fn check_coldkey_swap_extension() -> Weight; + fn check_weights_extension() -> Weight; + fn check_rate_limits_extension() -> Weight; + fn check_delegate_take_extension() -> Weight; + fn check_serving_endpoints_extension() -> Weight; + fn check_evm_key_association_extension() -> Weight; } /// Weights for `pallet_subtensor` using the Substrate node and recommended hardware. @@ -121,28 +131,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:1) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:5 w:5) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `Swap::LastPositionId` (r:1 w:1) - /// Proof: `Swap::LastPositionId` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -187,18 +179,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::IsNetworkMember` (r:0 w:1) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::AlphaSqrtPrice` (r:0 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:0 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Swap::SwapBalancer` (r:0 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `1716` - // Estimated: `13600` - // Minimum execution time: 374_002_000 picoseconds. - Weight::from_parts(380_312_000, 13600) - .saturating_add(T::DbWeight::get().reads(48_u64)) - .saturating_add(T::DbWeight::get().writes(40_u64)) + // Measured: `1865` + // Estimated: `6148` + // Minimum execution time: 343_840_000 picoseconds. + Weight::from_parts(349_872_000, 6148) + .saturating_add(T::DbWeight::get().reads(34_u64)) + .saturating_add(T::DbWeight::get().writes(29_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -236,10 +226,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `188792` - // Estimated: `10327382` - // Minimum execution time: 16_598_698_000 picoseconds. - Weight::from_parts(16_897_861_000, 10327382) + // Measured: `188820` + // Estimated: `10327410` + // Minimum execution time: 15_317_357_000 picoseconds. + Weight::from_parts(15_513_610_000, 10327410) .saturating_add(T::DbWeight::get().reads(4112_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -251,20 +241,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -277,8 +261,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) @@ -309,47 +291,47 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2633` + // Measured: `2226` // Estimated: `8727` - // Minimum execution time: 459_249_000 picoseconds. - Weight::from_parts(476_173_000, 8727) - .saturating_add(T::DbWeight::get().reads(38_u64)) - .saturating_add(T::DbWeight::get().writes(18_u64)) + // Minimum execution time: 639_698_000 picoseconds. + Weight::from_parts(664_064_000, 8727) + .saturating_add(T::DbWeight::get().reads(32_u64)) + .saturating_add(T::DbWeight::get().writes(16_u64)) } - /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) - /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Axons` (r:1 w:1) /// Proof: `SubtensorModule::Axons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn serve_axon() -> Weight { // Proof Size summary in bytes: - // Measured: `801` - // Estimated: `6741` - // Minimum execution time: 32_538_000 picoseconds. - Weight::from_parts(33_289_000, 6741) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `713` + // Estimated: `4178` + // Minimum execution time: 30_307_000 picoseconds. + Weight::from_parts(31_278_000, 4178) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) - /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Prometheus` (r:1 w:1) /// Proof: `SubtensorModule::Prometheus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn serve_prometheus() -> Weight { // Proof Size summary in bytes: - // Measured: `774` - // Estimated: `6714` - // Minimum execution time: 29_163_000 picoseconds. - Weight::from_parts(29_784_000, 6714) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `812` + // Estimated: `4277` + // Minimum execution time: 28_262_000 picoseconds. + Weight::from_parts(29_294_000, 4277) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -376,28 +358,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:1) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:5 w:5) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `Swap::LastPositionId` (r:1 w:1) - /// Proof: `Swap::LastPositionId` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -442,18 +406,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::IsNetworkMember` (r:0 w:1) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::AlphaSqrtPrice` (r:0 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:0 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Swap::SwapBalancer` (r:0 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) fn burned_register() -> Weight { // Proof Size summary in bytes: - // Measured: `1649` - // Estimated: `13600` - // Minimum execution time: 362_295_000 picoseconds. - Weight::from_parts(368_123_000, 13600) - .saturating_add(T::DbWeight::get().reads(48_u64)) - .saturating_add(T::DbWeight::get().writes(40_u64)) + // Measured: `1798` + // Estimated: `6148` + // Minimum execution time: 332_358_000 picoseconds. + Weight::from_parts(336_815_000, 6148) + .saturating_add(T::DbWeight::get().reads(34_u64)) + .saturating_add(T::DbWeight::get().writes(29_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -501,10 +463,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) fn root_register() -> Weight { // Proof Size summary in bytes: - // Measured: `1445` - // Estimated: `4910` - // Minimum execution time: 102_751_000 picoseconds. - Weight::from_parts(104_294_000, 4910) + // Measured: `1516` + // Estimated: `4981` + // Minimum execution time: 102_089_000 picoseconds. + Weight::from_parts(103_934_000, 4981) .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(16_u64)) } @@ -534,16 +496,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::TotalNetworks` (r:1 w:1) /// Proof: `SubtensorModule::TotalNetworks` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Kappa` (r:1 w:1) @@ -606,6 +564,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:0 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:0 w:1) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:0 w:1) @@ -624,12 +584,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network() -> Weight { // Proof Size summary in bytes: - // Measured: `1459` - // Estimated: `9874` - // Minimum execution time: 277_470_000 picoseconds. - Weight::from_parts(282_297_000, 9874) - .saturating_add(T::DbWeight::get().reads(42_u64)) - .saturating_add(T::DbWeight::get().writes(49_u64)) + // Measured: `1532` + // Estimated: `9947` + // Minimum execution time: 271_405_000 picoseconds. + Weight::from_parts(277_125_000, 9947) + .saturating_add(T::DbWeight::get().reads(40_u64)) + .saturating_add(T::DbWeight::get().writes(48_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -643,27 +603,35 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastUpdate` (r:1 w:1) /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) - /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::BlocksSinceLastStep` (r:1 w:0) + /// Proof: `SubtensorModule::BlocksSinceLastStep` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:1 w:0) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) fn commit_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1071` - // Estimated: `4536` - // Minimum execution time: 60_340_000 picoseconds. - Weight::from_parts(61_421_000, 4536) - .saturating_add(T::DbWeight::get().reads(10_u64)) + // Measured: `1249` + // Estimated: `4714` + // Minimum execution time: 67_886_000 picoseconds. + Weight::from_parts(69_049_000, 4714) + .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) @@ -698,11 +666,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reveal_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1589` - // Estimated: `7529` - // Minimum execution time: 108_541_000 picoseconds. - Weight::from_parts(110_183_000, 7529) - .saturating_add(T::DbWeight::get().reads(18_u64)) + // Measured: `1650` + // Estimated: `7590` + // Minimum execution time: 109_534_000 picoseconds. + Weight::from_parts(111_227_000, 7590) + .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::TxChildkeyTakeRateLimit` (r:0 w:1) @@ -711,8 +679,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_076_000 picoseconds. - Weight::from_parts(4_647_000, 0) + // Minimum execution time: 5_570_000 picoseconds. + Weight::from_parts(5_760_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -731,10 +699,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TransactionKeyLastBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_childkey_take() -> Weight { // Proof Size summary in bytes: - // Measured: `999` - // Estimated: `4464` - // Minimum execution time: 52_278_000 picoseconds. - Weight::from_parts(53_209_000, 4464) + // Measured: `1033` + // Estimated: `4498` + // Minimum execution time: 52_527_000 picoseconds. + Weight::from_parts(53_870_000, 4498) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -750,8 +718,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `694` // Estimated: `4159` - // Minimum execution time: 43_995_000 picoseconds. - Weight::from_parts(45_167_000, 4159) + // Minimum execution time: 45_574_000 picoseconds. + Weight::from_parts(47_248_000, 4159) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -779,6 +747,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingColdkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) @@ -787,17 +757,19 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:2 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::DecayingLock` (r:1 w:0) + /// Proof: `SubtensorModule::DecayingLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey_announced() -> Weight { // Proof Size summary in bytes: - // Measured: `2175` - // Estimated: `13065` - // Minimum execution time: 286_653_000 picoseconds. - Weight::from_parts(294_536_000, 13065) - .saturating_add(T::DbWeight::get().reads(35_u64)) + // Measured: `2110` + // Estimated: `13000` + // Minimum execution time: 284_379_000 picoseconds. + Weight::from_parts(288_156_000, 13000) + .saturating_add(T::DbWeight::get().reads(37_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } /// Storage: `System::Account` (r:2 w:2) @@ -826,6 +798,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingColdkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) @@ -834,6 +808,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:2 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::DecayingLock` (r:1 w:0) + /// Proof: `SubtensorModule::DecayingLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:0 w:1) /// Proof: `SubtensorModule::ColdkeySwapAnnouncements` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapDisputes` (r:0 w:1) @@ -842,11 +818,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey() -> Weight { // Proof Size summary in bytes: - // Measured: `2231` - // Estimated: `13121` - // Minimum execution time: 310_339_000 picoseconds. - Weight::from_parts(313_503_000, 13121) - .saturating_add(T::DbWeight::get().reads(35_u64)) + // Measured: `2166` + // Estimated: `13056` + // Minimum execution time: 308_323_000 picoseconds. + Weight::from_parts(312_301_000, 13056) + .saturating_add(T::DbWeight::get().reads(37_u64)) .saturating_add(T::DbWeight::get().writes(19_u64)) } /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:1 w:0) @@ -857,8 +833,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `665` // Estimated: `4130` - // Minimum execution time: 20_290_000 picoseconds. - Weight::from_parts(21_452_000, 4130) + // Minimum execution time: 22_442_000 picoseconds. + Weight::from_parts(23_293_000, 4130) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -870,8 +846,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `613` // Estimated: `4078` - // Minimum execution time: 16_995_000 picoseconds. - Weight::from_parts(17_505_000, 4078) + // Minimum execution time: 18_695_000 picoseconds. + Weight::from_parts(19_496_000, 4078) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -883,14 +859,16 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_830_000 picoseconds. - Weight::from_parts(7_271_000, 0) + // Minimum execution time: 8_335_000 picoseconds. + Weight::from_parts(8_857_000, 0) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) @@ -925,11 +903,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn batch_reveal_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `2094` - // Estimated: `8034` - // Minimum execution time: 429_484_000 picoseconds. - Weight::from_parts(443_415_000, 8034) - .saturating_add(T::DbWeight::get().reads(18_u64)) + // Measured: `2155` + // Estimated: `8095` + // Minimum execution time: 412_226_000 picoseconds. + Weight::from_parts(421_363_000, 8095) + .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -960,10 +938,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `AlphaAssets::TotalAlphaIssuance` (`max_values`: None, `max_size`: None, mode: `Measured`) fn recycle_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1873` - // Estimated: `5338` - // Minimum execution time: 176_220_000 picoseconds. - Weight::from_parts(178_253_000, 5338) + // Measured: `1754` + // Estimated: `5219` + // Minimum execution time: 169_976_000 picoseconds. + Weight::from_parts(172_139_000, 5219) .saturating_add(T::DbWeight::get().reads(13_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -993,10 +971,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `AlphaAssets::AlphaBurned` (`max_values`: None, `max_size`: None, mode: `Measured`) fn burn_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1873` - // Estimated: `5338` - // Minimum execution time: 172_335_000 picoseconds. - Weight::from_parts(174_197_000, 5338) + // Measured: `1754` + // Estimated: `5219` + // Minimum execution time: 166_359_000 picoseconds. + Weight::from_parts(168_964_000, 5219) .saturating_add(T::DbWeight::get().reads(12_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -1016,8 +994,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1118` // Estimated: `4583` - // Minimum execution time: 37_836_000 picoseconds. - Weight::from_parts(38_907_000, 4583) + // Minimum execution time: 38_482_000 picoseconds. + Weight::from_parts(39_454_000, 4583) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1025,20 +1003,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -1055,8 +1027,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) @@ -1087,18 +1057,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2633` + // Measured: `2226` // Estimated: `8727` - // Minimum execution time: 499_258_000 picoseconds. - Weight::from_parts(516_242_000, 8727) - .saturating_add(T::DbWeight::get().reads(38_u64)) - .saturating_add(T::DbWeight::get().writes(18_u64)) + // Minimum execution time: 829_031_000 picoseconds. + Weight::from_parts(853_005_000, 8727) + .saturating_add(T::DbWeight::get().reads(32_u64)) + .saturating_add(T::DbWeight::get().writes(16_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1110,8 +1080,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -1122,18 +1090,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn move_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2060` - // Estimated: `8000` - // Minimum execution time: 222_999_000 picoseconds. - Weight::from_parts(227_526_000, 8000) + // Measured: `1979` + // Estimated: `7919` + // Minimum execution time: 214_819_000 picoseconds. + Weight::from_parts(216_061_000, 7919) .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(7_u64)) } @@ -1151,34 +1121,24 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -1197,35 +1157,27 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2564` - // Estimated: `10979` - // Minimum execution time: 435_183_000 picoseconds. - Weight::from_parts(444_777_000, 10979) - .saturating_add(T::DbWeight::get().reads(35_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)) + // Measured: `2142` + // Estimated: `10557` + // Minimum execution time: 544_281_000 picoseconds. + Weight::from_parts(569_198_000, 10557) + .saturating_add(T::DbWeight::get().reads(28_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Alpha` (r:1 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) @@ -1242,8 +1194,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -1262,12 +1212,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2598` - // Estimated: `11013` - // Minimum execution time: 475_352_000 picoseconds. - Weight::from_parts(478_116_000, 11013) - .saturating_add(T::DbWeight::get().reads(34_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)) + // Measured: `2176` + // Estimated: `10591` + // Minimum execution time: 717_343_000 picoseconds. + Weight::from_parts(740_696_000, 10591) + .saturating_add(T::DbWeight::get().reads(27_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1283,22 +1233,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:2 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:2 w:0) @@ -1309,8 +1251,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:3 w:1) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -1339,16 +1279,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `3108` - // Estimated: `11523` - // Minimum execution time: 688_567_000 picoseconds. - Weight::from_parts(707_234_000, 11523) - .saturating_add(T::DbWeight::get().reads(54_u64)) - .saturating_add(T::DbWeight::get().writes(26_u64)) + // Measured: `2662` + // Estimated: `11077` + // Minimum execution time: 921_853_000 picoseconds. + Weight::from_parts(944_515_000, 11077) + .saturating_add(T::DbWeight::get().reads(47_u64)) + .saturating_add(T::DbWeight::get().writes(24_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1360,8 +1302,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -1378,10 +1318,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::AccountFlags` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_stake() -> Weight { @@ -1403,8 +1345,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:2 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:2 w:0) @@ -1415,26 +1355,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:3 w:1) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -1463,16 +1395,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2951` - // Estimated: `11366` - // Minimum execution time: 633_996_000 picoseconds. - Weight::from_parts(655_699_000, 11366) - .saturating_add(T::DbWeight::get().reads(54_u64)) - .saturating_add(T::DbWeight::get().writes(26_u64)) + // Measured: `2505` + // Estimated: `10920` + // Minimum execution time: 717_052_000 picoseconds. + Weight::from_parts(742_019_000, 10920) + .saturating_add(T::DbWeight::get().reads(47_u64)) + .saturating_add(T::DbWeight::get().writes(24_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1486,23 +1420,31 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastUpdate` (r:1 w:1) /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) - /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::BlocksSinceLastStep` (r:1 w:0) + /// Proof: `SubtensorModule::BlocksSinceLastStep` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:1 w:0) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightsSetRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::WeightsSetRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) + /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) fn batch_commit_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1122` - // Estimated: `4587` - // Minimum execution time: 127_058_000 picoseconds. - Weight::from_parts(129_030_000, 4587) - .saturating_add(T::DbWeight::get().reads(11_u64)) + // Measured: `1300` + // Estimated: `4765` + // Minimum execution time: 144_628_000 picoseconds. + Weight::from_parts(147_224_000, 4765) + .saturating_add(T::DbWeight::get().reads(15_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) @@ -1539,10 +1481,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn batch_set_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1426` - // Estimated: `7366` - // Minimum execution time: 101_319_000 picoseconds. - Weight::from_parts(102_992_000, 7366) + // Measured: `1455` + // Estimated: `7395` + // Minimum execution time: 100_516_000 picoseconds. + Weight::from_parts(103_092_000, 7395) .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1556,10 +1498,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn decrease_take() -> Weight { // Proof Size summary in bytes: - // Measured: `793` - // Estimated: `4258` - // Minimum execution time: 25_969_000 picoseconds. - Weight::from_parts(27_160_000, 4258) + // Measured: `830` + // Estimated: `4295` + // Minimum execution time: 29_324_000 picoseconds. + Weight::from_parts(29_895_000, 4295) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1575,10 +1517,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TxDelegateTakeRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn increase_take() -> Weight { // Proof Size summary in bytes: - // Measured: `886` - // Estimated: `4351` - // Minimum execution time: 33_360_000 picoseconds. - Weight::from_parts(34_381_000, 4351) + // Measured: `923` + // Estimated: `4388` + // Minimum execution time: 36_257_000 picoseconds. + Weight::from_parts(37_710_000, 4388) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -1606,16 +1548,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::BlockEmission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::TotalNetworks` (r:1 w:1) /// Proof: `SubtensorModule::TotalNetworks` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Kappa` (r:1 w:1) @@ -1680,6 +1618,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:0 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:0 w:1) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:0 w:1) @@ -1698,26 +1638,26 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `1343` - // Estimated: `9758` - // Minimum execution time: 271_161_000 picoseconds. - Weight::from_parts(278_281_000, 9758) - .saturating_add(T::DbWeight::get().reads(41_u64)) - .saturating_add(T::DbWeight::get().writes(48_u64)) + // Measured: `1468` + // Estimated: `9883` + // Minimum execution time: 270_502_000 picoseconds. + Weight::from_parts(275_041_000, 9883) + .saturating_add(T::DbWeight::get().reads(39_u64)) + .saturating_add(T::DbWeight::get().writes(47_u64)) } - /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) - /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Axons` (r:1 w:1) /// Proof: `SubtensorModule::Axons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn serve_axon_tls() -> Weight { // Proof Size summary in bytes: - // Measured: `772` - // Estimated: `6712` - // Minimum execution time: 31_877_000 picoseconds. - Weight::from_parts(32_949_000, 6712) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `684` + // Estimated: `4149` + // Minimum execution time: 29_786_000 picoseconds. + Weight::from_parts(30_617_000, 4149) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:0) @@ -1728,10 +1668,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::IdentitiesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `852` - // Estimated: `6792` - // Minimum execution time: 28_833_000 picoseconds. - Weight::from_parts(29_874_000, 6792) + // Measured: `889` + // Estimated: `6829` + // Minimum execution time: 31_168_000 picoseconds. + Weight::from_parts(32_040_000, 6829) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1743,12 +1683,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `595` // Estimated: `4060` - // Minimum execution time: 15_502_000 picoseconds. - Weight::from_parts(16_184_000, 4060) + // Minimum execution time: 17_122_000 picoseconds. + Weight::from_parts(17_513_000, 4060) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `SubtensorModule::Owner` (r:1 w:2) + /// Storage: `SubtensorModule::Owner` (r:2 w:2) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:4 w:7) /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1760,14 +1700,20 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:9 w:8) /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RootClaimed` (r:1 w:0) + /// Proof: `SubtensorModule::RootClaimed` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::NetworksAdded` (r:6 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ChildKeys` (r:10 w:10) + /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastHotkeySwapOnNetuid` (r:4 w:4) + /// Proof: `SubtensorModule::LastHotkeySwapOnNetuid` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Alpha` (r:9 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:9 w:8) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::NetworksAdded` (r:6 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetOwnerHotkey` (r:5 w:0) /// Proof: `SubtensorModule::SubnetOwnerHotkey` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::HotkeyLock` (r:5 w:0) @@ -1776,8 +1722,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::DecayingHotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::ChildKeys` (r:10 w:10) - /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ChildkeyTake` (r:5 w:0) + /// Proof: `SubtensorModule::ChildkeyTake` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ParentKeys` (r:10 w:10) /// Proof: `SubtensorModule::ParentKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::PendingChildKeys` (r:10 w:0) @@ -1818,12 +1764,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_hotkey() -> Weight { // Proof Size summary in bytes: - // Measured: `3026` - // Estimated: `28766` - // Minimum execution time: 1_201_334_000 picoseconds. - Weight::from_parts(1_208_365_000, 28766) - .saturating_add(T::DbWeight::get().reads(171_u64)) - .saturating_add(T::DbWeight::get().writes(95_u64)) + // Measured: `3172` + // Estimated: `28912` + // Minimum execution time: 1_227_100_000 picoseconds. + Weight::from_parts(1_242_038_000, 28912) + .saturating_add(T::DbWeight::get().reads(182_u64)) + .saturating_add(T::DbWeight::get().writes(99_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1833,10 +1779,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn try_associate_hotkey() -> Weight { // Proof Size summary in bytes: - // Measured: `745` - // Estimated: `4210` - // Minimum execution time: 22_373_000 picoseconds. - Weight::from_parts(23_134_000, 4210) + // Measured: `818` + // Estimated: `4283` + // Minimum execution time: 24_556_000 picoseconds. + Weight::from_parts(25_337_000, 4283) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -1848,10 +1794,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unstake_all() -> Weight { // Proof Size summary in bytes: - // Measured: `740` - // Estimated: `9155` - // Minimum execution time: 25_017_000 picoseconds. - Weight::from_parts(25_658_000, 9155) + // Measured: `774` + // Estimated: `9189` + // Minimum execution time: 25_918_000 picoseconds. + Weight::from_parts(26_830_000, 9189) .saturating_add(T::DbWeight::get().reads(6_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -1870,32 +1816,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:2 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -1920,12 +1856,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unstake_all_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `2642` + // Measured: `2235` // Estimated: `11306` - // Minimum execution time: 583_803_000 picoseconds. - Weight::from_parts(599_485_000, 11306) - .saturating_add(T::DbWeight::get().reads(50_u64)) - .saturating_add(T::DbWeight::get().writes(27_u64)) + // Minimum execution time: 674_042_000 picoseconds. + Weight::from_parts(700_873_000, 11306) + .saturating_add(T::DbWeight::get().reads(43_u64)) + .saturating_add(T::DbWeight::get().writes(25_u64)) } /// Storage: `SubtensorModule::Alpha` (r:1 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1941,32 +1877,22 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -1985,12 +1911,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_full_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2598` - // Estimated: `11013` - // Minimum execution time: 497_956_000 picoseconds. - Weight::from_parts(503_033_000, 11013) - .saturating_add(T::DbWeight::get().reads(34_u64)) - .saturating_add(T::DbWeight::get().writes(15_u64)) + // Measured: `2176` + // Estimated: `10591` + // Minimum execution time: 731_620_000 picoseconds. + Weight::from_parts(754_292_000, 10591) + .saturating_add(T::DbWeight::get().reads(27_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) } /// Storage: `Crowdloan::CurrentCrowdloanId` (r:1 w:0) /// Proof: `Crowdloan::CurrentCrowdloanId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -2024,16 +1950,12 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::BlockEmission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::TotalNetworks` (r:1 w:1) /// Proof: `SubtensorModule::TotalNetworks` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Kappa` (r:1 w:1) @@ -2106,6 +2028,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:0 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:0 w:1) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:0 w:1) @@ -2127,15 +2051,15 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[2, 500]`. fn register_leased_network(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1762 + k * (44 ±0)` - // Estimated: `10183 + k * (2579 ±0)` - // Minimum execution time: 488_121_000 picoseconds. - Weight::from_parts(306_068_429, 10183) - // Standard Error: 24_263 - .saturating_add(Weight::from_parts(48_393_644, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(51_u64)) + // Measured: `1835 + k * (44 ±0)` + // Estimated: `10256 + k * (2579 ±0)` + // Minimum execution time: 475_774_000 picoseconds. + Weight::from_parts(309_598_986, 10256) + // Standard Error: 26_969 + .saturating_add(Weight::from_parts(46_027_026, 0).saturating_mul(k.into())) + .saturating_add(T::DbWeight::get().reads(49_u64)) .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(k.into()))) - .saturating_add(T::DbWeight::get().writes(54_u64)) + .saturating_add(T::DbWeight::get().writes(53_u64)) .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 2579).saturating_mul(k.into())) } @@ -2160,17 +2084,17 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[2, 500]`. fn terminate_lease(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1468 + k * (53 ±0)` - // Estimated: `6148 + k * (2514 ±0)` - // Minimum execution time: 91_385_000 picoseconds. - Weight::from_parts(96_646_996, 6148) - // Standard Error: 5_309 - .saturating_add(Weight::from_parts(1_570_386, 0).saturating_mul(k.into())) + // Measured: `1501 + k * (53 ±0)` + // Estimated: `6148 + k * (2529 ±0)` + // Minimum execution time: 127_077_000 picoseconds. + Weight::from_parts(146_402_779, 6148) + // Standard Error: 3_377 + .saturating_add(Weight::from_parts(1_566_532, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(7_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 2514).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 2529).saturating_mul(k.into())) } /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2180,8 +2104,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `659` // Estimated: `9074` - // Minimum execution time: 24_486_000 picoseconds. - Weight::from_parts(25_798_000, 9074) + // Minimum execution time: 27_300_000 picoseconds. + Weight::from_parts(28_313_000, 9074) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -2199,19 +2123,27 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastUpdate` (r:1 w:1) /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::BlocksSinceLastStep` (r:1 w:0) + /// Proof: `SubtensorModule::BlocksSinceLastStep` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TimelockedWeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::TimelockedWeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:1 w:0) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) fn commit_timelocked_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1070` - // Estimated: `4535` - // Minimum execution time: 72_186_000 picoseconds. - Weight::from_parts(73_359_000, 4535) - .saturating_add(T::DbWeight::get().reads(10_u64)) + // Measured: `1248` + // Estimated: `4713` + // Minimum execution time: 85_228_000 picoseconds. + Weight::from_parts(86_951_000, 4713) + .saturating_add(T::DbWeight::get().reads(14_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -2226,8 +2158,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `809` // Estimated: `4274` - // Minimum execution time: 31_566_000 picoseconds. - Weight::from_parts(32_979_000, 4274) + // Minimum execution time: 32_871_000 picoseconds. + Weight::from_parts(33_853_000, 4274) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -2243,8 +2175,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `476` // Estimated: `3941` - // Minimum execution time: 15_613_000 picoseconds. - Weight::from_parts(16_064_000, 3941) + // Minimum execution time: 17_463_000 picoseconds. + Weight::from_parts(18_283_000, 3941) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -2272,10 +2204,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::RootClaimableThreshold` (`max_values`: None, `max_size`: None, mode: `Measured`) fn claim_root() -> Weight { // Proof Size summary in bytes: - // Measured: `1929` - // Estimated: `7869` - // Minimum execution time: 140_608_000 picoseconds. - Weight::from_parts(142_310_000, 7869) + // Measured: `1935` + // Estimated: `7875` + // Minimum execution time: 136_093_000 picoseconds. + Weight::from_parts(138_999_000, 7875) .saturating_add(T::DbWeight::get().reads(16_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -2285,8 +2217,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_883_000 picoseconds. - Weight::from_parts(2_083_000, 0) + // Minimum execution time: 2_545_000 picoseconds. + Weight::from_parts(2_735_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::RootClaimableThreshold` (r:0 w:1) @@ -2295,8 +2227,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_136_000 picoseconds. - Weight::from_parts(4_747_000, 0) + // Minimum execution time: 5_229_000 picoseconds. + Weight::from_parts(5_681_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -2307,10 +2239,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_auto_parent_delegation_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `862` - // Estimated: `4327` - // Minimum execution time: 23_675_000 picoseconds. - Weight::from_parts(25_277_000, 4327) + // Measured: `899` + // Estimated: `4364` + // Minimum execution time: 26_870_000 picoseconds. + Weight::from_parts(28_023_000, 4364) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -2318,20 +2250,14 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -2348,8 +2274,6 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) @@ -2382,18 +2306,18 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `AlphaAssets::AlphaBurned` (r:1 w:1) /// Proof: `AlphaAssets::AlphaBurned` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `2636` + // Measured: `2229` // Estimated: `8727` - // Minimum execution time: 630_852_000 picoseconds. - Weight::from_parts(646_565_000, 8727) - .saturating_add(T::DbWeight::get().reads(39_u64)) - .saturating_add(T::DbWeight::get().writes(19_u64)) + // Minimum execution time: 945_077_000 picoseconds. + Weight::from_parts(967_308_000, 8727) + .saturating_add(T::DbWeight::get().reads(33_u64)) + .saturating_add(T::DbWeight::get().writes(17_u64)) } /// Storage: `SubtensorModule::PendingChildKeyCooldown` (r:0 w:1) /// Proof: `SubtensorModule::PendingChildKeyCooldown` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -2401,8 +2325,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_963_000 picoseconds. - Weight::from_parts(2_083_000, 0) + // Minimum execution time: 2_585_000 picoseconds. + Weight::from_parts(2_805_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -2437,14 +2361,16 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn lock_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `1644` - // Estimated: `7584` - // Minimum execution time: 111_775_000 picoseconds. - Weight::from_parts(114_028_000, 7584) + // Measured: `1715` + // Estimated: `7655` + // Minimum execution time: 114_744_000 picoseconds. + Weight::from_parts(115_995_000, 7655) .saturating_add(T::DbWeight::get().reads(17_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } /// Storage: `SubtensorModule::Owner` (r:2 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2466,14 +2392,204 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:2) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn move_lock() -> Weight { // Proof Size summary in bytes: - // Measured: `1366` - // Estimated: `7306` - // Minimum execution time: 146_897_000 picoseconds. - Weight::from_parts(148_699_000, 7306) + // Measured: `1399` + // Estimated: `7339` + // Minimum execution time: 147_815_000 picoseconds. + Weight::from_parts(149_819_000, 7339) .saturating_add(T::DbWeight::get().reads(14_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AssociatedEvmAddress` (r:1 w:1) + /// Proof: `SubtensorModule::AssociatedEvmAddress` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn associate_evm_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `950` + // Estimated: `4415` + // Minimum execution time: 665_186_000 picoseconds. + Weight::from_parts(684_242_000, 4415) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:1) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) + /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TransactionKeyLastBlock` (r:1 w:1) + /// Proof: `SubtensorModule::TransactionKeyLastBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_tempo() -> Weight { + // Proof Size summary in bytes: + // Measured: `975` + // Estimated: `4440` + // Minimum execution time: 45_394_000 picoseconds. + Weight::from_parts(46_407_000, 4440) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:0) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) + /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::OwnerHyperparamRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ActivityCutoffFactorMilli` (r:0 w:1) + /// Proof: `SubtensorModule::ActivityCutoffFactorMilli` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_activity_cutoff_factor() -> Weight { + // Proof Size summary in bytes: + // Measured: `899` + // Estimated: `4364` + // Minimum execution time: 38_362_000 picoseconds. + Weight::from_parts(39_193_000, 4364) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:1) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) + /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:0) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::OwnerHyperparamRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn trigger_epoch() -> Weight { + // Proof Size summary in bytes: + // Measured: `982` + // Estimated: `4447` + // Minimum execution time: 41_588_000 picoseconds. + Weight::from_parts(42_539_000, 4447) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:1 w:0) + /// Proof: `SubtensorModule::ColdkeySwapAnnouncements` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ColdkeySwapDisputes` (r:1 w:0) + /// Proof: `SubtensorModule::ColdkeySwapDisputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_coldkey_swap_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `733` + // Estimated: `4198` + // Minimum execution time: 16_832_000 picoseconds. + Weight::from_parts(17_583_000, 4198) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `SubtensorModule::SubnetOwnerHotkey` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwnerHotkey` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TaoWeight` (r:1 w:0) + /// Proof: `SubtensorModule::TaoWeight` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:2 w:0) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ParentKeys` (r:1 w:0) + /// Proof: `SubtensorModule::ParentKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ChildKeys` (r:1 w:0) + /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) + /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::WeightCommits` (r:1 w:0) + /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:0) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) + /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_weights_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `1731` + // Estimated: `7671` + // Minimum execution time: 52_046_000 picoseconds. + Weight::from_parts(52_958_000, 7671) + .saturating_add(T::DbWeight::get().reads(11_u64)) + } + /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MechanismCountCurrent` (r:1 w:0) + /// Proof: `SubtensorModule::MechanismCountCurrent` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Keys` (r:1 w:0) + /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastUpdate` (r:1 w:0) + /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::WeightsSetRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::WeightsSetRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_rate_limits_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `1019` + // Estimated: `4484` + // Minimum execution time: 34_995_000 picoseconds. + Weight::from_parts(35_376_000, 4484) + .saturating_add(T::DbWeight::get().reads(7_u64)) + } + /// Storage: `SubtensorModule::MinDelegateTake` (r:1 w:0) + /// Proof: `SubtensorModule::MinDelegateTake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaxDelegateTake` (r:1 w:0) + /// Proof: `SubtensorModule::MaxDelegateTake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_delegate_take_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `721` + // Estimated: `4186` + // Minimum execution time: 14_998_000 picoseconds. + Weight::from_parts(15_378_000, 4186) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Axons` (r:1 w:0) + /// Proof: `SubtensorModule::Axons` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_serving_endpoints_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `647` + // Estimated: `4112` + // Minimum execution time: 18_775_000 picoseconds. + Weight::from_parts(19_035_000, 4112) + .saturating_add(T::DbWeight::get().reads(3_u64)) + } + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AssociatedEvmAddress` (r:1 w:0) + /// Proof: `SubtensorModule::AssociatedEvmAddress` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_evm_key_association_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `652` + // Estimated: `4117` + // Minimum execution time: 15_018_000 picoseconds. + Weight::from_parts(15_448_000, 4117) + .saturating_add(T::DbWeight::get().reads(2_u64)) } } @@ -2503,28 +2619,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:1) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:5 w:5) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `Swap::LastPositionId` (r:1 w:1) - /// Proof: `Swap::LastPositionId` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -2569,18 +2667,16 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::IsNetworkMember` (r:0 w:1) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::AlphaSqrtPrice` (r:0 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:0 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Swap::SwapBalancer` (r:0 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `1716` - // Estimated: `13600` - // Minimum execution time: 374_002_000 picoseconds. - Weight::from_parts(380_312_000, 13600) - .saturating_add(RocksDbWeight::get().reads(48_u64)) - .saturating_add(RocksDbWeight::get().writes(40_u64)) + // Measured: `1865` + // Estimated: `6148` + // Minimum execution time: 343_840_000 picoseconds. + Weight::from_parts(349_872_000, 6148) + .saturating_add(RocksDbWeight::get().reads(34_u64)) + .saturating_add(RocksDbWeight::get().writes(29_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2618,10 +2714,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `188792` - // Estimated: `10327382` - // Minimum execution time: 16_598_698_000 picoseconds. - Weight::from_parts(16_897_861_000, 10327382) + // Measured: `188820` + // Estimated: `10327410` + // Minimum execution time: 15_317_357_000 picoseconds. + Weight::from_parts(15_513_610_000, 10327410) .saturating_add(RocksDbWeight::get().reads(4112_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -2633,20 +2729,14 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:3 w:3) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -2659,8 +2749,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) @@ -2691,47 +2779,47 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2633` + // Measured: `2226` // Estimated: `8727` - // Minimum execution time: 459_249_000 picoseconds. - Weight::from_parts(476_173_000, 8727) - .saturating_add(RocksDbWeight::get().reads(38_u64)) - .saturating_add(RocksDbWeight::get().writes(18_u64)) + // Minimum execution time: 639_698_000 picoseconds. + Weight::from_parts(664_064_000, 8727) + .saturating_add(RocksDbWeight::get().reads(32_u64)) + .saturating_add(RocksDbWeight::get().writes(16_u64)) } - /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) - /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Axons` (r:1 w:1) /// Proof: `SubtensorModule::Axons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn serve_axon() -> Weight { // Proof Size summary in bytes: - // Measured: `801` - // Estimated: `6741` - // Minimum execution time: 32_538_000 picoseconds. - Weight::from_parts(33_289_000, 6741) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `713` + // Estimated: `4178` + // Minimum execution time: 30_307_000 picoseconds. + Weight::from_parts(31_278_000, 4178) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) - /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Prometheus` (r:1 w:1) /// Proof: `SubtensorModule::Prometheus` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn serve_prometheus() -> Weight { // Proof Size summary in bytes: - // Measured: `774` - // Estimated: `6714` - // Minimum execution time: 29_163_000 picoseconds. - Weight::from_parts(29_784_000, 6714) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `812` + // Estimated: `4277` + // Minimum execution time: 28_262_000 picoseconds. + Weight::from_parts(29_294_000, 4277) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -2758,28 +2846,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:1) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:1) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:5 w:5) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `Swap::LastPositionId` (r:1 w:1) - /// Proof: `Swap::LastPositionId` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) @@ -2824,18 +2894,16 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::IsNetworkMember` (r:0 w:1) /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::AlphaSqrtPrice` (r:0 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:0 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) + /// Storage: `Swap::SwapBalancer` (r:0 w:1) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) fn burned_register() -> Weight { // Proof Size summary in bytes: - // Measured: `1649` - // Estimated: `13600` - // Minimum execution time: 362_295_000 picoseconds. - Weight::from_parts(368_123_000, 13600) - .saturating_add(RocksDbWeight::get().reads(48_u64)) - .saturating_add(RocksDbWeight::get().writes(40_u64)) + // Measured: `1798` + // Estimated: `6148` + // Minimum execution time: 332_358_000 picoseconds. + Weight::from_parts(336_815_000, 6148) + .saturating_add(RocksDbWeight::get().reads(34_u64)) + .saturating_add(RocksDbWeight::get().writes(29_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -2883,10 +2951,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) fn root_register() -> Weight { // Proof Size summary in bytes: - // Measured: `1445` - // Estimated: `4910` - // Minimum execution time: 102_751_000 picoseconds. - Weight::from_parts(104_294_000, 4910) + // Measured: `1516` + // Estimated: `4981` + // Minimum execution time: 102_089_000 picoseconds. + Weight::from_parts(103_934_000, 4981) .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(16_u64)) } @@ -2916,16 +2984,12 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::TotalNetworks` (r:1 w:1) /// Proof: `SubtensorModule::TotalNetworks` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Kappa` (r:1 w:1) @@ -2988,6 +3052,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:0 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:0 w:1) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:0 w:1) @@ -3006,12 +3072,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network() -> Weight { // Proof Size summary in bytes: - // Measured: `1459` - // Estimated: `9874` - // Minimum execution time: 277_470_000 picoseconds. - Weight::from_parts(282_297_000, 9874) - .saturating_add(RocksDbWeight::get().reads(42_u64)) - .saturating_add(RocksDbWeight::get().writes(49_u64)) + // Measured: `1532` + // Estimated: `9947` + // Minimum execution time: 271_405_000 picoseconds. + Weight::from_parts(277_125_000, 9947) + .saturating_add(RocksDbWeight::get().reads(40_u64)) + .saturating_add(RocksDbWeight::get().writes(48_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3025,27 +3091,35 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastUpdate` (r:1 w:1) /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) - /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::BlocksSinceLastStep` (r:1 w:0) + /// Proof: `SubtensorModule::BlocksSinceLastStep` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:1 w:0) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) fn commit_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1071` - // Estimated: `4536` - // Minimum execution time: 60_340_000 picoseconds. - Weight::from_parts(61_421_000, 4536) - .saturating_add(RocksDbWeight::get().reads(10_u64)) + // Measured: `1249` + // Estimated: `4714` + // Minimum execution time: 67_886_000 picoseconds. + Weight::from_parts(69_049_000, 4714) + .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) @@ -3080,11 +3154,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn reveal_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1589` - // Estimated: `7529` - // Minimum execution time: 108_541_000 picoseconds. - Weight::from_parts(110_183_000, 7529) - .saturating_add(RocksDbWeight::get().reads(18_u64)) + // Measured: `1650` + // Estimated: `7590` + // Minimum execution time: 109_534_000 picoseconds. + Weight::from_parts(111_227_000, 7590) + .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::TxChildkeyTakeRateLimit` (r:0 w:1) @@ -3093,8 +3167,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_076_000 picoseconds. - Weight::from_parts(4_647_000, 0) + // Minimum execution time: 5_570_000 picoseconds. + Weight::from_parts(5_760_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -3113,10 +3187,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TransactionKeyLastBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_childkey_take() -> Weight { // Proof Size summary in bytes: - // Measured: `999` - // Estimated: `4464` - // Minimum execution time: 52_278_000 picoseconds. - Weight::from_parts(53_209_000, 4464) + // Measured: `1033` + // Estimated: `4498` + // Minimum execution time: 52_527_000 picoseconds. + Weight::from_parts(53_870_000, 4498) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3132,8 +3206,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `694` // Estimated: `4159` - // Minimum execution time: 43_995_000 picoseconds. - Weight::from_parts(45_167_000, 4159) + // Minimum execution time: 45_574_000 picoseconds. + Weight::from_parts(47_248_000, 4159) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -3161,6 +3235,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingColdkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) @@ -3169,17 +3245,19 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:2 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::DecayingLock` (r:1 w:0) + /// Proof: `SubtensorModule::DecayingLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `System::Account` (r:2 w:2) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(104), added: 2579, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey_announced() -> Weight { // Proof Size summary in bytes: - // Measured: `2175` - // Estimated: `13065` - // Minimum execution time: 286_653_000 picoseconds. - Weight::from_parts(294_536_000, 13065) - .saturating_add(RocksDbWeight::get().reads(35_u64)) + // Measured: `2110` + // Estimated: `13000` + // Minimum execution time: 284_379_000 picoseconds. + Weight::from_parts(288_156_000, 13000) + .saturating_add(RocksDbWeight::get().reads(37_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } /// Storage: `System::Account` (r:2 w:2) @@ -3208,6 +3286,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakingColdkeys` (r:1 w:0) + /// Proof: `SubtensorModule::StakingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:2 w:2) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::UnlockRate` (r:1 w:0) @@ -3216,6 +3296,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:2 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::DecayingLock` (r:1 w:0) + /// Proof: `SubtensorModule::DecayingLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:0 w:1) /// Proof: `SubtensorModule::ColdkeySwapAnnouncements` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ColdkeySwapDisputes` (r:0 w:1) @@ -3224,11 +3306,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_coldkey() -> Weight { // Proof Size summary in bytes: - // Measured: `2231` - // Estimated: `13121` - // Minimum execution time: 310_339_000 picoseconds. - Weight::from_parts(313_503_000, 13121) - .saturating_add(RocksDbWeight::get().reads(35_u64)) + // Measured: `2166` + // Estimated: `13056` + // Minimum execution time: 308_323_000 picoseconds. + Weight::from_parts(312_301_000, 13056) + .saturating_add(RocksDbWeight::get().reads(37_u64)) .saturating_add(RocksDbWeight::get().writes(19_u64)) } /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:1 w:0) @@ -3239,8 +3321,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `665` // Estimated: `4130` - // Minimum execution time: 20_290_000 picoseconds. - Weight::from_parts(21_452_000, 4130) + // Minimum execution time: 22_442_000 picoseconds. + Weight::from_parts(23_293_000, 4130) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -3252,8 +3334,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `613` // Estimated: `4078` - // Minimum execution time: 16_995_000 picoseconds. - Weight::from_parts(17_505_000, 4078) + // Minimum execution time: 18_695_000 picoseconds. + Weight::from_parts(19_496_000, 4078) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -3265,14 +3347,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_830_000 picoseconds. - Weight::from_parts(7_271_000, 0) + // Minimum execution time: 8_335_000 picoseconds. + Weight::from_parts(8_857_000, 0) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) @@ -3307,11 +3391,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn batch_reveal_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `2094` - // Estimated: `8034` - // Minimum execution time: 429_484_000 picoseconds. - Weight::from_parts(443_415_000, 8034) - .saturating_add(RocksDbWeight::get().reads(18_u64)) + // Measured: `2155` + // Estimated: `8095` + // Minimum execution time: 412_226_000 picoseconds. + Weight::from_parts(421_363_000, 8095) + .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -3342,10 +3426,10 @@ impl WeightInfo for () { /// Proof: `AlphaAssets::TotalAlphaIssuance` (`max_values`: None, `max_size`: None, mode: `Measured`) fn recycle_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1873` - // Estimated: `5338` - // Minimum execution time: 176_220_000 picoseconds. - Weight::from_parts(178_253_000, 5338) + // Measured: `1754` + // Estimated: `5219` + // Minimum execution time: 169_976_000 picoseconds. + Weight::from_parts(172_139_000, 5219) .saturating_add(RocksDbWeight::get().reads(13_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -3375,10 +3459,10 @@ impl WeightInfo for () { /// Proof: `AlphaAssets::AlphaBurned` (`max_values`: None, `max_size`: None, mode: `Measured`) fn burn_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `1873` - // Estimated: `5338` - // Minimum execution time: 172_335_000 picoseconds. - Weight::from_parts(174_197_000, 5338) + // Measured: `1754` + // Estimated: `5219` + // Minimum execution time: 166_359_000 picoseconds. + Weight::from_parts(168_964_000, 5219) .saturating_add(RocksDbWeight::get().reads(12_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -3398,8 +3482,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1118` // Estimated: `4583` - // Minimum execution time: 37_836_000 picoseconds. - Weight::from_parts(38_907_000, 4583) + // Minimum execution time: 38_482_000 picoseconds. + Weight::from_parts(39_454_000, 4583) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3407,20 +3491,14 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -3437,8 +3515,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) @@ -3469,18 +3545,18 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2633` + // Measured: `2226` // Estimated: `8727` - // Minimum execution time: 499_258_000 picoseconds. - Weight::from_parts(516_242_000, 8727) - .saturating_add(RocksDbWeight::get().reads(38_u64)) - .saturating_add(RocksDbWeight::get().writes(18_u64)) + // Minimum execution time: 829_031_000 picoseconds. + Weight::from_parts(853_005_000, 8727) + .saturating_add(RocksDbWeight::get().reads(32_u64)) + .saturating_add(RocksDbWeight::get().writes(16_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3492,8 +3568,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -3504,18 +3578,20 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn move_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2060` - // Estimated: `8000` - // Minimum execution time: 222_999_000 picoseconds. - Weight::from_parts(227_526_000, 8000) + // Measured: `1979` + // Estimated: `7919` + // Minimum execution time: 214_819_000 picoseconds. + Weight::from_parts(216_061_000, 7919) .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(7_u64)) } @@ -3533,34 +3609,24 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -3579,35 +3645,27 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2564` - // Estimated: `10979` - // Minimum execution time: 435_183_000 picoseconds. - Weight::from_parts(444_777_000, 10979) - .saturating_add(RocksDbWeight::get().reads(35_u64)) - .saturating_add(RocksDbWeight::get().writes(15_u64)) + // Measured: `2142` + // Estimated: `10557` + // Minimum execution time: 544_281_000 picoseconds. + Weight::from_parts(569_198_000, 10557) + .saturating_add(RocksDbWeight::get().reads(28_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Alpha` (r:1 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) @@ -3624,8 +3682,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -3644,12 +3700,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2598` - // Estimated: `11013` - // Minimum execution time: 475_352_000 picoseconds. - Weight::from_parts(478_116_000, 11013) - .saturating_add(RocksDbWeight::get().reads(34_u64)) - .saturating_add(RocksDbWeight::get().writes(15_u64)) + // Measured: `2176` + // Estimated: `10591` + // Minimum execution time: 717_343_000 picoseconds. + Weight::from_parts(740_696_000, 10591) + .saturating_add(RocksDbWeight::get().reads(27_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3665,22 +3721,14 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:2 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:2 w:0) @@ -3691,8 +3739,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:3 w:1) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -3721,16 +3767,18 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `3108` - // Estimated: `11523` - // Minimum execution time: 688_567_000 picoseconds. - Weight::from_parts(707_234_000, 11523) - .saturating_add(RocksDbWeight::get().reads(54_u64)) - .saturating_add(RocksDbWeight::get().writes(26_u64)) + // Measured: `2662` + // Estimated: `11077` + // Minimum execution time: 921_853_000 picoseconds. + Weight::from_parts(944_515_000, 11077) + .saturating_add(RocksDbWeight::get().reads(47_u64)) + .saturating_add(RocksDbWeight::get().writes(24_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3742,8 +3790,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -3760,10 +3806,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::AccountFlags` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_stake() -> Weight { @@ -3785,8 +3833,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::NetworksAdded` (r:2 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:2 w:0) @@ -3797,26 +3843,18 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:3 w:1) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -3845,16 +3883,18 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `2951` - // Estimated: `11366` - // Minimum execution time: 633_996_000 picoseconds. - Weight::from_parts(655_699_000, 11366) - .saturating_add(RocksDbWeight::get().reads(54_u64)) - .saturating_add(RocksDbWeight::get().writes(26_u64)) + // Measured: `2505` + // Estimated: `10920` + // Minimum execution time: 717_052_000 picoseconds. + Weight::from_parts(742_019_000, 10920) + .saturating_add(RocksDbWeight::get().reads(47_u64)) + .saturating_add(RocksDbWeight::get().writes(24_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -3868,23 +3908,31 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastUpdate` (r:1 w:1) /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) - /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::BlocksSinceLastStep` (r:1 w:0) + /// Proof: `SubtensorModule::BlocksSinceLastStep` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:1 w:0) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::WeightsSetRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::WeightsSetRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) + /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) fn batch_commit_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1122` - // Estimated: `4587` - // Minimum execution time: 127_058_000 picoseconds. - Weight::from_parts(129_030_000, 4587) - .saturating_add(RocksDbWeight::get().reads(11_u64)) + // Measured: `1300` + // Estimated: `4765` + // Minimum execution time: 144_628_000 picoseconds. + Weight::from_parts(147_224_000, 4765) + .saturating_add(RocksDbWeight::get().reads(15_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) @@ -3921,10 +3969,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Weights` (`max_values`: None, `max_size`: None, mode: `Measured`) fn batch_set_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1426` - // Estimated: `7366` - // Minimum execution time: 101_319_000 picoseconds. - Weight::from_parts(102_992_000, 7366) + // Measured: `1455` + // Estimated: `7395` + // Minimum execution time: 100_516_000 picoseconds. + Weight::from_parts(103_092_000, 7395) .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3938,10 +3986,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn decrease_take() -> Weight { // Proof Size summary in bytes: - // Measured: `793` - // Estimated: `4258` - // Minimum execution time: 25_969_000 picoseconds. - Weight::from_parts(27_160_000, 4258) + // Measured: `830` + // Estimated: `4295` + // Minimum execution time: 29_324_000 picoseconds. + Weight::from_parts(29_895_000, 4295) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3957,10 +4005,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TxDelegateTakeRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn increase_take() -> Weight { // Proof Size summary in bytes: - // Measured: `886` - // Estimated: `4351` - // Minimum execution time: 33_360_000 picoseconds. - Weight::from_parts(34_381_000, 4351) + // Measured: `923` + // Estimated: `4388` + // Minimum execution time: 36_257_000 picoseconds. + Weight::from_parts(37_710_000, 4388) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -3988,16 +4036,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::BlockEmission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::TotalNetworks` (r:1 w:1) /// Proof: `SubtensorModule::TotalNetworks` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Kappa` (r:1 w:1) @@ -4062,6 +4106,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:0 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:0 w:1) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:0 w:1) @@ -4080,26 +4126,26 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaxAllowedUids` (`max_values`: None, `max_size`: None, mode: `Measured`) fn register_network_with_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `1343` - // Estimated: `9758` - // Minimum execution time: 271_161_000 picoseconds. - Weight::from_parts(278_281_000, 9758) - .saturating_add(RocksDbWeight::get().reads(41_u64)) - .saturating_add(RocksDbWeight::get().writes(48_u64)) + // Measured: `1468` + // Estimated: `9883` + // Minimum execution time: 270_502_000 picoseconds. + Weight::from_parts(275_041_000, 9883) + .saturating_add(RocksDbWeight::get().reads(39_u64)) + .saturating_add(RocksDbWeight::get().writes(47_u64)) } - /// Storage: `SubtensorModule::IsNetworkMember` (r:2 w:0) - /// Proof: `SubtensorModule::IsNetworkMember` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Axons` (r:1 w:1) /// Proof: `SubtensorModule::Axons` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) fn serve_axon_tls() -> Weight { // Proof Size summary in bytes: - // Measured: `772` - // Estimated: `6712` - // Minimum execution time: 31_877_000 picoseconds. - Weight::from_parts(32_949_000, 6712) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `684` + // Estimated: `4149` + // Minimum execution time: 29_786_000 picoseconds. + Weight::from_parts(30_617_000, 4149) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:0) @@ -4110,10 +4156,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::IdentitiesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_identity() -> Weight { // Proof Size summary in bytes: - // Measured: `852` - // Estimated: `6792` - // Minimum execution time: 28_833_000 picoseconds. - Weight::from_parts(29_874_000, 6792) + // Measured: `889` + // Estimated: `6829` + // Minimum execution time: 31_168_000 picoseconds. + Weight::from_parts(32_040_000, 6829) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -4125,12 +4171,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `595` // Estimated: `4060` - // Minimum execution time: 15_502_000 picoseconds. - Weight::from_parts(16_184_000, 4060) + // Minimum execution time: 17_122_000 picoseconds. + Weight::from_parts(17_513_000, 4060) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `SubtensorModule::Owner` (r:1 w:2) + /// Storage: `SubtensorModule::Owner` (r:2 w:2) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:4 w:7) /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -4142,14 +4188,20 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::RootClaimable` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:9 w:8) /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RootClaimed` (r:1 w:0) + /// Proof: `SubtensorModule::RootClaimed` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::NetworksAdded` (r:6 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ChildKeys` (r:10 w:10) + /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastHotkeySwapOnNetuid` (r:4 w:4) + /// Proof: `SubtensorModule::LastHotkeySwapOnNetuid` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalIssuance` (r:1 w:1) /// Proof: `SubtensorModule::TotalIssuance` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Alpha` (r:9 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::AlphaV2` (r:9 w:8) /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::NetworksAdded` (r:6 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetOwnerHotkey` (r:5 w:0) /// Proof: `SubtensorModule::SubnetOwnerHotkey` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::HotkeyLock` (r:5 w:0) @@ -4158,8 +4210,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::DecayingHotkeyLock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::OwnedHotkeys` (r:1 w:1) /// Proof: `SubtensorModule::OwnedHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::ChildKeys` (r:10 w:10) - /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ChildkeyTake` (r:5 w:0) + /// Proof: `SubtensorModule::ChildkeyTake` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::ParentKeys` (r:10 w:10) /// Proof: `SubtensorModule::ParentKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::PendingChildKeys` (r:10 w:0) @@ -4200,12 +4252,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn swap_hotkey() -> Weight { // Proof Size summary in bytes: - // Measured: `3026` - // Estimated: `28766` - // Minimum execution time: 1_201_334_000 picoseconds. - Weight::from_parts(1_208_365_000, 28766) - .saturating_add(RocksDbWeight::get().reads(171_u64)) - .saturating_add(RocksDbWeight::get().writes(95_u64)) + // Measured: `3172` + // Estimated: `28912` + // Minimum execution time: 1_227_100_000 picoseconds. + Weight::from_parts(1_242_038_000, 28912) + .saturating_add(RocksDbWeight::get().reads(182_u64)) + .saturating_add(RocksDbWeight::get().writes(99_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:1) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -4215,10 +4267,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn try_associate_hotkey() -> Weight { // Proof Size summary in bytes: - // Measured: `745` - // Estimated: `4210` - // Minimum execution time: 22_373_000 picoseconds. - Weight::from_parts(23_134_000, 4210) + // Measured: `818` + // Estimated: `4283` + // Minimum execution time: 24_556_000 picoseconds. + Weight::from_parts(25_337_000, 4283) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -4230,10 +4282,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unstake_all() -> Weight { // Proof Size summary in bytes: - // Measured: `740` - // Estimated: `9155` - // Minimum execution time: 25_017_000 picoseconds. - Weight::from_parts(25_658_000, 9155) + // Measured: `774` + // Estimated: `9189` + // Minimum execution time: 25_918_000 picoseconds. + Weight::from_parts(26_830_000, 9189) .saturating_add(RocksDbWeight::get().reads(6_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -4252,32 +4304,22 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:2 w:2) /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:2 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:2 w:2) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:2 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:2 w:2) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:2 w:2) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -4302,12 +4344,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn unstake_all_alpha() -> Weight { // Proof Size summary in bytes: - // Measured: `2642` + // Measured: `2235` // Estimated: `11306` - // Minimum execution time: 583_803_000 picoseconds. - Weight::from_parts(599_485_000, 11306) - .saturating_add(RocksDbWeight::get().reads(50_u64)) - .saturating_add(RocksDbWeight::get().writes(27_u64)) + // Minimum execution time: 674_042_000 picoseconds. + Weight::from_parts(700_873_000, 11306) + .saturating_add(RocksDbWeight::get().reads(43_u64)) + .saturating_add(RocksDbWeight::get().writes(25_u64)) } /// Storage: `SubtensorModule::Alpha` (r:1 w:0) /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -4323,32 +4365,22 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:3 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:1 w:0) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Owner` (r:1 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:0) /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) @@ -4367,12 +4399,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn remove_stake_full_limit() -> Weight { // Proof Size summary in bytes: - // Measured: `2598` - // Estimated: `11013` - // Minimum execution time: 497_956_000 picoseconds. - Weight::from_parts(503_033_000, 11013) - .saturating_add(RocksDbWeight::get().reads(34_u64)) - .saturating_add(RocksDbWeight::get().writes(15_u64)) + // Measured: `2176` + // Estimated: `10591` + // Minimum execution time: 731_620_000 picoseconds. + Weight::from_parts(754_292_000, 10591) + .saturating_add(RocksDbWeight::get().reads(27_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) } /// Storage: `Crowdloan::CurrentCrowdloanId` (r:1 w:0) /// Proof: `Crowdloan::CurrentCrowdloanId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -4406,16 +4438,12 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::BlockEmission` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:1) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTaoProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTaoProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::TotalNetworks` (r:1 w:1) /// Proof: `SubtensorModule::TotalNetworks` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Kappa` (r:1 w:1) @@ -4488,6 +4516,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:0 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:0 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:0 w:1) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Uids` (r:0 w:1) @@ -4509,15 +4539,15 @@ impl WeightInfo for () { /// The range of component `k` is `[2, 500]`. fn register_leased_network(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1762 + k * (44 ±0)` - // Estimated: `10183 + k * (2579 ±0)` - // Minimum execution time: 488_121_000 picoseconds. - Weight::from_parts(306_068_429, 10183) - // Standard Error: 24_263 - .saturating_add(Weight::from_parts(48_393_644, 0).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(51_u64)) + // Measured: `1835 + k * (44 ±0)` + // Estimated: `10256 + k * (2579 ±0)` + // Minimum execution time: 475_774_000 picoseconds. + Weight::from_parts(309_598_986, 10256) + // Standard Error: 26_969 + .saturating_add(Weight::from_parts(46_027_026, 0).saturating_mul(k.into())) + .saturating_add(RocksDbWeight::get().reads(49_u64)) .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(k.into()))) - .saturating_add(RocksDbWeight::get().writes(54_u64)) + .saturating_add(RocksDbWeight::get().writes(53_u64)) .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 2579).saturating_mul(k.into())) } @@ -4542,17 +4572,17 @@ impl WeightInfo for () { /// The range of component `k` is `[2, 500]`. fn terminate_lease(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1468 + k * (53 ±0)` - // Estimated: `6148 + k * (2514 ±0)` - // Minimum execution time: 91_385_000 picoseconds. - Weight::from_parts(96_646_996, 6148) - // Standard Error: 5_309 - .saturating_add(Weight::from_parts(1_570_386, 0).saturating_mul(k.into())) + // Measured: `1501 + k * (53 ±0)` + // Estimated: `6148 + k * (2529 ±0)` + // Minimum execution time: 127_077_000 picoseconds. + Weight::from_parts(146_402_779, 6148) + // Standard Error: 3_377 + .saturating_add(Weight::from_parts(1_566_532, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(7_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 2514).saturating_mul(k.into())) + .saturating_add(Weight::from_parts(0, 2529).saturating_mul(k.into())) } /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -4562,8 +4592,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `659` // Estimated: `9074` - // Minimum execution time: 24_486_000 picoseconds. - Weight::from_parts(25_798_000, 9074) + // Minimum execution time: 27_300_000 picoseconds. + Weight::from_parts(28_313_000, 9074) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -4581,19 +4611,27 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastUpdate` (r:1 w:1) /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Tempo` (r:1 w:0) /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::BlocksSinceLastStep` (r:1 w:0) + /// Proof: `SubtensorModule::BlocksSinceLastStep` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TimelockedWeightCommits` (r:1 w:1) /// Proof: `SubtensorModule::TimelockedWeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetworkN` (r:1 w:0) /// Proof: `SubtensorModule::SubnetworkN` (`max_values`: None, `max_size`: None, mode: `Measured`) fn commit_timelocked_weights() -> Weight { // Proof Size summary in bytes: - // Measured: `1070` - // Estimated: `4535` - // Minimum execution time: 72_186_000 picoseconds. - Weight::from_parts(73_359_000, 4535) - .saturating_add(RocksDbWeight::get().reads(10_u64)) + // Measured: `1248` + // Estimated: `4713` + // Minimum execution time: 85_228_000 picoseconds. + Weight::from_parts(86_951_000, 4713) + .saturating_add(RocksDbWeight::get().reads(14_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) @@ -4608,8 +4646,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `809` // Estimated: `4274` - // Minimum execution time: 31_566_000 picoseconds. - Weight::from_parts(32_979_000, 4274) + // Minimum execution time: 32_871_000 picoseconds. + Weight::from_parts(33_853_000, 4274) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -4625,8 +4663,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `476` // Estimated: `3941` - // Minimum execution time: 15_613_000 picoseconds. - Weight::from_parts(16_064_000, 3941) + // Minimum execution time: 17_463_000 picoseconds. + Weight::from_parts(18_283_000, 3941) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -4654,10 +4692,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::RootClaimableThreshold` (`max_values`: None, `max_size`: None, mode: `Measured`) fn claim_root() -> Weight { // Proof Size summary in bytes: - // Measured: `1929` - // Estimated: `7869` - // Minimum execution time: 140_608_000 picoseconds. - Weight::from_parts(142_310_000, 7869) + // Measured: `1935` + // Estimated: `7875` + // Minimum execution time: 136_093_000 picoseconds. + Weight::from_parts(138_999_000, 7875) .saturating_add(RocksDbWeight::get().reads(16_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -4667,8 +4705,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_883_000 picoseconds. - Weight::from_parts(2_083_000, 0) + // Minimum execution time: 2_545_000 picoseconds. + Weight::from_parts(2_735_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::RootClaimableThreshold` (r:0 w:1) @@ -4677,8 +4715,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_136_000 picoseconds. - Weight::from_parts(4_747_000, 0) + // Minimum execution time: 5_229_000 picoseconds. + Weight::from_parts(5_681_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -4689,10 +4727,10 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::AutoParentDelegationEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_auto_parent_delegation_enabled() -> Weight { // Proof Size summary in bytes: - // Measured: `862` - // Estimated: `4327` - // Minimum execution time: 23_675_000 picoseconds. - Weight::from_parts(25_277_000, 4327) + // Measured: `899` + // Estimated: `4364` + // Minimum execution time: 26_870_000 picoseconds. + Weight::from_parts(28_023_000, 4364) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -4700,20 +4738,14 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaInProvided` (r:1 w:0) - /// Proof: `SubtensorModule::SubnetAlphaInProvided` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::SwapV3Initialized` (r:1 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:1) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:1) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::TickIndexBitmapWords` (r:3 w:0) - /// Proof: `Swap::TickIndexBitmapWords` (`max_values`: None, `max_size`: Some(47), added: 2522, mode: `MaxEncodedLen`) + /// Storage: `Swap::PalSwapInitialized` (r:1 w:0) + /// Proof: `Swap::PalSwapInitialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) + /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) + /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Swap::SwapBalancer` (r:1 w:0) + /// Proof: `Swap::SwapBalancer` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `Swap::FeeRate` (r:1 w:0) /// Proof: `Swap::FeeRate` (`max_values`: None, `max_size`: Some(12), added: 2487, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:0) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) @@ -4730,8 +4762,6 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::TotalStake` (r:1 w:1) /// Proof: `SubtensorModule::TotalStake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetVolume` (r:1 w:1) @@ -4764,18 +4794,18 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `AlphaAssets::AlphaBurned` (r:1 w:1) /// Proof: `AlphaAssets::AlphaBurned` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingOperationRateLimiter` (r:0 w:1) - /// Proof: `SubtensorModule::StakingOperationRateLimiter` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (r:0 w:1) /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn add_stake_burn() -> Weight { // Proof Size summary in bytes: - // Measured: `2636` + // Measured: `2229` // Estimated: `8727` - // Minimum execution time: 630_852_000 picoseconds. - Weight::from_parts(646_565_000, 8727) - .saturating_add(RocksDbWeight::get().reads(39_u64)) - .saturating_add(RocksDbWeight::get().writes(19_u64)) + // Minimum execution time: 945_077_000 picoseconds. + Weight::from_parts(967_308_000, 8727) + .saturating_add(RocksDbWeight::get().reads(33_u64)) + .saturating_add(RocksDbWeight::get().writes(17_u64)) } /// Storage: `SubtensorModule::PendingChildKeyCooldown` (r:0 w:1) /// Proof: `SubtensorModule::PendingChildKeyCooldown` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -4783,8 +4813,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_963_000 picoseconds. - Weight::from_parts(2_083_000, 0) + // Minimum execution time: 2_585_000 picoseconds. + Weight::from_parts(2_805_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `SubtensorModule::Owner` (r:1 w:0) @@ -4819,14 +4849,16 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:1) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn lock_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `1644` - // Estimated: `7584` - // Minimum execution time: 111_775_000 picoseconds. - Weight::from_parts(114_028_000, 7584) + // Measured: `1715` + // Estimated: `7655` + // Minimum execution time: 114_744_000 picoseconds. + Weight::from_parts(115_995_000, 7655) .saturating_add(RocksDbWeight::get().reads(17_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// Storage: `SubtensorModule::Owner` (r:2 w:0) /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -4848,13 +4880,203 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::UnlockRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::MaturityRate` (r:1 w:0) /// Proof: `SubtensorModule::MaturityRate` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LockingColdkeys` (r:0 w:2) + /// Proof: `SubtensorModule::LockingColdkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) fn move_lock() -> Weight { // Proof Size summary in bytes: - // Measured: `1366` - // Estimated: `7306` - // Minimum execution time: 146_897_000 picoseconds. - Weight::from_parts(148_699_000, 7306) + // Measured: `1399` + // Estimated: `7339` + // Minimum execution time: 147_815_000 picoseconds. + Weight::from_parts(149_819_000, 7339) .saturating_add(RocksDbWeight::get().reads(14_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AssociatedEvmAddress` (r:1 w:1) + /// Proof: `SubtensorModule::AssociatedEvmAddress` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn associate_evm_key() -> Weight { + // Proof Size summary in bytes: + // Measured: `950` + // Estimated: `4415` + // Minimum execution time: 665_186_000 picoseconds. + Weight::from_parts(684_242_000, 4415) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:1) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) + /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TransactionKeyLastBlock` (r:1 w:1) + /// Proof: `SubtensorModule::TransactionKeyLastBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_tempo() -> Weight { + // Proof Size summary in bytes: + // Measured: `975` + // Estimated: `4440` + // Minimum execution time: 45_394_000 picoseconds. + Weight::from_parts(46_407_000, 4440) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:0) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:0) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) + /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::OwnerHyperparamRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ActivityCutoffFactorMilli` (r:0 w:1) + /// Proof: `SubtensorModule::ActivityCutoffFactorMilli` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn set_activity_cutoff_factor() -> Weight { + // Proof Size summary in bytes: + // Measured: `899` + // Estimated: `4364` + // Minimum execution time: 38_362_000 picoseconds. + Weight::from_parts(39_193_000, 4364) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::SubnetOwner` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwner` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::PendingEpochAt` (r:1 w:1) + /// Proof: `SubtensorModule::PendingEpochAt` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AdminFreezeWindow` (r:1 w:0) + /// Proof: `SubtensorModule::AdminFreezeWindow` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:0) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastEpochBlock` (r:1 w:0) + /// Proof: `SubtensorModule::LastEpochBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::OwnerHyperparamRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::OwnerHyperparamRateLimit` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastRateLimitedBlock` (r:1 w:1) + /// Proof: `SubtensorModule::LastRateLimitedBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn trigger_epoch() -> Weight { + // Proof Size summary in bytes: + // Measured: `982` + // Estimated: `4447` + // Minimum execution time: 41_588_000 picoseconds. + Weight::from_parts(42_539_000, 4447) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: `SubtensorModule::ColdkeySwapAnnouncements` (r:1 w:0) + /// Proof: `SubtensorModule::ColdkeySwapAnnouncements` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ColdkeySwapDisputes` (r:1 w:0) + /// Proof: `SubtensorModule::ColdkeySwapDisputes` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_coldkey_swap_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `733` + // Estimated: `4198` + // Minimum execution time: 16_832_000 picoseconds. + Weight::from_parts(17_583_000, 4198) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: `SubtensorModule::SubnetOwnerHotkey` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetOwnerHotkey` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TaoWeight` (r:1 w:0) + /// Proof: `SubtensorModule::TaoWeight` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:2 w:0) + /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ParentKeys` (r:1 w:0) + /// Proof: `SubtensorModule::ParentKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ChildKeys` (r:1 w:0) + /// Proof: `SubtensorModule::ChildKeys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::StakeThreshold` (r:1 w:0) + /// Proof: `SubtensorModule::StakeThreshold` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::WeightCommits` (r:1 w:0) + /// Proof: `SubtensorModule::WeightCommits` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::SubnetEpochIndex` (r:1 w:0) + /// Proof: `SubtensorModule::SubnetEpochIndex` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Tempo` (r:1 w:0) + /// Proof: `SubtensorModule::Tempo` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::RevealPeriodEpochs` (r:1 w:0) + /// Proof: `SubtensorModule::RevealPeriodEpochs` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_weights_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `1731` + // Estimated: `7671` + // Minimum execution time: 52_046_000 picoseconds. + Weight::from_parts(52_958_000, 7671) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + } + /// Storage: `SubtensorModule::CommitRevealWeightsEnabled` (r:1 w:0) + /// Proof: `SubtensorModule::CommitRevealWeightsEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) + /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MechanismCountCurrent` (r:1 w:0) + /// Proof: `SubtensorModule::MechanismCountCurrent` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Keys` (r:1 w:0) + /// Proof: `SubtensorModule::Keys` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::LastUpdate` (r:1 w:0) + /// Proof: `SubtensorModule::LastUpdate` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::WeightsSetRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::WeightsSetRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_rate_limits_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `1019` + // Estimated: `4484` + // Minimum execution time: 34_995_000 picoseconds. + Weight::from_parts(35_376_000, 4484) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + } + /// Storage: `SubtensorModule::MinDelegateTake` (r:1 w:0) + /// Proof: `SubtensorModule::MinDelegateTake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::MaxDelegateTake` (r:1 w:0) + /// Proof: `SubtensorModule::MaxDelegateTake` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Owner` (r:1 w:0) + /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_delegate_take_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `721` + // Estimated: `4186` + // Minimum execution time: 14_998_000 picoseconds. + Weight::from_parts(15_378_000, 4186) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::Axons` (r:1 w:0) + /// Proof: `SubtensorModule::Axons` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::ServingRateLimit` (r:1 w:0) + /// Proof: `SubtensorModule::ServingRateLimit` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_serving_endpoints_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `647` + // Estimated: `4112` + // Minimum execution time: 18_775_000 picoseconds. + Weight::from_parts(19_035_000, 4112) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + } + /// Storage: `SubtensorModule::Uids` (r:1 w:0) + /// Proof: `SubtensorModule::Uids` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AssociatedEvmAddress` (r:1 w:0) + /// Proof: `SubtensorModule::AssociatedEvmAddress` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn check_evm_key_association_extension() -> Weight { + // Proof Size summary in bytes: + // Measured: `652` + // Estimated: `4117` + // Minimum execution time: 15_018_000 picoseconds. + Weight::from_parts(15_448_000, 4117) + .saturating_add(RocksDbWeight::get().reads(2_u64)) } } diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs deleted file mode 100644 index a37e9e49ad..0000000000 --- a/pallets/swap-interface/src/lib.rs +++ /dev/null @@ -1,98 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -use core::ops::Neg; - -use frame_support::pallet_prelude::*; -use substrate_fixed::types::U96F32; -use subtensor_macros::freeze_struct; -use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; - -pub use order::*; - -mod order; - -pub trait SwapEngine: DefaultPriceLimit { - fn swap( - netuid: NetUid, - order: O, - price_limit: TaoBalance, - drop_fees: bool, - should_rollback: bool, - ) -> Result, DispatchError>; -} - -pub trait SwapHandler { - fn swap( - netuid: NetUid, - order: O, - price_limit: TaoBalance, - drop_fees: bool, - should_rollback: bool, - ) -> Result, DispatchError> - where - Self: SwapEngine; - fn sim_swap( - netuid: NetUid, - order: O, - ) -> Result, DispatchError> - where - Self: SwapEngine; - - fn approx_fee_amount(netuid: NetUid, amount: T) -> T; - fn current_alpha_price(netuid: NetUid) -> U96F32; - fn get_protocol_tao(netuid: NetUid) -> TaoBalance; - fn max_price() -> C; - fn min_price() -> C; - fn adjust_protocol_liquidity(netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance); - fn is_user_liquidity_enabled(netuid: NetUid) -> bool; - fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResultWithPostInfo; - 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 -where - PaidIn: Token, - PaidOut: Token, -{ - fn default_price_limit() -> C; -} - -/// Externally used swap result (for RPC) -#[freeze_struct("6a03533fc53ccfb8")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] -pub struct SwapResult -where - PaidIn: Token, - PaidOut: Token, -{ - pub amount_paid_in: PaidIn, - pub amount_paid_out: PaidOut, - pub fee_paid: PaidIn, - pub fee_to_block_author: PaidIn, -} - -impl SwapResult -where - PaidIn: Token, - PaidOut: Token, -{ - pub fn paid_in_reserve_delta(&self) -> i128 { - self.amount_paid_in.to_u64() as i128 - } - - pub fn paid_in_reserve_delta_i64(&self) -> i64 { - self.paid_in_reserve_delta() - .clamp(i64::MIN as i128, i64::MAX as i128) as i64 - } - - pub fn paid_out_reserve_delta(&self) -> i128 { - (self.amount_paid_out.to_u64() as i128).neg() - } - - pub fn paid_out_reserve_delta_i64(&self) -> i64 { - (self.amount_paid_out.to_u64() as i128) - .neg() - .clamp(i64::MIN as i128, i64::MAX as i128) as i64 - } -} diff --git a/pallets/swap/Cargo.toml b/pallets/swap/Cargo.toml index c50d1d4f78..7bc61caa50 100644 --- a/pallets/swap/Cargo.toml +++ b/pallets/swap/Cargo.toml @@ -11,6 +11,7 @@ frame-benchmarking = { workspace = true, optional = true } frame-support.workspace = true frame-system.workspace = true log.workspace = true +safe-bigmath.workspace = true safe-math.workspace = true scale-info = { workspace = true, features = ["derive"] } serde = { workspace = true, optional = true } @@ -28,6 +29,8 @@ subtensor-swap-interface.workspace = true [dev-dependencies] sp-tracing.workspace = true +rand = { version = "0.8", default-features = false } +rayon = "1.10" [lints] workspace = true @@ -42,6 +45,8 @@ std = [ "frame-system/std", "log/std", "pallet-subtensor-swap-runtime-api/std", + "rand/std", + "safe-bigmath/std", "safe-math/std", "scale-info/std", "serde/std", @@ -61,4 +66,5 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "subtensor-runtime-common/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks", ] diff --git a/pallets/swap/rpc/src/lib.rs b/pallets/swap/rpc/src/lib.rs index b4a8d6a7a0..fa072c29ae 100644 --- a/pallets/swap/rpc/src/lib.rs +++ b/pallets/swap/rpc/src/lib.rs @@ -13,12 +13,14 @@ use sp_blockchain::HeaderBackend; use sp_runtime::traits::Block as BlockT; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance}; -pub use pallet_subtensor_swap_runtime_api::SwapRuntimeApi; +pub use pallet_subtensor_swap_runtime_api::{SubnetPrice, SwapRuntimeApi}; #[rpc(client, server)] pub trait SwapRpcApi { #[method(name = "swap_currentAlphaPrice")] fn current_alpha_price(&self, netuid: NetUid, at: Option) -> RpcResult; + #[method(name = "swap_currentAlphaPriceAll")] + fn current_alpha_price_all(&self, at: Option) -> RpcResult>; #[method(name = "swap_simSwapTaoForAlpha")] fn sim_swap_tao_for_alpha( &self, @@ -92,6 +94,18 @@ where }) } + fn current_alpha_price_all( + &self, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + api.current_alpha_price_all(at).map_err(|e| { + Error::RuntimeError(format!("Unable to get all current alpha prices: {e:?}")).into() + }) + } + fn sim_swap_tao_for_alpha( &self, netuid: NetUid, diff --git a/pallets/swap/runtime-api/Cargo.toml b/pallets/swap/runtime-api/Cargo.toml index 042875fdd0..7a70dc74e3 100644 --- a/pallets/swap/runtime-api/Cargo.toml +++ b/pallets/swap/runtime-api/Cargo.toml @@ -8,6 +8,7 @@ edition.workspace = true codec = { workspace = true, features = ["derive"] } frame-support.workspace = true scale-info.workspace = true +serde.workspace = true sp-api.workspace = true sp-std.workspace = true subtensor-macros.workspace = true @@ -20,6 +21,7 @@ std = [ "codec/std", "frame-support/std", "scale-info/std", + "serde/std", "sp-api/std", "sp-std/std", "subtensor-runtime-common/std", diff --git a/pallets/swap/runtime-api/src/lib.rs b/pallets/swap/runtime-api/src/lib.rs index 0433793efb..0f9803f162 100644 --- a/pallets/swap/runtime-api/src/lib.rs +++ b/pallets/swap/runtime-api/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::pallet_prelude::*; +use serde::{Deserialize, Serialize}; use sp_std::vec::Vec; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance}; @@ -16,8 +17,8 @@ pub struct SimSwapResult { pub alpha_slippage: AlphaBalance, } -#[freeze_struct("423384310ac5e2f7")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +#[freeze_struct("d7bbb761fc2b2eac")] +#[derive(Decode, Deserialize, Encode, PartialEq, Eq, Clone, Debug, Serialize, TypeInfo)] pub struct SubnetPrice { pub netuid: NetUid, pub price: u64, diff --git a/pallets/swap/src/benchmarking.rs b/pallets/swap/src/benchmarking.rs index b486266741..eed3c5a25b 100644 --- a/pallets/swap/src/benchmarking.rs +++ b/pallets/swap/src/benchmarking.rs @@ -2,24 +2,11 @@ #![allow(clippy::unwrap_used)] #![allow(clippy::multiple_bound_locations)] -use core::marker::PhantomData; - use frame_benchmarking::v2::*; -use frame_support::assert_err; -use frame_support::traits::Get; use frame_system::RawOrigin; -use substrate_fixed::types::{I64F64, U64F64}; use subtensor_runtime_common::NetUid; -use crate::{ - Error, - pallet::{ - AlphaSqrtPrice, BenchmarkHelper, Call, Config, CurrentLiquidity, CurrentTick, - EnabledUserLiquidity, Pallet, Positions, SwapV3Initialized, - }, - position::{Position, PositionId}, - tick::TickIndex, -}; +use crate::pallet::{Call, Config, Pallet}; #[benchmarks(where T: Config)] mod benchmarks { @@ -34,164 +21,5 @@ mod benchmarks { _(RawOrigin::Root, netuid, rate); } - #[benchmark] - fn add_liquidity() { - let netuid = NetUid::from(1); - - if !SwapV3Initialized::::get(netuid) { - SwapV3Initialized::::insert(netuid, true); - AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); - CurrentTick::::insert(netuid, TickIndex::new(0).unwrap()); - CurrentLiquidity::::insert(netuid, T::MinimumLiquidity::get()); - } - - let caller: T::AccountId = whitelisted_caller(); - let hotkey: T::AccountId = account("hotkey", 0, 0); - let tick_low = TickIndex::new_unchecked(-1000); - let tick_high = TickIndex::new_unchecked(1000); - - #[block] - { - assert_err!( - Pallet::::add_liquidity( - RawOrigin::Signed(caller).into(), - hotkey, - netuid, - tick_low, - tick_high, - 1000, - ), - Error::::UserLiquidityDisabled - ); - } - } - - #[benchmark] - fn remove_liquidity() { - let netuid = NetUid::from(1); - - T::BenchmarkHelper::setup_subnet(netuid); - - if !SwapV3Initialized::::get(netuid) { - SwapV3Initialized::::insert(netuid, true); - AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); - CurrentTick::::insert(netuid, TickIndex::new(0).unwrap()); - CurrentLiquidity::::insert(netuid, T::MinimumLiquidity::get()); - } - - let caller: T::AccountId = whitelisted_caller(); - let hotkey: T::AccountId = account("hotkey", 0, 0); - T::BenchmarkHelper::register_hotkey(&hotkey, &caller); - let id = PositionId::from(1u128); - - Positions::::insert( - (netuid, caller.clone(), id), - Position { - id, - netuid, - tick_low: TickIndex::new(-10000).unwrap(), - tick_high: TickIndex::new(10000).unwrap(), - liquidity: 1000, - fees_tao: I64F64::from_num(0), - fees_alpha: I64F64::from_num(0), - _phantom: PhantomData, - }, - ); - - #[extrinsic_call] - _(RawOrigin::Signed(caller), hotkey, netuid.into(), id.into()); - } - - #[benchmark] - fn modify_position() { - let netuid = NetUid::from(1); - - T::BenchmarkHelper::setup_subnet(netuid); - EnabledUserLiquidity::::insert(netuid, true); - - if !SwapV3Initialized::::get(netuid) { - SwapV3Initialized::::insert(netuid, true); - AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); - CurrentTick::::insert(netuid, TickIndex::new(0).unwrap()); - CurrentLiquidity::::insert(netuid, T::MinimumLiquidity::get()); - } - - let caller: T::AccountId = whitelisted_caller(); - let hotkey: T::AccountId = account("hotkey", 0, 0); - T::BenchmarkHelper::register_hotkey(&hotkey, &caller); - let id = PositionId::from(1u128); - - Positions::::insert( - (netuid, caller.clone(), id), - Position { - id, - netuid, - tick_low: TickIndex::new(-10000).unwrap(), - tick_high: TickIndex::new(10000).unwrap(), - liquidity: 10000, - fees_tao: I64F64::from_num(0), - fees_alpha: I64F64::from_num(0), - _phantom: PhantomData, - }, - ); - - #[extrinsic_call] - _( - RawOrigin::Signed(caller), - hotkey, - netuid.into(), - id.into(), - -5000, - ); - } - - #[benchmark] - fn disable_lp() { - // Create a single user LP position so that do_dissolve_all_liquidity_providers - // executes its main path at least once. - let caller: T::AccountId = whitelisted_caller(); - let id = PositionId::from(1u128); - - for index in 1..=128 { - let netuid = NetUid::from(index); - - SwapV3Initialized::::insert(netuid, true); - AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); - CurrentTick::::insert(netuid, TickIndex::new(0).unwrap()); - CurrentLiquidity::::insert(netuid, T::MinimumLiquidity::get()); - - Positions::::insert( - (netuid, caller.clone(), id), - Position { - id, - netuid, - tick_low: TickIndex::new(-10000).unwrap(), - tick_high: TickIndex::new(10000).unwrap(), - liquidity: 1_000, - fees_tao: I64F64::from_num(0), - fees_alpha: I64F64::from_num(0), - _phantom: PhantomData, - }, - ); - - // Enable user liquidity on this subnet so the toggle path is exercised. - EnabledUserLiquidity::::insert(netuid, true); - } - - #[extrinsic_call] - disable_lp(RawOrigin::Root); - } - - #[benchmark] - fn toggle_user_liquidity() { - let netuid = NetUid::from(101); - T::BenchmarkHelper::setup_subnet(netuid); - - assert!(!EnabledUserLiquidity::::get(netuid)); - - #[extrinsic_call] - _(RawOrigin::Root, netuid.into(), true); - } - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/pallets/swap/src/lib.rs b/pallets/swap/src/lib.rs index 6257df852b..b51c3351dc 100644 --- a/pallets/swap/src/lib.rs +++ b/pallets/swap/src/lib.rs @@ -1,10 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -use substrate_fixed::types::U64F64; - pub mod pallet; -pub mod position; -pub mod tick; pub mod weights; pub use pallet::*; @@ -14,5 +10,3 @@ pub mod benchmarking; #[cfg(test)] pub(crate) mod mock; - -type SqrtPrice = U64F64; diff --git a/pallets/swap/src/mock.rs b/pallets/swap/src/mock.rs index 65dcc676a2..1b0ada87be 100644 --- a/pallets/swap/src/mock.rs +++ b/pallets/swap/src/mock.rs @@ -9,21 +9,17 @@ use frame_support::{ }; use frame_support::{construct_runtime, derive_impl}; use frame_system::{self as system}; -use scale_info::prelude::collections::HashMap; use sp_core::H256; use sp_runtime::{ BuildStorage, Vec, traits::{BlakeTwo256, IdentityLookup}, }; -use sp_std::cell::RefCell; -use substrate_fixed::types::U64F64; +use std::{cell::RefCell, collections::HashMap}; use subtensor_runtime_common::{ - AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, Token, TokenReserve, + AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, TokenReserve, }; use subtensor_swap_interface::Order; -use crate::pallet::{EnabledUserLiquidity, FeeGlobalAlpha, FeeGlobalTao}; - construct_runtime!( pub enum Test { System: frame_system = 0, @@ -69,7 +65,6 @@ impl system::Config for Test { parameter_types! { pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); pub const MaxFeeRate: u16 = 10000; // 15.26% - pub const MaxPositions: u32 = 100; pub const MinimumLiquidity: u64 = 1_000; pub const MinimumReserves: NonZeroU64 = NonZeroU64::new(1).unwrap(); } @@ -147,23 +142,7 @@ impl TokenReserve for AlphaReserve { pub type GetAlphaForTao = subtensor_swap_interface::GetAlphaForTao; pub type GetTaoForAlpha = subtensor_swap_interface::GetTaoForAlpha; -pub(crate) trait GlobalFeeInfo: Token { - #[allow(dead_code)] - fn global_fee(&self, netuid: NetUid) -> U64F64; -} - -impl GlobalFeeInfo for TaoBalance { - fn global_fee(&self, netuid: NetUid) -> U64F64 { - FeeGlobalTao::::get(netuid) - } -} - -impl GlobalFeeInfo for AlphaBalance { - fn global_fee(&self, netuid: NetUid) -> U64F64 { - FeeGlobalAlpha::::get(netuid) - } -} - +#[allow(dead_code)] pub(crate) trait TestExt { fn approx_expected_swap_output( sqrt_current_price: f64, @@ -293,7 +272,6 @@ impl crate::pallet::Config for Test { type BalanceOps = MockBalanceOps; type ProtocolId = SwapProtocolId; type MaxFeeRate = MaxFeeRate; - type MaxPositions = MaxPositions; type MinimumLiquidity = MinimumLiquidity; type MinimumReserve = MinimumReserves; type WeightInfo = (); @@ -310,12 +288,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(storage); ext.execute_with(|| { System::set_block_number(1); - - for netuid in 0u16..=100 { - // enable V3 for this range of netuids - EnabledUserLiquidity::::set(NetUid::from(netuid), true); - } - EnabledUserLiquidity::::set(NetUid::from(WRAPPING_FEES_NETUID), true); }); ext } diff --git a/pallets/swap/src/pallet/balancer.rs b/pallets/swap/src/pallet/balancer.rs new file mode 100644 index 0000000000..2ffd04fdba --- /dev/null +++ b/pallets/swap/src/pallet/balancer.rs @@ -0,0 +1,1339 @@ +// Balancer swap +// +// Unlike uniswap v2 or v3, it allows adding liquidity disproportionally to price. This is +// achieved by introducing the weights w1 and w2 so that w1 + w2 = 1. In these formulas x +// means base currency (alpha) and y means quote currency (tao). The w1 weight in the code +// below is referred as weight_base, and w2 as weight_quote. Because of the w1 + w2 = 1 +// constraint, only weight_quote is stored, and weight_base is always calculated. +// +// The formulas used for pool operation are following: +// +// Price: p = (w1*y) / (w2*x) +// +// Reserve deltas / (or -1 * payouts) in swaps are computed by: +// +// if ∆x is given (sell) ∆y = y * ((x / (x+∆x))^(w1/w2) - 1) +// if ∆y is given (buy) ∆x = x * ((y / (y+∆y))^(w2/w1) - 1) +// +// When swaps are executing the orders with slippage control, we need to know what amount +// we can swap before the price reaches the limit value of p': +// +// If p' < p (sell): ∆x = x * ((p / p')^w2 - 1) +// If p' < p (buy): ∆y = y * ((p' / p)^w1 - 1) +// +// In order to initialize weights with existing reserve values and price: +// +// w1 = px / (px + y) +// w2 = y / (px + y) +// +// Weights are adjusted when some amounts are added to the reserves. This prevents price +// from changing. +// +// new_w1 = p * (x + ∆x) / (p * (x + ∆x) + y + ∆y) +// new_w2 = (y + ∆y) / (p * (x + ∆x) + y + ∆y) +// +// Weights are limited to stay within [0.1, 0.9] range to avoid precision issues in exponentiation. +// Practically, these limitations will not be achieved, but if they are, the swap will not allow injection +// that will push the weights out of this interval because we prefer chain and swap stability over success +// of a single injection. Currently, we only allow the protocol to inject disproportionally to price, and +// the amount of disproportion will not cause weigths to get far from 0.5. +// + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::pallet_prelude::*; +use safe_bigmath::*; +use safe_math::*; +use sp_arithmetic::Perquintill; +use sp_core::U256; +use sp_runtime::Saturating; +use sp_std::ops::Neg; +use substrate_fixed::types::U64F64; +use subtensor_macros::freeze_struct; + +/// Balancer implements all high complexity math for swap operations such as: +/// - Swapping x for y, which includes limit orders +/// - Adding and removing liquidity (including unbalanced) +/// +/// Notation used in this file: +/// - x: Base reserve (alplha reserve) +/// - y: Quote reserve (tao reserve) +/// - ∆x: Alpha paid in/out +/// - ∆y: Tao paid in/out +/// - w1: Base weight (a.k.a weight_base) +/// - w2: Quote weight (a.k.a weight_quote) +#[freeze_struct("33a4fb0774da77c7")] +#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct Balancer { + quote: Perquintill, +} + +/// Accuracy matches to 18 decimal digits used to represent weights +pub const ACCURACY: u64 = 1_000_000_000_000_000_000_u64; +/// Lower imit of weights is 0.01 +pub const MIN_WEIGHT: Perquintill = Perquintill::from_parts(ACCURACY / 100); +/// 1.0 in Perquintill +pub const ONE: Perquintill = Perquintill::from_parts(ACCURACY); + +#[derive(Debug)] +pub enum BalancerError { + /// The provided weight value is out of range + InvalidValue, +} + +impl Default for Balancer { + /// The default value of weights is 0.5 for pool initialization + fn default() -> Self { + Self { + quote: Perquintill::from_rational(1u128, 2u128), + } + } +} + +impl Balancer { + /// Creates a new instance of balancer with a given quote weight + pub fn new(quote: Perquintill) -> Result { + if Self::check_constraints(quote) { + Ok(Balancer { quote }) + } else { + Err(BalancerError::InvalidValue) + } + } + + /// Constraints limit balancer weights within certain range of values: + /// - Both weights are above minimum + /// - Sum of weights is equal to 1.0 + fn check_constraints(quote: Perquintill) -> bool { + let base = ONE.saturating_sub(quote); + (base >= MIN_WEIGHT) && (quote >= MIN_WEIGHT) + } + + /// We store quote weight as Perquintill + pub fn get_quote_weight(&self) -> Perquintill { + self.quote + } + + /// Base weight is calculated as 1.0 - quote_weight + pub fn get_base_weight(&self) -> Perquintill { + ONE.saturating_sub(self.quote) + } + + /// Sets quote currency weight in the balancer. + /// Because sum of weights is always 1.0, there is no need to + /// store base currency weight + pub fn set_quote_weight(&mut self, new_value: Perquintill) -> Result<(), BalancerError> { + if Self::check_constraints(new_value) { + self.quote = new_value; + Ok(()) + } else { + Err(BalancerError::InvalidValue) + } + } + + /// If base_quote is true, calculate (x / (x + ∆x))^(weight_base / weight_quote), + /// otherwise, calculate (x / (x + ∆x))^(weight_quote / weight_base) + /// + /// Here we use SafeInt from bigmath crate for high-precision exponentiation, + /// which exposes the function pow_ratio_scaled. + /// + /// Note: ∆x may be negative + fn exp_scaled(&self, x: u64, dx: i128, base_quote: bool) -> U64F64 { + let x_plus_dx = if dx >= 0 { + x.saturating_add(dx as u64) + } else { + x.saturating_sub(dx.neg() as u64) + }; + + if x_plus_dx == 0 { + return U64F64::saturating_from_num(0); + } + let w1: u128 = self.get_base_weight().deconstruct() as u128; + let w2: u128 = self.get_quote_weight().deconstruct() as u128; + + let precision = 1024; + let x_safe = SafeInt::from(x); + let w1_safe = SafeInt::from(w1); + let w2_safe = SafeInt::from(w2); + let perquintill_scale = SafeInt::from(ACCURACY as u128); + let denominator = SafeInt::from(x_plus_dx); + log::debug!("x = {:?}", x); + log::debug!("dx = {:?}", dx); + log::debug!("x_safe = {:?}", x_safe); + log::debug!("denominator = {:?}", denominator); + log::debug!("w1_safe = {:?}", w1_safe); + log::debug!("w2_safe = {:?}", w2_safe); + log::debug!("precision = {:?}", precision); + log::debug!("perquintill_scale = {:?}", perquintill_scale); + + let maybe_result_safe_int = if base_quote { + SafeInt::pow_ratio_scaled( + &x_safe, + &denominator, + &w1_safe, + &w2_safe, + precision, + &perquintill_scale, + ) + } else { + SafeInt::pow_ratio_scaled( + &x_safe, + &denominator, + &w2_safe, + &w1_safe, + precision, + &perquintill_scale, + ) + }; + + if let Some(result_safe_int) = maybe_result_safe_int + && let Some(result_u64) = result_safe_int.to_u64() + { + return U64F64::saturating_from_num(result_u64) + .safe_div(U64F64::saturating_from_num(ACCURACY)); + } + U64F64::saturating_from_num(0) + } + + /// Calculates exponent of (x / (x + ∆x)) ^ (w_base/w_quote) + /// This method is used in sell swaps + /// (∆x is given by user, ∆y is paid out by the pool) + pub fn exp_base_quote(&self, x: u64, dx: u64) -> U64F64 { + self.exp_scaled(x, dx as i128, true) + } + + /// Calculates exponent of (y / (y + ∆y)) ^ (w_quote/w_base) + /// This method is used in buy swaps + /// (∆y is given by user, ∆x is paid out by the pool) + pub fn exp_quote_base(&self, y: u64, dy: u64) -> U64F64 { + self.exp_scaled(y, dy as i128, false) + } + + /// Calculates price as (w1/w2) * (y/x), where + /// - w1 is base weight + /// - w2 is quote weight + /// - x is base reserve + /// - y is quote reserve + pub fn calculate_price(&self, x: u64, y: u64) -> U64F64 { + let w2_fixed = U64F64::saturating_from_num(self.get_quote_weight().deconstruct()); + let w1_fixed = U64F64::saturating_from_num(self.get_base_weight().deconstruct()); + let x_fixed = U64F64::saturating_from_num(x); + let y_fixed = U64F64::saturating_from_num(y); + w1_fixed + .safe_div(w2_fixed) + .saturating_mul(y_fixed.safe_div(x_fixed)) + } + + /// Multiply a u128 value by a Perquintill with u128 result rounded to the + /// nearest integer + fn mul_perquintill_round(p: Perquintill, value: u128) -> u128 { + let parts = p.deconstruct() as u128; + let acc = ACCURACY as u128; + + let num = U256::from(value).saturating_mul(U256::from(parts)); + let den = U256::from(acc); + + // Add 0.5 before integer division to achieve rounding to the nearest + // integer + let zero = U256::from(0); + let res = num + .saturating_add(den.checked_div(U256::from(2u8)).unwrap_or(zero)) + .checked_div(den) + .unwrap_or(zero); + res.min(U256::from(u128::MAX)) + .try_into() + .unwrap_or_default() + } + + /// When liquidity is added to balancer swap, it may be added with arbitrary proportion, + /// not necessarily in the proportion of price, like with uniswap v2 or v3. In order to + /// stay within balancer pool invariant, the weights need to be updated. Invariant: + /// + /// L = x ^ weight_base * y ^ weight_quote + /// + /// Note that weights must remain within the proper range (both be above MIN_WEIGHT), + /// so only reasonably small disproportions of updates are appropriate. + pub fn update_weights_for_added_liquidity( + &mut self, + tao_reserve: u64, + alpha_reserve: u64, + tao_delta: u64, + alpha_delta: u64, + ) -> Result<(), BalancerError> { + // Calculate new to-be reserves (do not update here) + let tao_reserve_u128 = u64::from(tao_reserve) as u128; + let alpha_reserve_u128 = u64::from(alpha_reserve) as u128; + let tao_delta_u128 = u64::from(tao_delta) as u128; + let alpha_delta_u128 = u64::from(alpha_delta) as u128; + let new_tao_reserve_u128 = tao_reserve_u128.saturating_add(tao_delta_u128); + let new_alpha_reserve_u128 = alpha_reserve_u128.saturating_add(alpha_delta_u128); + + // Calculate new weights + let quantity_1: u128 = Self::mul_perquintill_round( + self.get_base_weight(), + tao_reserve_u128.saturating_mul(new_alpha_reserve_u128), + ); + let quantity_2: u128 = Self::mul_perquintill_round( + self.get_quote_weight(), + alpha_reserve_u128.saturating_mul(new_tao_reserve_u128), + ); + let q_sum = quantity_1.saturating_add(quantity_2); + + // Calculate new reserve weights + let new_reserve_weight = if q_sum != 0 { + // Both TAO and Alpha are non-zero, normal case + Perquintill::from_rational(quantity_2, q_sum) + } else { + // Either TAO or Alpha reserve were and/or remain zero => Initialize weights to 0.5 + Perquintill::from_rational(1u128, 2u128) + }; + + self.set_quote_weight(new_reserve_weight) + } + + /// Calculates quote delta needed to reach the price up when byuing + /// This method is needed for limit orders. + /// + /// Formula is: + /// ∆y = y * ((price_new / price)^weight_base - 1) + /// price_new >= price + pub fn calculate_quote_delta_in( + &self, + current_price: U64F64, + target_price: U64F64, + reserve: u64, + ) -> u64 { + let base_numerator: u128 = target_price.to_bits(); + let base_denominator: u128 = current_price.to_bits(); + let w1_fixed: u128 = self.get_base_weight().deconstruct() as u128; + let scale: u128 = 10u128.pow(18); + + let maybe_exp_result = SafeInt::pow_ratio_scaled( + &SafeInt::from(base_numerator), + &SafeInt::from(base_denominator), + &SafeInt::from(w1_fixed), + &SafeInt::from(ACCURACY), + 1024, + &SafeInt::from(scale), + ); + + if let Some(exp_result_safe_int) = maybe_exp_result { + let reserve_fixed = U64F64::saturating_from_num(reserve); + let one = U64F64::saturating_from_num(1); + let scale_fixed = U64F64::saturating_from_num(scale); + let exp_result_fixed = if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { + U64F64::saturating_from_num(exp_result_u64) + } else if u64::MAX < exp_result_safe_int { + U64F64::saturating_from_num(u64::MAX) + } else { + U64F64::saturating_from_num(0) + }; + reserve_fixed + .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) + .saturating_to_num::() + } else { + 0u64 + } + } + + /// Calculates base delta needed to reach the price down when selling + /// This method is needed for limit orders. + /// + /// Formula is: + /// ∆x = x * ((price / price_new)^weight_quote - 1) + /// price_new <= price + pub fn calculate_base_delta_in( + &self, + current_price: U64F64, + target_price: U64F64, + reserve: u64, + ) -> u64 { + let base_numerator: u128 = current_price.to_bits(); + let base_denominator: u128 = target_price.to_bits(); + let w2_fixed: u128 = self.get_quote_weight().deconstruct() as u128; + let scale: u128 = 10u128.pow(18); + + let maybe_exp_result = SafeInt::pow_ratio_scaled( + &SafeInt::from(base_numerator), + &SafeInt::from(base_denominator), + &SafeInt::from(w2_fixed), + &SafeInt::from(ACCURACY), + 1024, + &SafeInt::from(scale), + ); + + if let Some(exp_result_safe_int) = maybe_exp_result { + let one = U64F64::saturating_from_num(1); + let scale_fixed = U64F64::saturating_from_num(scale); + let reserve_fixed = U64F64::saturating_from_num(reserve); + let exp_result_fixed = if let Some(exp_result_u64) = exp_result_safe_int.to_u64() { + U64F64::saturating_from_num(exp_result_u64) + } else if u64::MAX < exp_result_safe_int { + U64F64::saturating_from_num(u64::MAX) + } else { + U64F64::saturating_from_num(0) + }; + reserve_fixed + .saturating_mul(exp_result_fixed.safe_div(scale_fixed).saturating_sub(one)) + .saturating_to_num::() + } else { + 0u64 + } + } + + /// Calculates amount of Alpha that needs to be sold to get a given amount of TAO + pub fn get_base_needed_for_quote( + &self, + tao_reserve: u64, + alpha_reserve: u64, + delta_tao: u64, + ) -> u64 { + let e = self.exp_scaled(tao_reserve, (delta_tao as i128).neg(), false); + let one = U64F64::from_num(1); + let alpha_reserve_fixed = U64F64::from_num(alpha_reserve); + // e > 1 in this case + alpha_reserve_fixed + .saturating_mul(e.saturating_sub(one)) + .saturating_to_num::() + } +} + +// cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests --nocapture +#[cfg(test)] +#[allow(clippy::expect_used, clippy::unwrap_used)] +#[cfg(feature = "std")] +mod tests { + use crate::pallet::Balancer; + use crate::pallet::balancer::*; + use approx::assert_abs_diff_eq; + use sp_arithmetic::Perquintill; + use std::panic::{AssertUnwindSafe, catch_unwind}; + + // Helper: convert Perquintill to f64 for comparison + fn perquintill_to_f64(p: Perquintill) -> f64 { + let parts = p.deconstruct() as f64; + parts / ACCURACY as f64 + } + + // Helper: convert U64F64 to f64 for comparison + fn f(v: U64F64) -> f64 { + v.to_num::() + } + + fn assert_no_panic(label: &str, f: F) -> R + where + F: FnOnce() -> R, + { + catch_unwind(AssertUnwindSafe(f)).unwrap_or_else(|_| panic!("{label} panicked")) + } + + #[test] + fn test_balancer_rejects_invalid_boundary_weights_without_panicking() { + [ + Perquintill::zero(), + Perquintill::from_parts(1), + MIN_WEIGHT.saturating_sub(Perquintill::from_parts(1)), + ONE.saturating_sub(MIN_WEIGHT) + .saturating_add(Perquintill::from_parts(1)), + ONE, + ] + .into_iter() + .for_each(|quote| { + assert_no_panic("Balancer::new invalid boundary weight", || { + assert!(Balancer::new(quote).is_err()); + }); + }); + + let mut balancer = Balancer::default(); + assert_no_panic("Balancer::set_quote_weight invalid boundary weight", || { + assert!(balancer.set_quote_weight(Perquintill::zero()).is_err()); + }); + assert_eq!( + balancer.get_quote_weight(), + Perquintill::from_rational(1u128, 2u128) + ); + } + + #[test] + fn test_balancer_extreme_exp_inputs_do_not_panic() { + let weights = [ + MIN_WEIGHT, + Perquintill::from_rational(1u128, 2u128), + ONE.saturating_sub(MIN_WEIGHT), + ]; + let inputs = [ + (0u64, 0u64), + (0u64, 1u64), + (1u64, 0u64), + (1u64, 1u64), + (1u64, u64::MAX), + (u64::MAX, 0u64), + (u64::MAX, 1u64), + (u64::MAX, u64::MAX), + ]; + + for quote in weights { + let balancer = Balancer::new(quote).unwrap(); + for (reserve, delta) in inputs { + assert_no_panic("exp_base_quote extreme input", || { + let _ = balancer.exp_base_quote(reserve, delta); + }); + assert_no_panic("exp_quote_base extreme input", || { + let _ = balancer.exp_quote_base(reserve, delta); + }); + assert_no_panic("exp_scaled negative extreme input", || { + let _ = balancer.exp_scaled(reserve, -(delta as i128), true); + let _ = balancer.exp_scaled(reserve, -(delta as i128), false); + }); + } + } + } + + #[test] + fn test_balancer_price_and_limit_delta_corner_cases_do_not_panic() { + let balancer = Balancer::new(MIN_WEIGHT).unwrap(); + let prices = [ + U64F64::from_num(0), + U64F64::from_num(1), + U64F64::from_num(u64::MAX), + ]; + let reserves = [0u64, 1u64, u64::MAX]; + + for x in reserves { + for y in reserves { + assert_no_panic("calculate_price corner reserves", || { + let _ = balancer.calculate_price(x, y); + }); + } + } + + for current_price in prices { + for target_price in prices { + for reserve in reserves { + assert_no_panic("calculate_quote_delta_in corner input", || { + let _ = + balancer.calculate_quote_delta_in(current_price, target_price, reserve); + }); + assert_no_panic("calculate_base_delta_in corner input", || { + let _ = + balancer.calculate_base_delta_in(current_price, target_price, reserve); + }); + } + } + } + } + + #[test] + fn test_balancer_liquidity_weight_update_extremes_do_not_panic() { + let inputs = [ + (0u64, 0u64, 0u64, 0u64), + (0u64, 0u64, u64::MAX, u64::MAX), + (0u64, u64::MAX, u64::MAX, 0u64), + (u64::MAX, 0u64, 0u64, u64::MAX), + (u64::MAX, u64::MAX, u64::MAX, u64::MAX), + (1u64, u64::MAX, u64::MAX, 1u64), + (u64::MAX, 1u64, 1u64, u64::MAX), + ]; + + for (tao_reserve, alpha_reserve, tao_delta, alpha_delta) in inputs { + let mut balancer = Balancer::default(); + assert_no_panic("update_weights_for_added_liquidity extreme input", || { + let _ = balancer.update_weights_for_added_liquidity( + tao_reserve, + alpha_reserve, + tao_delta, + alpha_delta, + ); + }); + } + } + + #[test] + fn test_balancer_base_needed_for_quote_extremes_do_not_panic() { + let balancer = Balancer::new(ONE.saturating_sub(MIN_WEIGHT)).unwrap(); + let inputs = [ + (0u64, 0u64, 0u64), + (0u64, 1u64, 1u64), + (1u64, 0u64, 1u64), + (1u64, 1u64, 0u64), + (1u64, 1u64, 1u64), + (1u64, 1u64, u64::MAX), + (u64::MAX, u64::MAX, 0u64), + (u64::MAX, u64::MAX, u64::MAX), + ]; + + for (tao_reserve, alpha_reserve, delta_tao) in inputs { + assert_no_panic("get_base_needed_for_quote extreme input", || { + let _ = balancer.get_base_needed_for_quote(tao_reserve, alpha_reserve, delta_tao); + }); + } + } + + #[test] + fn test_safe_bigmath_pow_ratio_internal_paths_do_not_panic() { + let base_num = SafeInt::from(999_999_937u64); + let base_den = SafeInt::from(1_000_000_003u64); + let scale = SafeInt::from(1_000_000u64); + let cases = [ + // Exact integer/root path with exponent values at the safe-bigmath threshold. + ( + SafeInt::from(1024u32), + SafeInt::one(), + "exact max numerator", + ), + ( + SafeInt::from(999u32), + SafeInt::from(1024u32), + "exact root denominator", + ), + // One step over the threshold forces the fixed-point ln/exp fallback path. + (SafeInt::from(1025u32), SafeInt::one(), "fallback numerator"), + ( + SafeInt::from(999u32), + SafeInt::from(1025u32), + "fallback denominator", + ), + // GCD reduction should route this back to the exact path. + ( + SafeInt::from(2048u32), + SafeInt::from(4096u32), + "gcd reduced", + ), + ]; + + for (exp_num, exp_den, label) in cases { + let result = assert_no_panic(label, || { + SafeInt::pow_ratio_scaled(&base_num, &base_den, &exp_num, &exp_den, 64, &scale) + }); + assert!(result.is_some(), "{label} should produce a result"); + } + } + + #[test] + fn test_balancer_near_equal_weights_with_tiny_delta_do_not_panic() { + let weights = [ + Perquintill::from_parts(500_000_000_500_000_000), + Perquintill::from_parts(499_999_999_500_000_000), + Perquintill::from_parts(500_000_000_000_500_000), + Perquintill::from_parts(499_999_999_999_500_000), + ]; + let reserve = 21_000_000_000_000_000u64; + let tiny_deltas = [1u64, 100u64, 100_000u64]; + + for quote in weights { + let balancer = Balancer::new(quote).unwrap(); + for delta in tiny_deltas { + assert_no_panic("near-equal exp_base_quote tiny delta", || { + let e = balancer.exp_base_quote(reserve, delta); + assert!(e <= U64F64::from_num(1)); + assert!(e > U64F64::from_num(0)); + }); + assert_no_panic("near-equal exp_quote_base tiny delta", || { + let e = balancer.exp_quote_base(reserve, delta); + assert!(e <= U64F64::from_num(1)); + assert!(e > U64F64::from_num(0)); + }); + } + } + } + + #[test] + fn test_balancer_log_normalization_reserve_shapes_do_not_panic() { + let balancer = Balancer::new(Perquintill::from_parts(500_000_000_500_000_000)).unwrap(); + let reserves = [ + (1u64 << 42) - 1, + 1u64 << 42, + (1u64 << 42) + 1, + ((1u64 << 42) + (1u64 << 41)) - 1, + (1u64 << 42) + (1u64 << 41), + ((1u64 << 42) + (1u64 << 41)) + 1, + ]; + + for reserve in reserves { + for delta in [1u64, reserve / 1_000, reserve / 2] { + assert_no_panic("log-normalization exp_base_quote", || { + let e = balancer.exp_base_quote(reserve, delta); + assert!(e <= U64F64::from_num(1)); + }); + assert_no_panic("log-normalization exp_quote_base", || { + let e = balancer.exp_quote_base(reserve, delta); + assert!(e <= U64F64::from_num(1)); + }); + } + } + } + + #[test] + fn test_perquintill_power() { + const PRECISION: u32 = 4096; + const PERQUINTILL: u128 = ACCURACY as u128; + + let x = SafeInt::from(21_000_000_000_000_000u64); + let delta = SafeInt::from(7_000_000_000_000_000u64); + let w1 = SafeInt::from(600_000_000_000_000_000u128); + let w2 = SafeInt::from(400_000_000_000_000_000u128); + let denominator = &x + δ + assert_eq!(w1.clone() + w2.clone(), SafeInt::from(PERQUINTILL)); + + let perquintill_result = SafeInt::pow_ratio_scaled( + &x, + &denominator, + &w1, + &w2, + PRECISION, + &SafeInt::from(PERQUINTILL), + ) + .expect("perquintill integer result"); + + assert_eq!( + perquintill_result, + SafeInt::from(649_519_052_838_328_985u128) + ); + let readable = safe_bigmath::SafeDec::<18>::from_raw(perquintill_result); + assert_eq!(format!("{}", readable), "0.649519052838328985"); + } + + /// Validate realistic values that can be calculated with f64 precision + #[test] + fn test_exp_base_quote_happy_path() { + // Outer test cases: w_quote + [ + Perquintill::from_rational(500_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_001_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(499_999_999_999_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_000_100_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_001_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_010_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_000_100_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_001_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_010_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(500_100_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(501_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(510_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(100_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(100_000_000_001_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(200_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(300_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(400_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(600_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(700_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(800_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(899_999_999_999_u128, 1_000_000_000_000_u128), + Perquintill::from_rational(900_000_000_000_u128, 1_000_000_000_000_u128), + Perquintill::from_rational( + 102_337_248_363_782_924_u128, + 1_000_000_000_000_000_000_u128, + ), + ] + .into_iter() + .for_each(|w_quote| { + // Inner test cases: y, x, ∆x + [ + (1_000_u64, 1_000_u64, 0_u64), + (1_000_u64, 1_000_u64, 1_u64), + (1_500_u64, 1_000_u64, 1_u64), + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 1_000_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 100_000_000_u64, + ), + (100_000_000_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + ( + 100_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + ( + 1_000_000_u64, + 100_000_000_000_000_u64, + 1_000_000_000_000_u64, + ), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 1_000_u64), + (1_000_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + (10_u64, 100_000_000_000_000_u64, 100_000_000_000_000_u64), + // Extreme values of ∆x for small x + (1_000_000_000_u64, 4_000_000_000_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_u64, 1_000_000_000_000_u64), + ( + 5_628_038_062_729_553_u64, + 400_775_553_u64, + 14_446_633_907_665_582_u64, + ), + ( + 5_600_000_000_000_000_u64, + 400_000_000_u64, + 14_000_000_000_000_000_u64, + ), + ] + .into_iter() + .for_each(|(y, x, dx)| { + let bal = Balancer::new(w_quote).unwrap(); + let e1 = bal.exp_base_quote(x, dx); + let e2 = bal.exp_quote_base(x, dx); + let one = U64F64::from_num(1); + let y_fixed = U64F64::from_num(y); + let dy1 = y_fixed * (one - e1); + let dy2 = y_fixed * (one - e2); + + let w1 = perquintill_to_f64(bal.get_base_weight()); + let w2 = perquintill_to_f64(bal.get_quote_weight()); + let e1_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); + let dy1_expected = y as f64 * (1. - e1_expected); + let e2_expected = (x as f64 / (x as f64 + dx as f64)).powf(w2 / w1); + let dy2_expected = y as f64 * (1. - e2_expected); + + // Start tolerance with 0.001 rao + let mut eps1 = 0.001; + let mut eps2 = 0.001; + + // If swapping more than 100k tao/alpha, relax tolerance to 1.0 rao + if dy1_expected > 100_000_000_000_000_f64 { + eps1 = 1.0; + } + if dy2_expected > 100_000_000_000_000_f64 { + eps2 = 1.0; + } + assert_abs_diff_eq!(f(dy1), dy1_expected, epsilon = eps1); + assert_abs_diff_eq!(f(dy2), dy2_expected, epsilon = eps2); + }) + }); + } + + /// This test exercises practical application edge cases of exp_base_quote + /// The practical formula where this function is used: + /// ∆y = y * (exp_base_quote(x, ∆x) - 1) + /// + /// The test validates that two different sets of parameters produce (sensibly) + /// different results + /// + #[test] + fn test_exp_base_quote_dy_precision() { + // Test cases: y, x1, ∆x1, w_quote1, x2, ∆x2, w_quote2 + // Realized dy1 should be greater than dy2 + [ + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_001_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_001_u128), + 21_000_000_000_000_000_u64, + 21_000_000_000_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 2_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_010_000_000_000_u128, 2_000_000_000_000_u128), + ), + ( + 1_000_000_000_u64, + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_010_000_000_000_u128), + 21_000_000_000_000_000_u64, + 1_u64, + Perquintill::from_rational(1_000_000_000_000_u128, 2_000_000_000_000_u128), + ), + ] + .into_iter() + .for_each(|(y, x1, dx1, w_quote1, x2, dx2, w_quote2)| { + let bal1 = Balancer::new(w_quote1).unwrap(); + let bal2 = Balancer::new(w_quote2).unwrap(); + + let exp1 = bal1.exp_base_quote(x1, dx1); + let exp2 = bal2.exp_base_quote(x2, dx2); + + let one = U64F64::from_num(1); + let y_fixed = U64F64::from_num(y); + let dy1 = y_fixed * (one - exp1); + let dy2 = y_fixed * (one - exp2); + + assert!(dy1 > dy2); + + let zero = U64F64::from_num(0); + assert!(dy1 != zero); + assert!(dy2 != zero); + }) + } + + /// Test the broad range of w_quote values, usually should be ignored + #[ignore] + #[test] + fn test_exp_quote_broad_range() { + let y = 1_000_000_000_000_u64; + let x = 100_000_000_000_000_u64; + let dx = 10_000_000_u64; + + let mut prev = U64F64::from_num(1_000_000_000); + let mut last_progress = 0.; + let start = 100_000_000_000_u128; + let stop = 900_000_000_000_u128; + for num in (start..=stop).step_by(1000_usize) { + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + let e = bal.exp_base_quote(x, dx); + + let one = U64F64::from_num(1); + let dy = U64F64::from_num(y) * (one - e); + + let progress = (num as f64 - start as f64) / (stop as f64 - start as f64); + if progress - last_progress >= 0.0001 { + // Replace with println for real-time progress + log::debug!("progress = {:?}%", progress * 100.); + log::debug!("dy = {:?}", dy); + last_progress = progress; + } + + assert!(dy != U64F64::from_num(0)); + assert!(dy <= prev); + prev = dy; + } + } + + #[ignore] + #[test] + fn test_exp_quote_fuzzy() { + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; + use rayon::prelude::*; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + + const ITERATIONS: usize = 1_000_000_000; + let counter = Arc::new(AtomicUsize::new(0)); + + (0..ITERATIONS) + .into_par_iter() + .for_each(|i| { + // Each iteration gets its own deterministic RNG. + // Seed depends on i, so runs are reproducible. + let mut rng = StdRng::seed_from_u64(42 + i as u64); + let max_supply: u64 = 21_000_000_000_000_000; + let full_range = true; + + let x: u64 = rng.gen_range(1_000..=max_supply); // Alpha reserve + let y: u64 = if full_range { + // TAO reserve (allow huge prices) + rng.gen_range(1_000..=max_supply) + } else { + // TAO reserve (limit prices with 0-1000) + rng.gen_range(1_000..x.saturating_mul(1000).min(max_supply)) + }; + let dx: u64 = if full_range { + // Alhpa sold (allow huge values) + rng.gen_range(1_000..=21_000_000_000_000_000) + } else { + // Alhpa sold (do not sell more than 100% of what's in alpha reserve) + rng.gen_range(1_000..=x) + }; + let w_numerator: u64 = rng.gen_range(ACCURACY / 10..=ACCURACY / 10 * 9); + let w_quote = Perquintill::from_rational(w_numerator, ACCURACY); + + let bal = Balancer::new(w_quote).unwrap(); + let e = bal.exp_base_quote(x, dx); + + let one = U64F64::from_num(1); + let dy = U64F64::from_num(y) * (one - e); + + // Calculate expected in f64 and approx-assert + let w1 = perquintill_to_f64(bal.get_base_weight()); + let w2 = perquintill_to_f64(bal.get_quote_weight()); + let e_expected = (x as f64 / (x as f64 + dx as f64)).powf(w1 / w2); + let dy_expected = y as f64 * (1. - e_expected); + + let actual = dy.to_num::(); + let eps = (dy_expected / 1_000_000.).clamp(1.0, 1000.0); + + assert!( + (actual - dy_expected).abs() <= eps, + "dy mismatch:\n actual: {}\n expected: {}\n eps: {}\nParameters:\n x: {}\n y: {}\n dx: {}\n w_numerator: {}\n", + actual, dy_expected, eps, x, y, dx, w_numerator, + ); + + // Assert that we aren't giving out more than reserve y + assert!(dy <= y, "dy = {},\ny = {}", dy, y,); + + // Print progress + let done = counter.fetch_add(1, Ordering::Relaxed) + 1; + if done % 100_000_000 == 0 { + let progress = done as f64 / ITERATIONS as f64 * 100.0; + // Replace with println for real-time progress + log::debug!("progress = {progress:.4}%"); + } + }); + } + + #[test] + fn test_calculate_quote_delta_in() { + let num = 250_000_000_000_u128; // w1 = 0.75 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.2); + let tao_reserve: u64 = 1_000_000_000; + + let dy = bal.calculate_quote_delta_in(current_price, target_price, tao_reserve); + + // ∆y = y•[(p'/p)^w1 - 1] + let dy_expected = tao_reserve as f64 + * ((target_price.to_num::() / current_price.to_num::()).powf(0.75) - 1.0); + + assert_eq!(dy, dy_expected as u64,); + } + + #[test] + fn test_calculate_base_delta_in() { + let num = 250_000_000_000_u128; // w2 = 0.25 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + + let current_price: U64F64 = U64F64::from_num(0.2); + let target_price: U64F64 = U64F64::from_num(0.1); + let alpha_reserve: u64 = 1_000_000_000; + + let dx = bal.calculate_base_delta_in(current_price, target_price, alpha_reserve); + + // ∆x = x•[(p/p')^w2 - 1] + let dx_expected = alpha_reserve as f64 + * ((current_price.to_num::() / target_price.to_num::()).powf(0.25) - 1.0); + + assert_eq!(dx, dx_expected as u64,); + } + + #[test] + fn test_calculate_quote_delta_in_impossible() { + let num = 250_000_000_000_u128; // w1 = 0.75 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + + // Impossible price (lower) + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.05); + let tao_reserve: u64 = 1_000_000_000; + + let dy = bal.calculate_quote_delta_in(current_price, target_price, tao_reserve); + let dy_expected = 0u64; + + assert_eq!(dy, dy_expected); + } + + #[test] + fn test_calculate_base_delta_in_impossible() { + let num = 250_000_000_000_u128; // w2 = 0.25 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + + // Impossible price (higher) + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.2); + let alpha_reserve: u64 = 1_000_000_000; + + let dx = bal.calculate_base_delta_in(current_price, target_price, alpha_reserve); + let dx_expected = 0u64; + + assert_eq!(dx, dx_expected); + } + + #[test] + fn test_calculate_delta_in_reverse_swap() { + let num = 500_000_000_000_u128; + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + + let current_price: U64F64 = U64F64::from_num(0.1); + let target_price: U64F64 = U64F64::from_num(0.2); + let tao_reserve: u64 = 1_000_000_000; + + // Here is the simple case of w1 = w2 = 0.5, so alpha = tao / price + let alpha_reserve: u64 = (tao_reserve as f64 / current_price.to_num::()) as u64; + + let dy = bal.calculate_quote_delta_in(current_price, target_price, tao_reserve); + let dx = alpha_reserve as f64 + * (1.0 + - (tao_reserve as f64 / (tao_reserve as f64 + dy as f64)) + .powf(num as f64 / (1_000_000_000_000 - num) as f64)); + + // Verify that buying with dy will in fact bring the price to target_price + let actual_price = bal.calculate_price(alpha_reserve - dx as u64, tao_reserve + dy); + assert_abs_diff_eq!( + actual_price.to_num::(), + target_price.to_num::(), + epsilon = target_price.to_num::() / 1_000_000_000. + ); + } + + #[test] + fn test_mul_round_zero_and_one() { + let v = 1_000_000u128; + + // p = 0 -> always 0 + assert_eq!(Balancer::mul_perquintill_round(Perquintill::zero(), v), 0); + + // p = 1 -> identity + assert_eq!(Balancer::mul_perquintill_round(Perquintill::one(), v), v); + } + + #[test] + fn test_mul_round_half_behaviour() { + // p = 1/2 + let p = Perquintill::from_rational(1u128, 2u128); + + // Check rounding around .5 boundaries + // value * 1/2, rounded to nearest + assert_eq!(Balancer::mul_perquintill_round(p, 0), 0); // 0.0 -> 0 + assert_eq!(Balancer::mul_perquintill_round(p, 1), 1); // 0.5 -> 1 (round up) + assert_eq!(Balancer::mul_perquintill_round(p, 2), 1); // 1.0 -> 1 + assert_eq!(Balancer::mul_perquintill_round(p, 3), 2); // 1.5 -> 2 + assert_eq!(Balancer::mul_perquintill_round(p, 4), 2); // 2.0 -> 2 + assert_eq!(Balancer::mul_perquintill_round(p, 5), 3); // 2.5 -> 3 + assert_eq!(Balancer::mul_perquintill_round(p, 1023), 512); // 511.5 -> 512 + assert_eq!(Balancer::mul_perquintill_round(p, 1025), 513); // 512.5 -> 513 + } + + #[test] + fn test_mul_round_third_behaviour() { + // p = 1/3 + let p = Perquintill::from_rational(1u128, 3u128); + + // value * 1/3, rounded to nearest + assert_eq!(Balancer::mul_perquintill_round(p, 3), 1); // 1.0 -> 1 + assert_eq!(Balancer::mul_perquintill_round(p, 4), 1); // 1.333... -> 1 + assert_eq!(Balancer::mul_perquintill_round(p, 5), 2); // 1.666... -> 2 + assert_eq!(Balancer::mul_perquintill_round(p, 6), 2); // 2.0 -> 2 + } + + #[test] + fn test_mul_round_large_values_simple_rational() { + // p = 7/10 (exact in perquintill: 0.7) + let p = Perquintill::from_rational(7u128, 10u128); + let v: u128 = 1_000_000_000_000_000_000; + + let res = Balancer::mul_perquintill_round(p, v); + + // Expected = round(0.7 * v) with pure integer math: + // round(v * 7 / 10) = (v*7 + 10/2) / 10 + let expected = (v.saturating_mul(7) + 10 / 2) / 10; + + assert_eq!(res, expected); + } + + #[test] + fn test_mul_round_max_value_with_one() { + let v = u128::MAX; + let p = ONE; + + // For p = 1, result must be exactly value, and must not overflow + let res = Balancer::mul_perquintill_round(p, v); + assert_eq!(res, v); + } + + #[test] + fn test_price_with_equal_weights_is_y_over_x() { + // quote = 0.5, base = 0.5 -> w1 / w2 = 1, so price = y/x + let quote = Perquintill::from_rational(1u128, 2u128); + let bal = Balancer::new(quote).unwrap(); + + let x = 2u64; + let y = 5u64; + + let price = bal.calculate_price(x, y); + let price_f = f(price); + + let expected_f = (y as f64) / (x as f64); + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-12); + } + + #[test] + fn test_price_scales_with_weight_ratio_two_to_one() { + // Assume base = 1 - quote. + // quote = 1/3 -> base = 2/3, so w1 / w2 = 2. + // Then price = 2 * (y/x). + let quote = Perquintill::from_rational(1u128, 3u128); + let bal = Balancer::new(quote).unwrap(); + + let x = 4u64; + let y = 10u64; + + let price_f = f(bal.calculate_price(x, y)); + let expected_f = 2.0 * (y as f64 / x as f64); + + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-10); + } + + #[test] + fn test_price_is_zero_when_y_is_zero() { + // If y = 0, y/x = 0 so price must be 0 regardless of weights (for x > 0). + let quote = Perquintill::from_rational(3u128, 10u128); // 0.3 + let bal = Balancer::new(quote).unwrap(); + + let x = 10u64; + let y = 0u64; + + let price_f = f(bal.calculate_price(x, y)); + assert_abs_diff_eq!(price_f, 0.0, epsilon = 0.0); + } + + #[test] + fn test_price_invariant_when_scaling_x_and_y_with_equal_weights() { + // For equal weights, price(x, y) == price(kx, ky). + let quote = Perquintill::from_rational(1u128, 2u128); // 0.5 + let bal = Balancer::new(quote).unwrap(); + + let x1 = 3u64; + let y1 = 7u64; + let k = 10u64; + let x2 = x1 * k; + let y2 = y1 * k; + + let p1 = f(bal.calculate_price(x1, y1)); + let p2 = f(bal.calculate_price(x2, y2)); + + assert_abs_diff_eq!(p1, p2, epsilon = 1e-12); + } + + #[test] + fn test_price_matches_formula_for_general_quote() { + // General check: price = (w1 / w2) * (y/x), + // where w1 = base_weight, w2 = quote_weight. + // Here we assume get_base_weight = 1 - quote. + let quote = Perquintill::from_rational(2u128, 5u128); // 0.4 + let bal = Balancer::new(quote).unwrap(); + + let x = 9u64; + let y = 25u64; + + let price_f = f(bal.calculate_price(x, y)); + + let base = Perquintill::one() - quote; + let w1 = base.deconstruct() as f64; + let w2 = quote.deconstruct() as f64; + + let expected_f = (w1 / w2) * (y as f64 / x as f64); + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-9); + } + + #[test] + fn test_price_high_values_non_equal_weights() { + // Non-equal weights, high x and y (up to 21e15) + let quote = Perquintill::from_rational(3u128, 10u128); // 0.3 + let bal = Balancer::new(quote).unwrap(); + + let x: u64 = 21_000_000_000_000_000; + let y: u64 = 15_000_000_000_000_000; + + let price = bal.calculate_price(x, y); + let price_f = f(price); + + // Expected: (w1 / w2) * (y / x), using Balancer's actual weights + let w1 = bal.get_base_weight().deconstruct() as f64; + let w2 = bal.get_quote_weight().deconstruct() as f64; + let expected_f = (w1 / w2) * (y as f64 / x as f64); + + assert_abs_diff_eq!(price_f, expected_f, epsilon = 1e-9); + } + + // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests::test_exp_scaled --exact --nocapture + #[test] + fn test_exp_scaled() { + [ + // base_weight_numerator, base_weight_denominator, reserve, d_reserve, base_quote + (5_u64, 10_u64, 100000_u64, 100_u64, true, 0.999000999000999), + (1_u64, 4_u64, 500000_u64, 5000_u64, true, 0.970590147927644), + (3_u64, 4_u64, 200000_u64, 2000_u64, false, 0.970590147927644), + ( + 9_u64, + 10_u64, + 13513642_u64, + 1673_u64, + false, + 0.998886481979889, + ), + ( + 773_u64, + 1000_u64, + 7_000_000_000_u64, + 10_000_u64, + true, + 0.999999580484586, + ), + ] + .into_iter() + .map(|v| { + ( + Perquintill::from_rational(v.0, v.1), + v.2, + v.3, + v.4, + U64F64::from_num(v.5), + ) + }) + .for_each(|(quote_weight, reserve, d_reserve, base_quote, expected)| { + let balancer = Balancer::new(quote_weight).unwrap(); + let result = balancer.exp_scaled(reserve, d_reserve as i128, base_quote); + assert_abs_diff_eq!( + result.to_num::(), + expected.to_num::(), + epsilon = 0.000000001 + ); + }); + } + + // cargo test --package pallet-subtensor-swap --lib -- pallet::balancer::tests::test_base_needed_for_quote --exact --nocapture + #[test] + fn test_base_needed_for_quote() { + let num = 250_000_000_000_u128; // w1 = 0.75 + let w_quote = Perquintill::from_rational(num, 1_000_000_000_000_u128); + let bal = Balancer::new(w_quote).unwrap(); + + let tao_reserve: u64 = 1_000_000_000; + let alpha_reserve: u64 = 1_000_000_000; + let tao_delta: u64 = 1_123_432; // typical fee range + + let dx = bal.get_base_needed_for_quote(tao_reserve, alpha_reserve, tao_delta); + + // ∆x = x•[(y/(y+∆y))^(w2/w1) - 1] + let dx_expected = tao_reserve as f64 + * ((tao_reserve as f64 / ((tao_reserve - tao_delta) as f64)).powf(0.25 / 0.75) - 1.0); + + assert_eq!(dx, dx_expected as u64,); + } +} diff --git a/pallets/swap/src/pallet/hooks.rs b/pallets/swap/src/pallet/hooks.rs new file mode 100644 index 0000000000..90989d5f52 --- /dev/null +++ b/pallets/swap/src/pallet/hooks.rs @@ -0,0 +1,30 @@ +use frame_support::pallet_macros::pallet_section; + +#[pallet_section] +mod hooks { + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(block_number: BlockNumberFor) -> Weight { + Weight::from_parts(0, 0) + } + + fn on_finalize(_block_number: BlockNumberFor) {} + + fn on_runtime_upgrade() -> Weight { + // --- Migrate storage + let mut weight = Weight::from_parts(0, 0); + + weight = weight + // Cleanup uniswap v3 and migrate to balancer + .saturating_add( + migrations::migrate_swapv3_to_balancer::migrate_swapv3_to_balancer::(), + ); + weight + } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Ok(()) + } + } +} diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 7be087c0f0..c3e0b2f1d3 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -1,216 +1,118 @@ -use core::ops::Neg; - -use frame_support::dispatch::DispatchResultWithPostInfo; use frame_support::storage::{TransactionOutcome, transactional}; -use frame_support::{ensure, pallet_prelude::DispatchError, traits::Get, weights::Weight}; +use frame_support::{ + ensure, + pallet_prelude::{DispatchError, Zero}, + traits::Get, +}; use safe_math::*; -use sp_arithmetic::{helpers_128bit, traits::Zero}; +use sp_arithmetic::Perquintill; use sp_runtime::{DispatchResult, traits::AccountIdConversion}; -use substrate_fixed::types::{I64F64, U64F64, U96F32}; -use subtensor_runtime_common::{ - AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, Token, TokenReserve, -}; - -use super::pallet::*; -use super::swap_step::{BasicSwapStep, SwapStep, SwapStepAction}; -use crate::{ - SqrtPrice, - position::{Position, PositionId}, - tick::{ActiveTickIndexManager, Tick, TickIndex}, -}; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::{AlphaBalance, NetUid, SubnetInfo, TaoBalance, Token, TokenReserve}; use subtensor_swap_interface::{ DefaultPriceLimit, Order as OrderT, SwapEngine, SwapHandler, SwapResult, }; -const MAX_SWAP_ITERATIONS: u16 = 1000; - -#[derive(Debug, PartialEq)] -pub struct UpdateLiquidityResult { - pub tao: TaoBalance, - pub alpha: AlphaBalance, - pub fee_tao: TaoBalance, - pub fee_alpha: AlphaBalance, - pub removed: bool, - pub tick_low: TickIndex, - pub tick_high: TickIndex, -} - -#[derive(Debug, PartialEq)] -pub struct RemoveLiquidityResult { - pub tao: TaoBalance, - pub alpha: AlphaBalance, - pub fee_tao: TaoBalance, - pub fee_alpha: AlphaBalance, - pub tick_low: TickIndex, - pub tick_high: TickIndex, - pub liquidity: u64, -} +use super::pallet::*; +use super::swap_step::{BasicSwapStep, SwapStep}; +use crate::{pallet::Balancer, pallet::balancer::BalancerError}; impl Pallet { - pub fn current_price(netuid: NetUid) -> U96F32 { + pub fn current_price(netuid: NetUid) -> U64F64 { match T::SubnetInfo::mechanism(netuid.into()) { 1 => { - if SwapV3Initialized::::get(netuid) { - let sqrt_price = AlphaSqrtPrice::::get(netuid); - U96F32::saturating_from_num(sqrt_price.saturating_mul(sqrt_price)) - } else { + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + if !alpha_reserve.is_zero() { let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - if !alpha_reserve.is_zero() { - U96F32::saturating_from_num(tao_reserve) - .saturating_div(U96F32::saturating_from_num(alpha_reserve)) - } else { - U96F32::saturating_from_num(0) - } + let balancer = SwapBalancer::::get(netuid); + balancer.calculate_price(alpha_reserve.into(), tao_reserve.into()) + } else { + U64F64::saturating_from_num(0) } } - _ => U96F32::saturating_from_num(1), + _ => U64F64::saturating_from_num(1), } } - // initializes V3 swap for a subnet if needed - pub fn maybe_initialize_v3(netuid: NetUid) -> Result<(), Error> { - if SwapV3Initialized::::get(netuid) { + // initializes pal-swap (balancer) for a subnet if needed + pub fn maybe_initialize_palswap( + netuid: NetUid, + maybe_price: Option, + ) -> Result<(), Error> { + if PalSwapInitialized::::get(netuid) { return Ok(()); } - // Initialize the v3: - // Reserves are re-purposed, nothing to set, just query values for liquidity and price - // calculation + // Query reserves let tao_reserve = T::TaoReserve::reserve(netuid.into()); let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); - // Set price - let price = U64F64::saturating_from_num(tao_reserve) - .safe_div(U64F64::saturating_from_num(alpha_reserve)); - - let epsilon = U64F64::saturating_from_num(0.000000000001); - - let current_sqrt_price = price.checked_sqrt(epsilon).unwrap_or(U64F64::from_num(0)); - AlphaSqrtPrice::::set(netuid, current_sqrt_price); - - // Set current tick - let current_tick = TickIndex::from_sqrt_price_bounded(current_sqrt_price); - CurrentTick::::set(netuid, current_tick); - - // Set initial (protocol owned) liquidity and positions - // Protocol liquidity makes one position from TickIndex::MIN to TickIndex::MAX - // We are using the sp_arithmetic sqrt here, which works for u128 - let liquidity = helpers_128bit::sqrt( - (tao_reserve.to_u64() as u128).saturating_mul(alpha_reserve.to_u64() as u128), - ) as u64; - let protocol_account_id = Self::protocol_account_id(); - - let (position, _, _) = Self::add_liquidity_not_insert( - netuid, - &protocol_account_id, - TickIndex::MIN, - TickIndex::MAX, - liquidity, - )?; + // Create balancer based on price + let balancer = Balancer::new(if let Some(price) = maybe_price { + // Price is given, calculate weights: + // w_quote = y / (px + y) + let px_high = (price.saturating_to_num::() as u128) + .saturating_mul(u64::from(alpha_reserve) as u128); + let px_low = U64F64::saturating_from_num(alpha_reserve) + .saturating_mul(price.frac()) + .saturating_to_num::(); + let px_plus_y = px_high + .saturating_add(px_low) + .saturating_add(u64::from(tao_reserve) as u128); + + // If price is given and both reserves are zero, the swap doesn't initialize + if px_plus_y == 0u128 { + return Err(Error::::ReservesOutOfBalance); + } + Perquintill::from_rational(u64::from(tao_reserve) as u128, px_plus_y) + } else { + // No price = insert 0.5 into SwapBalancer + Perquintill::from_rational(1_u64, 2_u64) + }) + .map_err(|err| match err { + BalancerError::InvalidValue => Error::::ReservesOutOfBalance, + })?; + SwapBalancer::::insert(netuid, balancer.clone()); - Positions::::insert(&(netuid, protocol_account_id, position.id), position); + PalSwapInitialized::::insert(netuid, true); Ok(()) } - pub(crate) fn get_proportional_alpha_tao_and_remainders( - sqrt_alpha_price: U64F64, - amount_tao: TaoBalance, - amount_alpha: AlphaBalance, - ) -> (TaoBalance, AlphaBalance, TaoBalance, AlphaBalance) { - let price = sqrt_alpha_price.saturating_mul(sqrt_alpha_price); - let tao_equivalent: u64 = U64F64::saturating_from_num(u64::from(amount_alpha)) - .saturating_mul(price) - .saturating_to_num(); - let amount_tao_u64 = u64::from(amount_tao); - - if tao_equivalent <= amount_tao_u64 { - // Too much or just enough TAO - ( - tao_equivalent.into(), - amount_alpha, - amount_tao.saturating_sub(TaoBalance::from(tao_equivalent)), - 0.into(), - ) - } else { - // Too much Alpha - let alpha_equivalent: u64 = U64F64::saturating_from_num(u64::from(amount_tao)) - .safe_div(price) - .saturating_to_num(); - ( - amount_tao, - alpha_equivalent.into(), - 0.into(), - u64::from(amount_alpha) - .saturating_sub(alpha_equivalent) - .into(), - ) - } - } - - /// Adjusts protocol liquidity with new values of TAO and Alpha reserve + /// Returns actually added Tao and Alpha, which may be zero in case + /// of a high disbalance pub(super) fn adjust_protocol_liquidity( netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance, - ) { - // Update protocol position with new liquidity - let protocol_account_id = Self::protocol_account_id(); - let mut positions = - Positions::::iter_prefix_values((netuid, protocol_account_id.clone())) - .collect::>(); - - if let Some(position) = positions.get_mut(0) { - // Claim protocol fees and add them to liquidity - let (tao_fees, alpha_fees) = position.collect_fees(); - - // Add fee reservoirs and get proportional amounts - let current_sqrt_price = AlphaSqrtPrice::::get(netuid); - let tao_reservoir = ScrapReservoirTao::::get(netuid); - let alpha_reservoir = ScrapReservoirAlpha::::get(netuid); - let (corrected_tao_delta, corrected_alpha_delta, tao_scrap, alpha_scrap) = - Self::get_proportional_alpha_tao_and_remainders( - current_sqrt_price, - tao_delta - .saturating_add(TaoBalance::from(tao_fees)) - .saturating_add(tao_reservoir), - alpha_delta - .saturating_add(AlphaBalance::from(alpha_fees)) - .saturating_add(alpha_reservoir), - ); - - // Update scrap reservoirs - ScrapReservoirTao::::insert(netuid, tao_scrap); - ScrapReservoirAlpha::::insert(netuid, alpha_scrap); - - // Adjust liquidity - let maybe_token_amounts = position.to_token_amounts(current_sqrt_price); - if let Ok((tao, alpha)) = maybe_token_amounts { - // Get updated reserves, calculate liquidity - let new_tao_reserve = tao.saturating_add(corrected_tao_delta.to_u64()); - let new_alpha_reserve = alpha.saturating_add(corrected_alpha_delta.to_u64()); - let new_liquidity = helpers_128bit::sqrt( - (new_tao_reserve as u128).saturating_mul(new_alpha_reserve as u128), - ) as u64; - let liquidity_delta = new_liquidity.saturating_sub(position.liquidity); - - // Update current liquidity - CurrentLiquidity::::mutate(netuid, |current_liquidity| { - *current_liquidity = current_liquidity.saturating_add(liquidity_delta); - }); - - // Update protocol position - position.liquidity = new_liquidity; - Positions::::insert( - (netuid, protocol_account_id, position.id), - position.clone(), - ); - - // Update position ticks - Self::add_liquidity_at_index(netuid, position.tick_low, liquidity_delta, false); - Self::add_liquidity_at_index(netuid, position.tick_high, liquidity_delta, true); - } + ) -> (TaoBalance, AlphaBalance) { + // Get reserves + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let mut balancer = SwapBalancer::::get(netuid); + + // Update weights and log errors if they go out of range + if balancer + .update_weights_for_added_liquidity( + u64::from(tao_reserve), + u64::from(alpha_reserve), + u64::from(tao_delta), + u64::from(alpha_delta), + ) + .is_err() + { + log::warn!( + "Reserves are out of range for emission: netuid = {}, tao = {}, alpha = {}, tao_delta = {}, alpha_delta = {}", + netuid, + tao_reserve, + alpha_reserve, + tao_delta, + alpha_delta + ); + (TaoBalance::ZERO, AlphaBalance::ZERO) + } else { + SwapBalancer::::insert(netuid, balancer); + (tao_delta, alpha_delta) } } @@ -241,7 +143,7 @@ impl Pallet { pub(crate) fn do_swap( netuid: NetUid, order: Order, - limit_sqrt_price: SqrtPrice, + limit_price: U64F64, drop_fees: bool, simulate: bool, ) -> Result, DispatchError> @@ -252,7 +154,7 @@ impl Pallet { transactional::with_transaction(|| { let reserve = Order::ReserveOut::reserve(netuid.into()); - let result = Self::swap_inner::(netuid, order, limit_sqrt_price, drop_fees) + let result = Self::swap_inner::(netuid, order, limit_price, drop_fees) .map_err(Into::into); if simulate || result.is_err() { @@ -278,7 +180,7 @@ impl Pallet { fn swap_inner( netuid: NetUid, order: Order, - limit_sqrt_price: SqrtPrice, + limit_price: U64F64, drop_fees: bool, ) -> Result, Error> where @@ -290,74 +192,37 @@ impl Pallet { Error::::ReservesTooLow ); - Self::maybe_initialize_v3(netuid)?; + Self::maybe_initialize_palswap(netuid, None)?; // Because user specifies the limit price, check that it is in fact beoynd the current one ensure!( - order.is_beyond_price_limit(AlphaSqrtPrice::::get(netuid), limit_sqrt_price), + order.is_beyond_price_limit(Self::current_price(netuid), limit_price), Error::::PriceLimitExceeded ); - let mut amount_remaining = order.amount(); - let mut amount_paid_out = Order::PaidOut::ZERO; - let mut iteration_counter: u16 = 0; - let mut in_acc = Order::PaidIn::ZERO; - let mut fee_acc = Order::PaidIn::ZERO; - let mut fee_to_block_author_acc = Order::PaidIn::ZERO; - log::trace!("======== Start Swap ========"); - log::trace!("Amount Remaining: {amount_remaining}"); - - // Swap one tick at a time until we reach one of the stop conditions - while !amount_remaining.is_zero() { - log::trace!("\nIteration: {iteration_counter}"); - log::trace!( - "\tCurrent Liquidity: {}", - CurrentLiquidity::::get(netuid) - ); - - // Create and execute a swap step - let mut swap_step = BasicSwapStep::::new( - netuid, - amount_remaining, - limit_sqrt_price, - drop_fees, - ); - - let swap_result = swap_step.execute()?; - - in_acc = in_acc.saturating_add(swap_result.delta_in); - fee_acc = fee_acc.saturating_add(swap_result.fee_paid); - fee_to_block_author_acc = - fee_to_block_author_acc.saturating_add(swap_result.fee_to_block_author); - amount_remaining = amount_remaining.saturating_sub(swap_result.amount_to_take); - amount_paid_out = amount_paid_out.saturating_add(swap_result.delta_out); + let amount_to_swap = order.amount(); + log::trace!("Amount to swap: {amount_to_swap}"); - if swap_step.action() == SwapStepAction::Stop { - amount_remaining = Order::PaidIn::ZERO; - } - - // The swap step didn't exchange anything - if swap_result.amount_to_take.is_zero() { - amount_remaining = Order::PaidIn::ZERO; - } + // Create and execute a swap step + let mut swap_step = BasicSwapStep::::new( + netuid, + amount_to_swap, + limit_price, + drop_fees, + ); - iteration_counter = iteration_counter.saturating_add(1); + let swap_result = swap_step.execute()?; - ensure!( - iteration_counter <= MAX_SWAP_ITERATIONS, - Error::::TooManySwapSteps - ); - } - - log::trace!("\nAmount Paid Out: {amount_paid_out}"); + log::trace!("Delta out: {}", swap_result.delta_out); + log::trace!("Fees: {}", swap_result.fee_paid); log::trace!("======== End Swap ========"); Ok(SwapResult { - amount_paid_in: in_acc, - amount_paid_out, - fee_paid: fee_acc, - fee_to_block_author: fee_to_block_author_acc, + amount_paid_in: swap_result.delta_in, + amount_paid_out: swap_result.delta_out, + fee_paid: swap_result.fee_paid, + fee_to_block_author: swap_result.fee_to_block_author, }) } @@ -382,427 +247,6 @@ impl Pallet { } } - pub fn find_closest_lower_active_tick(netuid: NetUid, index: TickIndex) -> Option { - ActiveTickIndexManager::::find_closest_lower(netuid, index) - .and_then(|ti| Ticks::::get(netuid, ti)) - } - - pub fn find_closest_higher_active_tick(netuid: NetUid, index: TickIndex) -> Option { - ActiveTickIndexManager::::find_closest_higher(netuid, index) - .and_then(|ti| Ticks::::get(netuid, ti)) - } - - /// Here we subtract minimum safe liquidity from current liquidity to stay in the safe range - pub(crate) fn current_liquidity_safe(netuid: NetUid) -> U64F64 { - U64F64::saturating_from_num( - CurrentLiquidity::::get(netuid).saturating_sub(T::MinimumLiquidity::get()), - ) - } - - /// Adds liquidity to the specified price range. - /// - /// This function allows an account to provide liquidity to a given range of price ticks. The - /// amount of liquidity to be added can be determined using - /// [`get_tao_based_liquidity`] and [`get_alpha_based_liquidity`], which compute the required - /// liquidity based on TAO and Alpha balances for the current price tick. - /// - /// ### Behavior: - /// - If the `protocol` flag is **not set** (`false`), the function will attempt to - /// **withdraw balances** from the account using `state_ops.withdraw_balances()`. - /// - If the `protocol` flag is **set** (`true`), the liquidity is added without modifying balances. - /// - If swap V3 was not initialized before, updates the value in storage. - /// - /// ### Parameters: - /// - `coldkey_account_id`: A reference to the account coldkey that is providing liquidity. - /// - `hotkey_account_id`: A reference to the account hotkey that is providing liquidity. - /// - `tick_low`: The lower bound of the price tick range. - /// - `tick_high`: The upper bound of the price tick range. - /// - `liquidity`: The amount of liquidity to be added. - /// - /// ### Returns: - /// - `Ok((u64, u64))`: (tao, alpha) amounts at new position - /// - `Err(SwapError)`: If the operation fails due to insufficient balance, invalid tick range, - /// or other swap-related errors. - /// - /// ### Errors: - /// - [`SwapError::InsufficientBalance`] if the account does not have enough balance. - /// - [`SwapError::InvalidTickRange`] if `tick_low` is greater than or equal to `tick_high`. - /// - Other [`SwapError`] variants as applicable. - pub fn do_add_liquidity( - netuid: NetUid, - coldkey_account_id: &T::AccountId, - hotkey_account_id: &T::AccountId, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: u64, - ) -> Result<(PositionId, u64, u64), Error> { - ensure!( - EnabledUserLiquidity::::get(netuid), - Error::::UserLiquidityDisabled - ); - - let (position, tao, alpha) = Self::add_liquidity_not_insert( - netuid, - coldkey_account_id, - tick_low, - tick_high, - liquidity, - )?; - let position_id = position.id; - - ensure!( - T::BalanceOps::tao_balance(coldkey_account_id) >= TaoBalance::from(tao) - && T::BalanceOps::alpha_balance( - netuid.into(), - coldkey_account_id, - hotkey_account_id - ) >= AlphaBalance::from(alpha), - Error::::InsufficientBalance - ); - - // Small delta is not allowed - ensure!( - liquidity >= T::MinimumLiquidity::get(), - Error::::InvalidLiquidityValue - ); - - Positions::::insert(&(netuid, coldkey_account_id, position.id), position); - - Ok((position_id, tao, alpha)) - } - - // add liquidity without inserting position into storage (used privately for v3 intiialization). - // unlike Self::add_liquidity it also doesn't perform account's balance check. - // - // the public interface is [`Self::add_liquidity`] - fn add_liquidity_not_insert( - netuid: NetUid, - coldkey_account_id: &T::AccountId, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: u64, - ) -> Result<(Position, u64, u64), Error> { - ensure!( - Self::count_positions(netuid, coldkey_account_id) < T::MaxPositions::get() as usize, - Error::::MaxPositionsExceeded - ); - - // Ensure that tick_high is actually higher than tick_low - ensure!(tick_high > tick_low, Error::::InvalidTickRange); - - // Add liquidity at tick - Self::add_liquidity_at_index(netuid, tick_low, liquidity, false); - Self::add_liquidity_at_index(netuid, tick_high, liquidity, true); - - // Update current tick liquidity - let current_tick_index = TickIndex::current_bounded::(netuid); - Self::clamp_sqrt_price(netuid, current_tick_index); - - Self::update_liquidity_if_needed(netuid, tick_low, tick_high, liquidity as i128); - - // New position - let position_id = PositionId::new::(); - let position = Position::new(position_id, netuid, tick_low, tick_high, liquidity); - - let current_price_sqrt = AlphaSqrtPrice::::get(netuid); - let (tao, alpha) = position.to_token_amounts(current_price_sqrt)?; - - SwapV3Initialized::::set(netuid, true); - - Ok((position, tao, alpha)) - } - - /// Remove liquidity and credit balances back to (coldkey_account_id, hotkey_account_id) stake. - /// Removing is allowed even when user liquidity is enabled. - /// - /// Account ID and Position ID identify position in the storage map - pub fn do_remove_liquidity( - netuid: NetUid, - coldkey_account_id: &T::AccountId, - position_id: PositionId, - ) -> Result> { - let Some(mut position) = Positions::::get((netuid, coldkey_account_id, position_id)) - else { - return Err(Error::::LiquidityNotFound); - }; - - // Collect fees and get tao and alpha amounts - let (fee_tao, fee_alpha) = position.collect_fees(); - let current_price = AlphaSqrtPrice::::get(netuid); - let (tao, alpha) = position.to_token_amounts(current_price)?; - - // Update liquidity at position ticks - Self::remove_liquidity_at_index(netuid, position.tick_low, position.liquidity, false); - Self::remove_liquidity_at_index(netuid, position.tick_high, position.liquidity, true); - - // Update current tick liquidity - Self::update_liquidity_if_needed( - netuid, - position.tick_low, - position.tick_high, - (position.liquidity as i128).neg(), - ); - - // Remove user position - Positions::::remove((netuid, coldkey_account_id, position_id)); - - Ok(RemoveLiquidityResult { - tao: tao.into(), - alpha: alpha.into(), - fee_tao: fee_tao.into(), - fee_alpha: fee_alpha.into(), - tick_low: position.tick_low, - tick_high: position.tick_high, - liquidity: position.liquidity, - }) - } - - pub fn do_modify_position( - netuid: NetUid, - coldkey_account_id: &T::AccountId, - hotkey_account_id: &T::AccountId, - position_id: PositionId, - liquidity_delta: i64, - ) -> Result> { - ensure!( - EnabledUserLiquidity::::get(netuid), - Error::::UserLiquidityDisabled - ); - - // Find the position - let Some(mut position) = Positions::::get((netuid, coldkey_account_id, position_id)) - else { - return Err(Error::::LiquidityNotFound); - }; - - // Small delta is not allowed - ensure!( - liquidity_delta.unsigned_abs() >= T::MinimumLiquidity::get(), - Error::::InvalidLiquidityValue - ); - let mut delta_liquidity_abs = liquidity_delta.unsigned_abs(); - - // Determine the effective price for token calculations - let current_price_sqrt = AlphaSqrtPrice::::get(netuid); - let sqrt_pa: SqrtPrice = position - .tick_low - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let sqrt_pb: SqrtPrice = position - .tick_high - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let sqrt_price_box = if current_price_sqrt < sqrt_pa { - sqrt_pa - } else if current_price_sqrt > sqrt_pb { - sqrt_pb - } else { - // Update current liquidity if price is in range - let new_liquidity_curr = if liquidity_delta > 0 { - CurrentLiquidity::::get(netuid).saturating_add(delta_liquidity_abs) - } else { - CurrentLiquidity::::get(netuid).saturating_sub(delta_liquidity_abs) - }; - CurrentLiquidity::::set(netuid, new_liquidity_curr); - current_price_sqrt - }; - - // Calculate token amounts for the liquidity change - let mul = SqrtPrice::from_num(1) - .safe_div(sqrt_price_box) - .saturating_sub(SqrtPrice::from_num(1).safe_div(sqrt_pb)); - let alpha = SqrtPrice::saturating_from_num(delta_liquidity_abs).saturating_mul(mul); - let tao = SqrtPrice::saturating_from_num(delta_liquidity_abs) - .saturating_mul(sqrt_price_box.saturating_sub(sqrt_pa)); - - // Validate delta - if liquidity_delta > 0 { - // Check that user has enough balances - ensure!( - T::BalanceOps::tao_balance(coldkey_account_id) - >= TaoBalance::from(tao.saturating_to_num::()) - && T::BalanceOps::alpha_balance(netuid, coldkey_account_id, hotkey_account_id) - >= AlphaBalance::from(alpha.saturating_to_num::()), - Error::::InsufficientBalance - ); - } else { - // Check that position has enough liquidity - ensure!( - position.liquidity >= delta_liquidity_abs, - Error::::InsufficientLiquidity - ); - } - - // Collect fees - let (fee_tao, fee_alpha) = position.collect_fees(); - - // If delta brings the position liquidity below MinimumLiquidity, eliminate position and - // withdraw full amounts - let mut remove = false; - if (liquidity_delta < 0) - && (position.liquidity.saturating_sub(delta_liquidity_abs) < T::MinimumLiquidity::get()) - { - delta_liquidity_abs = position.liquidity; - remove = true; - } - - // Adjust liquidity at the ticks based on the delta sign - if liquidity_delta > 0 { - // Add liquidity at tick - Self::add_liquidity_at_index(netuid, position.tick_low, delta_liquidity_abs, false); - Self::add_liquidity_at_index(netuid, position.tick_high, delta_liquidity_abs, true); - - // Add liquidity to user position - position.liquidity = position.liquidity.saturating_add(delta_liquidity_abs); - } else { - // Remove liquidity at tick - Self::remove_liquidity_at_index(netuid, position.tick_low, delta_liquidity_abs, false); - Self::remove_liquidity_at_index(netuid, position.tick_high, delta_liquidity_abs, true); - - // Remove liquidity from user position - position.liquidity = position.liquidity.saturating_sub(delta_liquidity_abs); - } - - // Update or, in case if full liquidity is removed, remove the position - if remove { - Positions::::remove((netuid, coldkey_account_id, position_id)); - } else { - Positions::::insert(&(netuid, coldkey_account_id, position.id), position.clone()); - } - - Ok(UpdateLiquidityResult { - tao: tao.saturating_to_num::().into(), - alpha: alpha.saturating_to_num::().into(), - fee_tao: fee_tao.into(), - fee_alpha: fee_alpha.into(), - removed: remove, - tick_low: position.tick_low, - tick_high: position.tick_high, - }) - } - - /// Adds or updates liquidity at a specific tick index for a subnet - /// - /// # Arguments - /// * `netuid` - The subnet ID - /// * `tick_index` - The tick index to add liquidity to - /// * `liquidity` - The amount of liquidity to add - fn add_liquidity_at_index(netuid: NetUid, tick_index: TickIndex, liquidity: u64, upper: bool) { - // Convert liquidity to signed value, negating it for upper bounds - let net_liquidity_change = if upper { - (liquidity as i128).neg() - } else { - liquidity as i128 - }; - - Ticks::::mutate(netuid, tick_index, |maybe_tick| match maybe_tick { - Some(tick) => { - tick.liquidity_net = tick.liquidity_net.saturating_add(net_liquidity_change); - tick.liquidity_gross = tick.liquidity_gross.saturating_add(liquidity); - } - None => { - let current_tick = TickIndex::current_bounded::(netuid); - - let (fees_out_tao, fees_out_alpha) = if tick_index > current_tick { - ( - I64F64::saturating_from_num(FeeGlobalTao::::get(netuid)), - I64F64::saturating_from_num(FeeGlobalAlpha::::get(netuid)), - ) - } else { - ( - I64F64::saturating_from_num(0), - I64F64::saturating_from_num(0), - ) - }; - *maybe_tick = Some(Tick { - liquidity_net: net_liquidity_change, - liquidity_gross: liquidity, - fees_out_tao, - fees_out_alpha, - }); - } - }); - - // Update active ticks - ActiveTickIndexManager::::insert(netuid, tick_index); - } - - /// Remove liquidity at tick index. - fn remove_liquidity_at_index( - netuid: NetUid, - tick_index: TickIndex, - liquidity: u64, - upper: bool, - ) { - // Calculate net liquidity addition - let net_reduction = if upper { - (liquidity as i128).neg() - } else { - liquidity as i128 - }; - - Ticks::::mutate_exists(netuid, tick_index, |maybe_tick| { - if let Some(tick) = maybe_tick { - tick.liquidity_net = tick.liquidity_net.saturating_sub(net_reduction); - tick.liquidity_gross = tick.liquidity_gross.saturating_sub(liquidity); - - // If no liquidity is left at the tick, remove it - if tick.liquidity_gross == 0 { - *maybe_tick = None; - - // Update active ticks: Final liquidity is zero, remove this tick from active. - ActiveTickIndexManager::::remove(netuid, tick_index); - } - } - }); - } - - /// Updates the current liquidity for a subnet if the current tick index is within the specified - /// range - /// - /// This function handles both increasing and decreasing liquidity based on the sign of the - /// liquidity parameter. It uses i128 to safely handle values up to u64::MAX in both positive - /// and negative directions. - fn update_liquidity_if_needed( - netuid: NetUid, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: i128, - ) { - let current_tick_index = TickIndex::current_bounded::(netuid); - if (tick_low <= current_tick_index) && (current_tick_index < tick_high) { - CurrentLiquidity::::mutate(netuid, |current_liquidity| { - let is_neg = liquidity.is_negative(); - let liquidity = liquidity.abs().min(u64::MAX as i128) as u64; - if is_neg { - *current_liquidity = current_liquidity.saturating_sub(liquidity); - } else { - *current_liquidity = current_liquidity.saturating_add(liquidity); - } - }); - } - } - - /// Clamps the subnet's sqrt price when tick index is outside of valid bounds - fn clamp_sqrt_price(netuid: NetUid, tick_index: TickIndex) { - if tick_index >= TickIndex::MAX || tick_index <= TickIndex::MIN { - let corrected_price = tick_index.as_sqrt_price_bounded(); - AlphaSqrtPrice::::set(netuid, corrected_price); - } - } - - /// Returns the number of positions for an account in a specific subnet - /// - /// # Arguments - /// * `netuid` - The subnet ID - /// * `account_id` - The account ID - /// - /// # Returns - /// The number of positions that the account has in the specified subnet - pub(super) fn count_positions(netuid: NetUid, account_id: &T::AccountId) -> usize { - Positions::::iter_prefix_values((netuid, account_id.clone())).count() - } - /// Returns the protocol account ID /// /// # Returns @@ -812,94 +256,26 @@ impl Pallet { } pub(crate) fn min_price_inner() -> C { - TickIndex::min_sqrt_price() - .saturating_mul(TickIndex::min_sqrt_price()) - .saturating_mul(SqrtPrice::saturating_from_num(1_000_000_000)) - .saturating_to_num::() - .into() + u64::from(1_000_u64).into() } pub(crate) fn max_price_inner() -> C { - TickIndex::max_sqrt_price() - .saturating_mul(TickIndex::max_sqrt_price()) - .saturating_mul(SqrtPrice::saturating_from_num(1_000_000_000)) - .saturating_round() - .saturating_to_num::() - .into() - } - - /// Dissolve all LPs and clean state. - pub fn do_dissolve_all_liquidity_providers(_netuid: NetUid) -> DispatchResultWithPostInfo { - // Deprecated in balancer, also we do not have any active liquidity providers - // or any ways to provide liquidity. - Ok(Some(Weight::default()).into()) + u64::from(1_000_000_000_000_000_u64).into() } /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { - let protocol_account = Self::protocol_account_id(); - - // 1) Force-close only protocol positions, burning proceeds. - let mut burned_tao = TaoBalance::ZERO; - let mut burned_alpha = AlphaBalance::ZERO; + // 1) Force-close protocol liquidity, burning proceeds. + let burned_tao = T::TaoReserve::reserve(netuid.into()); + let burned_alpha = T::AlphaReserve::reserve(netuid.into()); - // Collect protocol position IDs first to avoid mutating while iterating. - let protocol_pos_ids: sp_std::vec::Vec = Positions::::iter_prefix((netuid,)) - .filter_map(|((owner, pos_id), _)| { - if owner == protocol_account { - Some(pos_id) - } else { - None - } - }) - .collect(); + T::TaoReserve::decrease_provided(netuid.into(), burned_tao); + T::AlphaReserve::decrease_provided(netuid.into(), burned_alpha); - for pos_id in protocol_pos_ids { - match Self::do_remove_liquidity(netuid, &protocol_account, pos_id) { - Ok(rm) => { - let alpha_total_from_pool: AlphaBalance = rm.alpha.saturating_add(rm.fee_alpha); - let tao_total_from_pool: TaoBalance = rm.tao.saturating_add(rm.fee_tao); + PalSwapInitialized::::remove(netuid); - if tao_total_from_pool > TaoBalance::ZERO { - burned_tao = burned_tao.saturating_add(tao_total_from_pool); - } - if alpha_total_from_pool > AlphaBalance::ZERO { - burned_alpha = burned_alpha.saturating_add(alpha_total_from_pool); - } - - log::debug!( - "clear_protocol_liquidity: burned protocol pos: netuid={netuid:?}, pos_id={pos_id:?}, τ={tao_total_from_pool:?}, α_total={alpha_total_from_pool:?}" - ); - } - Err(e) => { - log::debug!( - "clear_protocol_liquidity: force-close failed: netuid={netuid:?}, pos_id={pos_id:?}, err={e:?}" - ); - continue; - } - } - } - - // 2) Clear active tick index entries, then all swap state (idempotent even if empty/non‑V3). - let active_ticks: sp_std::vec::Vec = - Ticks::::iter_prefix(netuid).map(|(ti, _)| ti).collect(); - for ti in active_ticks { - ActiveTickIndexManager::::remove(netuid, ti); - } - - let _ = Positions::::clear_prefix((netuid,), u32::MAX, None); - let _ = Ticks::::clear_prefix(netuid, u32::MAX, None); - - FeeGlobalTao::::remove(netuid); - FeeGlobalAlpha::::remove(netuid); - CurrentLiquidity::::remove(netuid); - CurrentTick::::remove(netuid); - AlphaSqrtPrice::::remove(netuid); - SwapV3Initialized::::remove(netuid); - - let _ = TickIndexBitmapWords::::clear_prefix((netuid,), u32::MAX, None); FeeRate::::remove(netuid); - EnabledUserLiquidity::::remove(netuid); + SwapBalancer::::remove(netuid); log::debug!( "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" @@ -935,15 +311,13 @@ where drop_fees: bool, should_rollback: bool, ) -> Result, DispatchError> { - let limit_sqrt_price = SqrtPrice::saturating_from_num(price_limit.to_u64()) - .safe_div(SqrtPrice::saturating_from_num(1_000_000_000)) - .checked_sqrt(SqrtPrice::saturating_from_num(0.0000000001)) - .ok_or(Error::::PriceLimitExceeded)?; + let limit_price = U64F64::saturating_from_num(price_limit.to_u64()) + .safe_div(U64F64::saturating_from_num(1_000_000_000_u64)); Self::do_swap::( NetUid::from(netuid), order, - limit_sqrt_price, + limit_price, drop_fees, should_rollback, ) @@ -1000,28 +374,10 @@ impl SwapHandler for Pallet { Self::calculate_fee_amount(netuid, amount, false) } - fn current_alpha_price(netuid: NetUid) -> U96F32 { + fn current_alpha_price(netuid: NetUid) -> U64F64 { Self::current_price(netuid.into()) } - fn get_protocol_tao(netuid: NetUid) -> TaoBalance { - let protocol_account_id = Self::protocol_account_id(); - let mut positions = - Positions::::iter_prefix_values((netuid, protocol_account_id.clone())) - .collect::>(); - - if let Some(position) = positions.get_mut(0) { - let current_sqrt_price = AlphaSqrtPrice::::get(netuid); - // Adjust liquidity - let maybe_token_amounts = position.to_token_amounts(current_sqrt_price); - if let Ok((tao, _)) = maybe_token_amounts { - return tao.into(); - } - } - - TaoBalance::ZERO - } - fn min_price() -> C { Self::min_price_inner() } @@ -1030,23 +386,22 @@ impl SwapHandler for Pallet { Self::max_price_inner() } - fn adjust_protocol_liquidity(netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance) { - Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + fn adjust_protocol_liquidity( + netuid: NetUid, + tao_delta: TaoBalance, + alpha_delta: AlphaBalance, + ) -> (TaoBalance, AlphaBalance) { + Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta) } - fn is_user_liquidity_enabled(netuid: NetUid) -> bool { - EnabledUserLiquidity::::get(netuid) - } - fn dissolve_all_liquidity_providers(netuid: NetUid) -> DispatchResultWithPostInfo { - Self::do_dissolve_all_liquidity_providers(netuid) - } - fn toggle_user_liquidity(netuid: NetUid, enabled: bool) { - EnabledUserLiquidity::::insert(netuid, enabled) - } fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { Self::do_clear_protocol_liquidity(netuid) } + fn init_swap(netuid: NetUid, maybe_price: Option) { + Self::maybe_initialize_palswap(netuid, maybe_price).unwrap_or_default(); + } + /// 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()) { @@ -1055,7 +410,7 @@ impl SwapHandler for Pallet { // 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)) + U64F64::from(u64::from(tao_amount)) .safe_div(alpha_price) .saturating_to_num::(), ) diff --git a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs index 63392da9ea..2f06d88a00 100644 --- a/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs +++ b/pallets/swap/src/pallet/migrations/migrate_swapv3_to_balancer.rs @@ -44,7 +44,17 @@ pub fn migrate_swapv3_to_balancer() -> Weight { // ------------------------------ for (netuid, price_sqrt) in deprecated_swap_maps::AlphaSqrtPrice::::iter() { let price = price_sqrt.saturating_mul(price_sqrt); - crate::Pallet::::maybe_initialize_palswap(netuid, Some(price)).unwrap_or_default(); + if let Err(error) = crate::Pallet::::maybe_initialize_palswap(netuid, Some(price)) { + log::warn!( + "Migration '{}' failed to initialize balancer with V3 price for netuid {}: {:?}. Falling back to default balancer.", + String::from_utf8_lossy(&migration_name), + netuid, + error, + ); + SwapBalancer::::insert(netuid, Balancer::default()); + PalSwapInitialized::::insert(netuid, true); + weight = weight.saturating_add(T::DbWeight::get().writes(2)); + } } // ------------------------------ diff --git a/pallets/swap/src/pallet/migrations/mod.rs b/pallets/swap/src/pallet/migrations/mod.rs new file mode 100644 index 0000000000..d34626f05e --- /dev/null +++ b/pallets/swap/src/pallet/migrations/mod.rs @@ -0,0 +1,25 @@ +use super::*; +use frame_support::pallet_prelude::Weight; +use sp_io::KillStorageResult; +use sp_io::hashing::twox_128; +use sp_io::storage::clear_prefix; +use sp_std::vec::Vec; + +pub mod migrate_swapv3_to_balancer; + +pub(crate) fn remove_prefix(module: &str, old_map: &str, weight: &mut Weight) { + let mut prefix = Vec::new(); + prefix.extend_from_slice(&twox_128(module.as_bytes())); + prefix.extend_from_slice(&twox_128(old_map.as_bytes())); + + let removal_results = clear_prefix(&prefix, Some(u32::MAX)); + let removed_entries_count = match removal_results { + KillStorageResult::AllRemoved(removed) => removed as u64, + KillStorageResult::SomeRemaining(removed) => { + log::info!("Failed To Remove Some Items During migration"); + removed as u64 + } + }; + + *weight = (*weight).saturating_add(T::DbWeight::get().writes(removed_entries_count)); +} diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 763d2150b2..1d2fd07c59 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -2,31 +2,31 @@ use core::num::NonZeroU64; use frame_support::{PalletId, pallet_prelude::*, traits::Get}; use frame_system::pallet_prelude::*; -use sp_arithmetic::Perbill; -use substrate_fixed::types::U64F64; use subtensor_runtime_common::{ AlphaBalance, BalanceOps, NetUid, SubnetInfo, TaoBalance, TokenReserve, }; -use crate::{ - position::{Position, PositionId}, - tick::{LayerLevel, Tick, TickIndex}, - weights::WeightInfo, -}; - +use crate::{pallet::balancer::Balancer, weights::WeightInfo}; pub use pallet::*; +use subtensor_macros::freeze_struct; +mod balancer; +mod hooks; mod impls; +pub mod migrations; mod swap_step; #[cfg(test)] mod tests; +// Define a maximum length for the migration key +type MigrationKeyMaxLen = ConstU32<128>; + #[allow(clippy::module_inception)] #[frame_support::pallet] #[allow(clippy::expect_used)] mod pallet { use super::*; - use frame_system::{ensure_root, ensure_signed}; + use frame_system::ensure_root; #[pallet::pallet] pub struct Pallet(_); @@ -56,10 +56,6 @@ mod pallet { #[pallet::constant] type MaxFeeRate: Get; - /// The maximum number of positions a user can have - #[pallet::constant] - type MaxPositions: Get; - /// Minimum liquidity that is safe for rounding and integer math. #[pallet::constant] type MinimumLiquidity: Get; @@ -95,85 +91,32 @@ mod pallet { 33 // ~0.05 % } - /// Fee split between pool and block builder. - /// Pool receives the portion returned by this function - #[pallet::type_value] - pub fn DefaultFeeSplit() -> Perbill { - Perbill::zero() - } - /// The fee rate applied to swaps per subnet, normalized value between 0 and u16::MAX #[pallet::storage] pub type FeeRate = StorageMap<_, Twox64Concat, NetUid, u16, ValueQuery, DefaultFeeRate>; - // Global accrued fees in tao per subnet - #[pallet::storage] - pub type FeeGlobalTao = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - - // Global accrued fees in alpha per subnet - #[pallet::storage] - pub type FeeGlobalAlpha = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - - /// Storage for all ticks, using subnet ID as the primary key and tick index as the secondary key - #[pallet::storage] - pub type Ticks = StorageDoubleMap<_, Twox64Concat, NetUid, Twox64Concat, TickIndex, Tick>; - - /// Storage to determine whether swap V3 was initialized for a specific subnet. - #[pallet::storage] - pub type SwapV3Initialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; + //////////////////////////////////////////////////// + // Balancer (PalSwap) maps and variables - /// Storage for the square root price of Alpha token for each subnet. - #[pallet::storage] - pub type AlphaSqrtPrice = StorageMap<_, Twox64Concat, NetUid, U64F64, ValueQuery>; - - /// Storage for the current price tick. - #[pallet::storage] - pub type CurrentTick = StorageMap<_, Twox64Concat, NetUid, TickIndex, ValueQuery>; - - /// Storage for the current liquidity amount for each subnet. - #[pallet::storage] - pub type CurrentLiquidity = StorageMap<_, Twox64Concat, NetUid, u64, ValueQuery>; + /// Default reserve weight + #[pallet::type_value] + pub fn DefaultBalancer() -> Balancer { + Balancer::default() + } - /// Indicates whether a subnet has been switched to V3 swap from V2. - /// If `true`, the subnet is permanently on V3 swap mode allowing add/remove liquidity - /// operations. Once set to `true` for a subnet, it cannot be changed back to `false`. + /// u64-normalized reserve weight #[pallet::storage] - pub type EnabledUserLiquidity = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; + pub type SwapBalancer = + StorageMap<_, Twox64Concat, NetUid, Balancer, ValueQuery, DefaultBalancer>; - /// Storage for user positions, using subnet ID and account ID as keys - /// The value is a bounded vector of Position structs with details about the liquidity positions + /// Storage to determine whether balancer swap was initialized for a specific subnet. #[pallet::storage] - pub type Positions = StorageNMap< - _, - ( - NMapKey, // Subnet ID - NMapKey, // Account ID - NMapKey, // Position ID - ), - Position, - OptionQuery, - >; - - /// Position ID counter. - #[pallet::storage] - pub type LastPositionId = StorageValue<_, u128, ValueQuery>; + pub type PalSwapInitialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; - /// Tick index bitmap words storage - #[pallet::storage] - pub type TickIndexBitmapWords = StorageNMap< - _, - ( - NMapKey, // Subnet ID - NMapKey, // Layer level - NMapKey, // word index - ), - u128, - ValueQuery, - >; - - /// TAO reservoir for scraps of protocol claimed fees. + /// --- Storage for migration run status #[pallet::storage] - pub type ScrapReservoirTao = StorageMap<_, Twox64Concat, NetUid, TaoBalance, ValueQuery>; + pub type HasMigrationRun = + StorageMap<_, Identity, BoundedVec, bool, ValueQuery>; /// Alpha reservoir for scraps of protocol claimed fees. #[pallet::storage] @@ -184,85 +127,6 @@ mod pallet { pub enum Event { /// Event emitted when the fee rate has been updated for a subnet FeeRateSet { netuid: NetUid, rate: u16 }, - - /// Event emitted when user liquidity operations are enabled for a subnet. - /// First enable even indicates a switch from V2 to V3 swap. - UserLiquidityToggled { netuid: NetUid, enable: bool }, - - /// Event emitted when a liquidity position is added to a subnet's liquidity pool. - LiquidityAdded { - /// The coldkey account that owns the position - coldkey: T::AccountId, - /// The hotkey account where Alpha comes from - hotkey: T::AccountId, - /// The subnet identifier - netuid: NetUid, - /// Unique identifier for the liquidity position - position_id: PositionId, - /// The amount of liquidity added to the position - liquidity: u64, - /// The amount of TAO tokens committed to the position - tao: TaoBalance, - /// The amount of Alpha tokens committed to the position - alpha: AlphaBalance, - /// the lower tick - tick_low: TickIndex, - /// the upper tick - tick_high: TickIndex, - }, - - /// Event emitted when a liquidity position is removed from a subnet's liquidity pool. - LiquidityRemoved { - /// The coldkey account that owns the position - coldkey: T::AccountId, - /// The hotkey account where Alpha goes to - hotkey: T::AccountId, - /// The subnet identifier - netuid: NetUid, - /// Unique identifier for the liquidity position - position_id: PositionId, - /// The amount of liquidity removed from the position - liquidity: u64, - /// The amount of TAO tokens returned to the user - tao: TaoBalance, - /// The amount of Alpha tokens returned to the user - alpha: AlphaBalance, - /// The amount of TAO fees earned from the position - fee_tao: TaoBalance, - /// The amount of Alpha fees earned from the position - fee_alpha: AlphaBalance, - /// the lower tick - tick_low: TickIndex, - /// the upper tick - tick_high: TickIndex, - }, - - /// Event emitted when a liquidity position is modified in a subnet's liquidity pool. - /// Modifying causes the fees to be claimed. - LiquidityModified { - /// The coldkey account that owns the position - coldkey: T::AccountId, - /// The hotkey account where Alpha comes from or goes to - hotkey: T::AccountId, - /// The subnet identifier - netuid: NetUid, - /// Unique identifier for the liquidity position - position_id: PositionId, - /// The amount of liquidity added to or removed from the position - liquidity: i64, - /// The amount of TAO tokens returned to the user - tao: i64, - /// The amount of Alpha tokens returned to the user - alpha: i64, - /// The amount of TAO fees earned from the position - fee_tao: TaoBalance, - /// The amount of Alpha fees earned from the position - fee_alpha: AlphaBalance, - /// the lower tick - tick_low: TickIndex, - /// the upper tick - tick_high: TickIndex, - }, } #[pallet::error] @@ -283,18 +147,9 @@ mod pallet { /// The caller does not have enough balance for the operation. InsufficientBalance, - /// Attempted to remove liquidity that does not exist. - LiquidityNotFound, - /// The provided tick range is invalid. InvalidTickRange, - /// Maximum user positions exceeded - MaxPositionsExceeded, - - /// Too many swap steps - TooManySwapSteps, - /// Provided liquidity parameter is invalid (likely too small) InvalidLiquidityValue, @@ -304,11 +159,14 @@ mod pallet { /// The subnet does not exist. MechanismDoesNotExist, - /// User liquidity operations are disabled for this subnet - UserLiquidityDisabled, - /// The subnet does not have subtoken enabled SubtokenDisabled, + + /// Swap reserves are too imbalanced + ReservesOutOfBalance, + + /// The extrinsic is deprecated + Deprecated, } #[pallet::call] @@ -339,149 +197,47 @@ mod pallet { Ok(()) } - /// Enable user liquidity operations for a specific subnet. This switches the - /// subnet from V2 to V3 swap mode. Thereafter, adding new user liquidity can be disabled - /// by toggling this flag to false, but the swap mode will remain V3 because of existing - /// user liquidity until all users withdraw their liquidity. - /// - /// Only sudo or subnet owner can enable user liquidity. - /// Only sudo can disable user liquidity. + /// DEPRECATED #[pallet::call_index(4)] - #[pallet::weight(::WeightInfo::toggle_user_liquidity())] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] pub fn toggle_user_liquidity( - origin: OriginFor, - netuid: NetUid, - enable: bool, + _origin: OriginFor, + _netuid: NetUid, + _enable: bool, ) -> DispatchResult { - if ensure_root(origin.clone()).is_err() { - let account_id: T::AccountId = ensure_signed(origin)?; - // Only enabling is allowed to subnet owner - ensure!( - T::SubnetInfo::is_owner(&account_id, netuid.into()) && enable, - DispatchError::BadOrigin - ); - } - - ensure!( - T::SubnetInfo::exists(netuid.into()), - Error::::MechanismDoesNotExist - ); - - // EnabledUserLiquidity::::insert(netuid, enable); - - // Self::deposit_event(Event::UserLiquidityToggled { netuid, enable }); - - Ok(()) + Err(Error::::Deprecated.into()) } - /// Add liquidity to a specific price range for a subnet. - /// - /// Parameters: - /// - origin: The origin of the transaction - /// - netuid: Subnet ID - /// - tick_low: Lower bound of the price range - /// - tick_high: Upper bound of the price range - /// - liquidity: Amount of liquidity to add - /// - /// Emits `Event::LiquidityAdded` on success + /// DEPRECATED #[pallet::call_index(1)] - #[pallet::weight(::WeightInfo::add_liquidity())] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] pub fn add_liquidity( - origin: OriginFor, + _origin: OriginFor, _hotkey: T::AccountId, _netuid: NetUid, _tick_low: TickIndex, _tick_high: TickIndex, _liquidity: u64, ) -> DispatchResult { - ensure_signed(origin)?; - - // Extrinsic should have no effect. This fix may have to be reverted later, - // so leaving the code in for now. - - // // Ensure that the subnet exists. - // ensure!( - // T::SubnetInfo::exists(netuid.into()), - // Error::::MechanismDoesNotExist - // ); - - // ensure!( - // T::SubnetInfo::is_subtoken_enabled(netuid.into()), - // Error::::SubtokenDisabled - // ); - - // let (position_id, tao, alpha) = Self::do_add_liquidity( - // netuid.into(), - // &coldkey, - // &hotkey, - // tick_low, - // tick_high, - // liquidity, - // )?; - // let alpha = AlphaBalance::from(alpha); - // let tao = TaoBalance::from(tao); - - // // Remove TAO and Alpha balances or fail transaction if they can't be removed exactly - // let tao_provided = T::BalanceOps::decrease_balance(&coldkey, tao)?; - // ensure!(tao_provided == tao, Error::::InsufficientBalance); - - // let alpha_provided = - // T::BalanceOps::decrease_stake(&coldkey, &hotkey, netuid.into(), alpha)?; - // ensure!(alpha_provided == alpha, Error::::InsufficientBalance); - - // // Add provided liquidity to user-provided reserves - // T::TaoReserve::increase_provided(netuid.into(), tao_provided); - // T::AlphaReserve::increase_provided(netuid.into(), alpha_provided); - - // // Emit an event - // Self::deposit_event(Event::LiquidityAdded { - // coldkey, - // hotkey, - // netuid, - // position_id, - // liquidity, - // tao, - // alpha, - // tick_low, - // tick_high, - // }); - - // Ok(()) - - Err(Error::::UserLiquidityDisabled.into()) + Err(Error::::Deprecated.into()) } - /// Remove liquidity from a specific position. - /// - /// Parameters: - /// - origin: The origin of the transaction - /// - netuid: Subnet ID - /// - position_id: ID of the position to remove - /// - /// Emits `Event::LiquidityRemoved` on success + /// DEPRECATED #[pallet::call_index(2)] - #[pallet::weight(::WeightInfo::remove_liquidity())] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] pub fn remove_liquidity( _origin: OriginFor, _hotkey: T::AccountId, _netuid: NetUid, _position_id: PositionId, ) -> DispatchResult { - // Deprecated by balancer. We don't have any active liquidity providers either. - Ok(()) + Err(Error::::Deprecated.into()) } - /// Modify a liquidity position. - /// - /// Parameters: - /// - origin: The origin of the transaction - /// - netuid: Subnet ID - /// - position_id: ID of the position to remove - /// - liquidity_delta: Liquidity to add (if positive) or remove (if negative) - /// - /// Emits `Event::LiquidityRemoved` on success + /// DEPRECATED #[pallet::call_index(3)] - #[pallet::weight(::WeightInfo::modify_position())] + #[pallet::weight(Weight::from_parts(15_000_000, 0))] + #[deprecated(note = "Deprecated, user liquidity is permanently disabled")] pub fn modify_position( _origin: OriginFor, _hotkey: T::AccountId, @@ -489,35 +245,52 @@ mod pallet { _position_id: PositionId, _liquidity_delta: i64, ) -> DispatchResult { - // Deprecated by balancer. We don't have any active liquidity providers either. - Ok(()) + Err(Error::::Deprecated.into()) } - /// Disable user liquidity in all subnets. - /// - /// Emits `Event::UserLiquidityToggled` on success + /// DEPRECATED #[pallet::call_index(5)] - #[pallet::weight(::WeightInfo::disable_lp())] - pub fn disable_lp(origin: OriginFor) -> DispatchResult { - ensure_root(origin)?; - - for netuid in 1..=128 { - let netuid = NetUid::from(netuid as u16); - if EnabledUserLiquidity::::get(netuid) { - EnabledUserLiquidity::::insert(netuid, false); - Self::deposit_event(Event::UserLiquidityToggled { - netuid, - enable: false, - }); - } - - // Remove provided liquidity unconditionally because the network may have - // user liquidity previously disabled - // Ignore result to avoid early stopping - let _ = Self::do_dissolve_all_liquidity_providers(netuid); - } - - Ok(()) + #[pallet::weight(Weight::from_parts(15_000_000, 0))] + #[deprecated(note = "Deprecated, user liquidity is permanently disabled")] + pub fn disable_lp(_origin: OriginFor) -> DispatchResult { + Err(Error::::Deprecated.into()) } } } + +/// Struct representing a tick index, DEPRECATED +#[freeze_struct("7c280c2b3bbbb33e")] +#[derive( + Debug, + Default, + Clone, + Copy, + Decode, + Encode, + DecodeWithMemTracking, + TypeInfo, + MaxEncodedLen, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, +)] +pub struct TickIndex(i32); + +/// Struct representing a liquidity position ID, DEPRECATED +#[freeze_struct("e695cd6455c3f0cb")] +#[derive( + Clone, + Copy, + Decode, + DecodeWithMemTracking, + Default, + Encode, + Eq, + MaxEncodedLen, + PartialEq, + RuntimeDebug, + TypeInfo, +)] +pub struct PositionId(u128); diff --git a/pallets/swap/src/pallet/swap_step.rs b/pallets/swap/src/pallet/swap_step.rs index bdcad40074..7f10bff65a 100644 --- a/pallets/swap/src/pallet/swap_step.rs +++ b/pallets/swap/src/pallet/swap_step.rs @@ -1,15 +1,11 @@ use core::marker::PhantomData; -use frame_support::{ensure, pallet_prelude::Zero, traits::Get}; +use frame_support::ensure; use safe_math::*; -use substrate_fixed::types::{I64F64, U64F64}; -use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; +use substrate_fixed::types::U64F64; +use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token, TokenReserve}; use super::pallet::*; -use crate::{ - SqrtPrice, - tick::{ActiveTickIndexManager, TickIndex}, -}; /// A struct representing a single swap step with all its parameters and state pub(crate) struct BasicSwapStep @@ -21,22 +17,16 @@ where // Input parameters netuid: NetUid, drop_fees: bool, + requested_delta_in: PaidIn, + limit_price: U64F64, - // Computed values - current_liquidity: U64F64, - possible_delta_in: PaidIn, - - // Ticks and prices (current, limit, edge, target) - target_sqrt_price: SqrtPrice, - limit_sqrt_price: SqrtPrice, - current_sqrt_price: SqrtPrice, - edge_sqrt_price: SqrtPrice, - edge_tick: TickIndex, + // Intermediate calculations + target_price: U64F64, + current_price: U64F64, // Result values - action: SwapStepAction, delta_in: PaidIn, - final_price: SqrtPrice, + final_price: U64F64, fee: PaidIn, _phantom: PhantomData<(T, PaidIn, PaidOut)>, @@ -53,36 +43,25 @@ where pub(crate) fn new( netuid: NetUid, amount_remaining: PaidIn, - limit_sqrt_price: SqrtPrice, + limit_price: U64F64, drop_fees: bool, ) -> Self { - // Calculate prices and ticks - let current_tick = CurrentTick::::get(netuid); - let current_sqrt_price = AlphaSqrtPrice::::get(netuid); - let edge_tick = Self::tick_edge(netuid, current_tick); - let edge_sqrt_price = edge_tick.as_sqrt_price_bounded(); - let fee = Pallet::::calculate_fee_amount(netuid, amount_remaining, drop_fees); - let possible_delta_in = amount_remaining.saturating_sub(fee); + let requested_delta_in = amount_remaining.saturating_sub(fee); - // Target price and quantities - let current_liquidity = U64F64::saturating_from_num(CurrentLiquidity::::get(netuid)); - let target_sqrt_price = - Self::sqrt_price_target(current_liquidity, current_sqrt_price, possible_delta_in); + // Target and current prices + let target_price = Self::price_target(netuid, requested_delta_in); + let current_price = Pallet::::current_price(netuid); Self { netuid, drop_fees, - target_sqrt_price, - limit_sqrt_price, - current_sqrt_price, - edge_sqrt_price, - edge_tick, - possible_delta_in, - current_liquidity, - action: SwapStepAction::Stop, + requested_delta_in, + limit_price, + target_price, + current_price, delta_in: PaidIn::ZERO, - final_price: target_sqrt_price, + final_price: target_price, fee, _phantom: PhantomData, } @@ -99,64 +78,25 @@ where let mut recalculate_fee = false; // Calculate the stopping price: The price at which we either reach the limit price, - // exchange the full amount, or reach the edge price. - if Self::price_is_closer(&self.target_sqrt_price, &self.limit_sqrt_price) - && Self::price_is_closer(&self.target_sqrt_price, &self.edge_sqrt_price) - { - // Case 1. target_quantity is the lowest - // The trade completely happens within one tick, no tick crossing happens. - self.action = SwapStepAction::Stop; - self.final_price = self.target_sqrt_price; - self.delta_in = self.possible_delta_in; - } else if Self::price_is_closer(&self.limit_sqrt_price, &self.target_sqrt_price) - && Self::price_is_closer(&self.limit_sqrt_price, &self.edge_sqrt_price) - { - // Case 2. lim_quantity is the lowest - // The trade also completely happens within one tick, no tick crossing happens. - self.action = SwapStepAction::Stop; - self.final_price = self.limit_sqrt_price; - self.delta_in = Self::delta_in( - self.current_liquidity, - self.current_sqrt_price, - self.limit_sqrt_price, - ); - recalculate_fee = true; + // or exchange the full amount. + if Self::price_is_closer(&self.target_price, &self.limit_price) { + // Case 1. target_quantity is the lowest, execute in full + self.final_price = self.target_price; + self.delta_in = self.requested_delta_in; } else { - // Case 3. edge_quantity is the lowest - // Tick crossing is likely - self.action = SwapStepAction::Crossing; - self.delta_in = Self::delta_in( - self.current_liquidity, - self.current_sqrt_price, - self.edge_sqrt_price, - ); - self.final_price = self.edge_sqrt_price; + // Case 2. lim_quantity is the lowest + self.final_price = self.limit_price; + self.delta_in = Self::delta_in(self.netuid, self.current_price, self.limit_price); recalculate_fee = true; } - log::trace!("\tAction : {:?}", self.action); - log::trace!( - "\tCurrent Price : {}", - self.current_sqrt_price - .saturating_mul(self.current_sqrt_price) - ); - log::trace!( - "\tTarget Price : {}", - self.target_sqrt_price - .saturating_mul(self.target_sqrt_price) - ); - log::trace!( - "\tLimit Price : {}", - self.limit_sqrt_price.saturating_mul(self.limit_sqrt_price) - ); - log::trace!( - "\tEdge Price : {}", - self.edge_sqrt_price.saturating_mul(self.edge_sqrt_price) - ); + log::trace!("\tCurrent Price : {}", self.current_price); + log::trace!("\tTarget Price : {}", self.target_price); + log::trace!("\tLimit Price : {}", self.limit_price); log::trace!("\tDelta In : {}", self.delta_in); // Because on step creation we calculate fee off the total amount, we might need to - // recalculate it in case if we hit the limit price or the edge price. + // recalculate it in case if we hit the limit price. if recalculate_fee { let u16_max = U64F64::saturating_from_num(u16::MAX); let fee_rate = if self.drop_fees { @@ -170,345 +110,115 @@ where .saturating_to_num::() .into(); } - - // Now correct the action if we stopped exactly at the edge no matter what was the case - // above. Because order type buy moves the price up and tick semi-open interval doesn't - // include its right point, we cross on buys and stop on sells. - let natural_reason_stop_price = - if Self::price_is_closer(&self.limit_sqrt_price, &self.target_sqrt_price) { - self.limit_sqrt_price - } else { - self.target_sqrt_price - }; - if natural_reason_stop_price == self.edge_sqrt_price { - self.action = Self::action_on_edge_sqrt_price(); - } } /// Process a single step of a swap fn process_swap(&self) -> Result, Error> { + // Convert amounts, actual swap happens here let delta_out = Self::convert_deltas(self.netuid, self.delta_in); log::trace!("\tDelta Out : {delta_out}"); - let mut fee_to_block_author = 0.into(); - if self.delta_in > 0.into() { - ensure!(delta_out > 0.into(), Error::::ReservesTooLow); - - // Split fees according to DefaultFeeSplit between liquidity pool and - // validators. In case we want just to forward 100% of fees to the block - // author, it can be done this way: - // ``` - // fee_to_block_author = self.fee; - // ``` - let fee_split = DefaultFeeSplit::get(); - let lp_fee: PaidIn = fee_split.mul_floor(self.fee.to_u64()).into(); - - // Hold the reserve portion of fees - if !lp_fee.is_zero() { - Self::add_fees( - self.netuid, - Pallet::::current_liquidity_safe(self.netuid), - lp_fee, - ); - } + if !self.delta_in.is_zero() { + ensure!(!delta_out.is_zero(), Error::::ReservesTooLow); - fee_to_block_author = self.fee.saturating_sub(lp_fee); + // 100% of swap fees to to block builder + fee_to_block_author = self.fee; } - if self.action == SwapStepAction::Crossing { - let mut tick = Ticks::::get(self.netuid, self.edge_tick).unwrap_or_default(); - tick.fees_out_tao = I64F64::saturating_from_num(FeeGlobalTao::::get(self.netuid)) - .saturating_sub(tick.fees_out_tao); - tick.fees_out_alpha = - I64F64::saturating_from_num(FeeGlobalAlpha::::get(self.netuid)) - .saturating_sub(tick.fees_out_alpha); - Self::update_liquidity_at_crossing(self.netuid)?; - Ticks::::insert(self.netuid, self.edge_tick, tick); - } - - // Update current price - AlphaSqrtPrice::::set(self.netuid, self.final_price); - - // Update current tick - let new_current_tick = TickIndex::from_sqrt_price_bounded(self.final_price); - CurrentTick::::set(self.netuid, new_current_tick); - Ok(SwapStepResult { - amount_to_take: self.delta_in.saturating_add(self.fee), fee_paid: self.fee, delta_in: self.delta_in, delta_out, fee_to_block_author, }) } - - pub(crate) fn action(&self) -> SwapStepAction { - self.action - } } impl SwapStep for BasicSwapStep { - fn delta_in( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - sqrt_price_target: SqrtPrice, - ) -> TaoBalance { - liquidity_curr - .saturating_mul(sqrt_price_target.saturating_sub(sqrt_price_curr)) - .saturating_to_num::() - .into() + fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> TaoBalance { + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let balancer = SwapBalancer::::get(netuid); + TaoBalance::from(balancer.calculate_quote_delta_in( + price_curr, + price_target, + tao_reserve.into(), + )) } - fn tick_edge(netuid: NetUid, current_tick: TickIndex) -> TickIndex { - ActiveTickIndexManager::::find_closest_higher( - netuid, - current_tick.next().unwrap_or(TickIndex::MAX), + fn price_target(netuid: NetUid, delta_in: TaoBalance) -> U64F64 { + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let balancer = SwapBalancer::::get(netuid); + let dy = delta_in; + let dx = Self::convert_deltas(netuid, dy); + balancer.calculate_price( + u64::from(alpha_reserve.saturating_sub(dx)), + u64::from(tao_reserve.saturating_add(dy)), ) - .unwrap_or(TickIndex::MAX) } - fn sqrt_price_target( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - delta_in: TaoBalance, - ) -> SqrtPrice { - let delta_fixed = U64F64::saturating_from_num(delta_in); - - // No liquidity means that price should go to the limit - if liquidity_curr == 0 { - return SqrtPrice::saturating_from_num( - Pallet::::max_price_inner::().to_u64(), - ); - } - - delta_fixed - .safe_div(liquidity_curr) - .saturating_add(sqrt_price_curr) - } - - fn price_is_closer(sq_price1: &SqrtPrice, sq_price2: &SqrtPrice) -> bool { - sq_price1 <= sq_price2 - } - - fn action_on_edge_sqrt_price() -> SwapStepAction { - SwapStepAction::Crossing - } - - fn add_fees(netuid: NetUid, current_liquidity: U64F64, fee: TaoBalance) { - if current_liquidity == 0 { - return; - } - - let fee_fixed = U64F64::saturating_from_num(fee.to_u64()); - - FeeGlobalTao::::mutate(netuid, |value| { - *value = value.saturating_add(fee_fixed.safe_div(current_liquidity)) - }); + fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool { + price1 <= price2 } fn convert_deltas(netuid: NetUid, delta_in: TaoBalance) -> AlphaBalance { - // Skip conversion if delta_in is zero - if delta_in.is_zero() { - return AlphaBalance::ZERO; - } - - let liquidity_curr = SqrtPrice::saturating_from_num(CurrentLiquidity::::get(netuid)); - let sqrt_price_curr = AlphaSqrtPrice::::get(netuid); - let delta_fixed = SqrtPrice::saturating_from_num(delta_in.to_u64()); - - // Calculate result based on order type with proper fixed-point math - // Using safe math operations throughout to prevent overflows - let result = { - // (liquidity_curr * sqrt_price_curr + delta_fixed) * sqrt_price_curr; - let a = liquidity_curr - .saturating_mul(sqrt_price_curr) - .saturating_add(delta_fixed) - .saturating_mul(sqrt_price_curr); - // liquidity_curr / a; - let b = liquidity_curr.safe_div(a); - // b * delta_fixed; - b.saturating_mul(delta_fixed) - }; - - result.saturating_to_num::().into() - } - - fn update_liquidity_at_crossing(netuid: NetUid) -> Result<(), Error> { - let mut liquidity_curr = CurrentLiquidity::::get(netuid); - let current_tick_index = TickIndex::current_bounded::(netuid); - - // Find the appropriate tick based on order type - let tick = { - // Self::find_closest_higher_active_tick(netuid, current_tick_index), - let upper_tick = ActiveTickIndexManager::::find_closest_higher( - netuid, - current_tick_index.next().unwrap_or(TickIndex::MAX), - ) - .unwrap_or(TickIndex::MAX); - Ticks::::get(netuid, upper_tick) - } - .ok_or(Error::::InsufficientLiquidity)?; - - let liquidity_update_abs_u64 = tick.liquidity_net_as_u64(); - - // Update liquidity based on the sign of liquidity_net and the order type - liquidity_curr = if tick.liquidity_net >= 0 { - liquidity_curr.saturating_add(liquidity_update_abs_u64) - } else { - liquidity_curr.saturating_sub(liquidity_update_abs_u64) - }; - - CurrentLiquidity::::set(netuid, liquidity_curr); - - Ok(()) + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let balancer = SwapBalancer::::get(netuid); + let e = balancer.exp_quote_base(tao_reserve.into(), delta_in.into()); + let one = U64F64::from_num(1); + let alpha_reserve_fixed = U64F64::from_num(alpha_reserve); + AlphaBalance::from( + alpha_reserve_fixed + .saturating_mul(one.saturating_sub(e)) + .saturating_to_num::(), + ) } } impl SwapStep for BasicSwapStep { - fn delta_in( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - sqrt_price_target: SqrtPrice, - ) -> AlphaBalance { - let one = U64F64::saturating_from_num(1); - - liquidity_curr - .saturating_mul( - one.safe_div(sqrt_price_target.into()) - .saturating_sub(one.safe_div(sqrt_price_curr)), - ) - .saturating_to_num::() - .into() + fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> AlphaBalance { + let alpha_reserve = T::AlphaReserve::reserve(netuid); + let balancer = SwapBalancer::::get(netuid); + AlphaBalance::from(balancer.calculate_base_delta_in( + price_curr, + price_target, + alpha_reserve.into(), + )) } - fn tick_edge(netuid: NetUid, current_tick: TickIndex) -> TickIndex { - let current_price: SqrtPrice = AlphaSqrtPrice::::get(netuid); - let current_tick_price = current_tick.as_sqrt_price_bounded(); - let is_active = ActiveTickIndexManager::::tick_is_active(netuid, current_tick); - - if is_active && current_price > current_tick_price { - return ActiveTickIndexManager::::find_closest_lower(netuid, current_tick) - .unwrap_or(TickIndex::MIN); - } - - ActiveTickIndexManager::::find_closest_lower( - netuid, - current_tick.prev().unwrap_or(TickIndex::MIN), - ) - .unwrap_or(TickIndex::MIN) - } - - fn sqrt_price_target( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - delta_in: AlphaBalance, - ) -> SqrtPrice { - let delta_fixed = U64F64::saturating_from_num(delta_in); - let one = U64F64::saturating_from_num(1); - - // No liquidity means that price should go to the limit - if liquidity_curr == 0 { - return SqrtPrice::saturating_from_num( - Pallet::::min_price_inner::().to_u64(), - ); - } - - one.safe_div( - delta_fixed - .safe_div(liquidity_curr) - .saturating_add(one.safe_div(sqrt_price_curr)), + fn price_target(netuid: NetUid, delta_in: AlphaBalance) -> U64F64 { + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let balancer = SwapBalancer::::get(netuid); + let dx = delta_in; + let dy = Self::convert_deltas(netuid, dx); + balancer.calculate_price( + u64::from(alpha_reserve.saturating_add(dx)), + u64::from(tao_reserve.saturating_sub(dy)), ) } - fn price_is_closer(sq_price1: &SqrtPrice, sq_price2: &SqrtPrice) -> bool { - sq_price1 >= sq_price2 - } - - fn action_on_edge_sqrt_price() -> SwapStepAction { - SwapStepAction::Stop - } - - fn add_fees(netuid: NetUid, current_liquidity: U64F64, fee: AlphaBalance) { - if current_liquidity == 0 { - return; - } - - let fee_fixed = U64F64::saturating_from_num(fee.to_u64()); - - FeeGlobalAlpha::::mutate(netuid, |value| { - *value = value.saturating_add(fee_fixed.safe_div(current_liquidity)) - }); + fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool { + price1 >= price2 } fn convert_deltas(netuid: NetUid, delta_in: AlphaBalance) -> TaoBalance { - // Skip conversion if delta_in is zero - if delta_in.is_zero() { - return TaoBalance::ZERO; - } - - let liquidity_curr = SqrtPrice::saturating_from_num(CurrentLiquidity::::get(netuid)); - let sqrt_price_curr = AlphaSqrtPrice::::get(netuid); - let delta_fixed = SqrtPrice::saturating_from_num(delta_in.to_u64()); - - // Calculate result based on order type with proper fixed-point math - // Using safe math operations throughout to prevent overflows - let result = { - // liquidity_curr / (liquidity_curr / sqrt_price_curr + delta_fixed); - let denom = liquidity_curr - .safe_div(sqrt_price_curr) - .saturating_add(delta_fixed); - let a = liquidity_curr.safe_div(denom); - // a * sqrt_price_curr; - let b = a.saturating_mul(sqrt_price_curr); - - // delta_fixed * b; - delta_fixed.saturating_mul(b) - }; - - result.saturating_to_num::().into() - } - - fn update_liquidity_at_crossing(netuid: NetUid) -> Result<(), Error> { - let mut liquidity_curr = CurrentLiquidity::::get(netuid); - let current_tick_index = TickIndex::current_bounded::(netuid); - - // Find the appropriate tick based on order type - let tick = { - // Self::find_closest_lower_active_tick(netuid, current_tick_index) - let current_price = AlphaSqrtPrice::::get(netuid); - let current_tick_price = current_tick_index.as_sqrt_price_bounded(); - let is_active = ActiveTickIndexManager::::tick_is_active(netuid, current_tick_index); - - let lower_tick = if is_active && current_price > current_tick_price { - ActiveTickIndexManager::::find_closest_lower(netuid, current_tick_index) - .unwrap_or(TickIndex::MIN) - } else { - ActiveTickIndexManager::::find_closest_lower( - netuid, - current_tick_index.prev().unwrap_or(TickIndex::MIN), - ) - .unwrap_or(TickIndex::MIN) - }; - Ticks::::get(netuid, lower_tick) - } - .ok_or(Error::::InsufficientLiquidity)?; - - let liquidity_update_abs_u64 = tick.liquidity_net_as_u64(); - - // Update liquidity based on the sign of liquidity_net and the order type - liquidity_curr = if tick.liquidity_net >= 0 { - liquidity_curr.saturating_sub(liquidity_update_abs_u64) - } else { - liquidity_curr.saturating_add(liquidity_update_abs_u64) - }; - - CurrentLiquidity::::set(netuid, liquidity_curr); - - Ok(()) + let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); + let tao_reserve = T::TaoReserve::reserve(netuid.into()); + let balancer = SwapBalancer::::get(netuid); + let e = balancer.exp_base_quote(alpha_reserve.into(), delta_in.into()); + let one = U64F64::from_num(1); + let tao_reserve_fixed = U64F64::from_num(u64::from(tao_reserve)); + TaoBalance::from( + tao_reserve_fixed + .saturating_mul(one.saturating_sub(e)) + .saturating_to_num::(), + ) } } @@ -519,49 +229,21 @@ where PaidOut: Token, { /// Get the input amount needed to reach the target price - fn delta_in( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - sqrt_price_target: SqrtPrice, - ) -> PaidIn; + fn delta_in(netuid: NetUid, price_curr: U64F64, price_target: U64F64) -> PaidIn; - /// Get the tick at the current tick edge. - /// - /// If anything is wrong with tick math and it returns Err, we just abort the deal, i.e. return - /// the edge that is impossible to execute - fn tick_edge(netuid: NetUid, current_tick: TickIndex) -> TickIndex; - - /// Get the target square root price based on the input amount - /// - /// This is the price that would be reached if - /// - There are no liquidity positions other than protocol liquidity - /// - Full delta_in amount is executed - fn sqrt_price_target( - liquidity_curr: U64F64, - sqrt_price_curr: SqrtPrice, - delta_in: PaidIn, - ) -> SqrtPrice; + /// Get the target price based on the input amount + fn price_target(netuid: NetUid, delta_in: PaidIn) -> U64F64; - /// Returns True if sq_price1 is closer to the current price than sq_price2 - /// in terms of order direction. - /// For buying: sq_price1 <= sq_price2 - /// For selling: sq_price1 >= sq_price2 - fn price_is_closer(sq_price1: &SqrtPrice, sq_price2: &SqrtPrice) -> bool; - - /// Get swap step action on the edge sqrt price. - fn action_on_edge_sqrt_price() -> SwapStepAction; - - /// Add fees to the global fee counters - fn add_fees(netuid: NetUid, current_liquidity: U64F64, fee: PaidIn); + /// Returns True if price1 is closer to the current price than price2 + /// For buying: price1 <= price2 + /// For selling: price1 >= price2 + fn price_is_closer(price1: &U64F64, price2: &U64F64) -> bool; /// Convert input amount (delta_in) to output amount (delta_out) /// - /// This is the core method of uniswap V3 that tells how much output token is given for an + /// This is the core method of the swap that tells how much output token is given for an /// amount of input token within one price tick. fn convert_deltas(netuid: NetUid, delta_in: PaidIn) -> PaidOut; - - /// Update liquidity when crossing a tick - fn update_liquidity_at_crossing(netuid: NetUid) -> Result<(), Error>; } #[derive(Debug, PartialEq)] @@ -570,15 +252,8 @@ where PaidIn: Token, PaidOut: Token, { - pub(crate) amount_to_take: PaidIn, pub(crate) fee_paid: PaidIn, pub(crate) delta_in: PaidIn, pub(crate) delta_out: PaidOut, pub(crate) fee_to_block_author: PaidIn, } - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum SwapStepAction { - Crossing, - Stop, -} diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 5d75c7da27..b1071294d3 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -6,76 +6,30 @@ )] use approx::assert_abs_diff_eq; -use frame_support::{assert_err, assert_noop, assert_ok}; -use sp_arithmetic::helpers_128bit; +use frame_support::{assert_noop, assert_ok}; +use sp_arithmetic::Perquintill; use sp_runtime::DispatchError; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{NetUid, Token}; use subtensor_swap_interface::Order as OrderT; use super::*; +use crate::mock::*; use crate::pallet::swap_step::*; -use crate::{SqrtPrice, mock::*}; - -// this function is used to convert price (NON-SQRT price!) to TickIndex. it's only utility for -// testing, all the implementation logic is based on sqrt prices -fn price_to_tick(price: f64) -> TickIndex { - let price_sqrt: SqrtPrice = SqrtPrice::from_num(price.sqrt()); - // Handle potential errors in the conversion - match TickIndex::try_from_sqrt_price(price_sqrt) { - Ok(mut tick) => { - // Ensure the tick is within bounds - if tick > TickIndex::MAX { - tick = TickIndex::MAX; - } else if tick < TickIndex::MIN { - tick = TickIndex::MIN; - } - tick - } - // Default to a reasonable value when conversion fails - Err(_) => { - if price > 1.0 { - TickIndex::MAX - } else { - TickIndex::MIN - } - } - } -} -fn get_ticked_prices_around_current_price() -> (f64, f64) { - // Get current price, ticks around it, and prices on the tick edges for test cases - let netuid = NetUid::from(1); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let current_tick = CurrentTick::::get(netuid); - - // Low and high prices that match to a lower and higher tick that doesn't contain the current price - let current_price_low_sqrt = current_tick.as_sqrt_price_bounded(); - let current_price_high_sqrt = current_tick.next().unwrap().as_sqrt_price_bounded(); - let current_price_low = U96F32::from_num(current_price_low_sqrt * current_price_low_sqrt); - let current_price_high = U96F32::from_num(current_price_high_sqrt * current_price_high_sqrt); - - ( - current_price_low.to_num::(), - current_price_high.to_num::() + 0.000000001, - ) +// Run all tests: +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests --nocapture + +#[allow(dead_code)] +fn get_min_price() -> U64F64 { + U64F64::from_num(Pallet::::min_price_inner::()) + / U64F64::from_num(1_000_000_000) } -// this function is used to convert tick index NON-SQRT (!) price. it's only utility for -// testing, all the implementation logic is based on sqrt prices -fn tick_to_price(tick: TickIndex) -> f64 { - // Handle errors gracefully - match tick.try_to_sqrt_price() { - Ok(price_sqrt) => (price_sqrt * price_sqrt).to_num::(), - Err(_) => { - // Return a sensible default based on whether the tick is above or below the valid range - if tick > TickIndex::MAX { - tick_to_price(TickIndex::MAX) // Use the max valid tick price - } else { - tick_to_price(TickIndex::MIN) // Use the min valid tick price - } - } - } +#[allow(dead_code)] +fn get_max_price() -> U64F64 { + U64F64::from_num(Pallet::::max_price_inner::()) + / U64F64::from_num(1_000_000_000) } mod dispatchables { @@ -105,607 +59,363 @@ mod dispatchables { ); }); } -} - -#[test] -fn test_swap_initialization() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - // Get reserves from the mock provider - let tao = TaoReserve::reserve(netuid.into()); - let alpha = AlphaReserve::reserve(netuid.into()); - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - assert!(SwapV3Initialized::::get(netuid)); - - // Verify current price is set - let sqrt_price = AlphaSqrtPrice::::get(netuid); - let expected_sqrt_price = U64F64::from_num(0.5_f64); - assert_abs_diff_eq!( - sqrt_price.to_num::(), - expected_sqrt_price.to_num::(), - epsilon = 0.000000001 - ); - // Verify that current tick is set - let current_tick = CurrentTick::::get(netuid); - let expected_current_tick = TickIndex::from_sqrt_price_bounded(expected_sqrt_price); - assert_eq!(current_tick, expected_current_tick); - - // Calculate expected liquidity - let expected_liquidity = - helpers_128bit::sqrt((tao.to_u64() as u128).saturating_mul(alpha.to_u64() as u128)) - as u64; - - // Get the protocol account - let protocol_account_id = Pallet::::protocol_account_id(); - - // Verify position created for protocol account - let positions = Positions::::iter_prefix_values((netuid, protocol_account_id)) - .collect::>(); - assert_eq!(positions.len(), 1); - - let position = &positions[0]; - assert_eq!(position.liquidity, expected_liquidity); - assert_eq!(position.tick_low, TickIndex::MIN); - assert_eq!(position.tick_high, TickIndex::MAX); - assert_eq!(position.fees_tao, 0); - assert_eq!(position.fees_alpha, 0); - - // Verify ticks were created - let tick_low = Ticks::::get(netuid, TickIndex::MIN).unwrap(); - let tick_high = Ticks::::get(netuid, TickIndex::MAX).unwrap(); - - // Check liquidity values - assert_eq!(tick_low.liquidity_net, expected_liquidity as i128); - assert_eq!(tick_low.liquidity_gross, expected_liquidity); - assert_eq!(tick_high.liquidity_net, -(expected_liquidity as i128)); - assert_eq!(tick_high.liquidity_gross, expected_liquidity); - - // Verify current liquidity is set - assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity); - }); -} + fn perquintill_to_f64(p: Perquintill) -> f64 { + let parts = p.deconstruct() as f64; + parts / 1_000_000_000_000_000_000_f64 + } -// Test adding liquidity on top of the existing protocol liquidity -#[test] -fn test_add_liquidity_basic() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - assert_eq!(max_tick, TickIndex::MAX); - - assert_ok!(Pallet::::maybe_initialize_v3(NetUid::from(1))); - let current_price = Pallet::::current_price(NetUid::from(1)).to_num::(); - let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - - // As a user add liquidity with all possible corner cases - // - Initial price is 0.25 - // - liquidity is expressed in RAO units - // Test case is (price_low, price_high, liquidity, tao, alpha) + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_happy --exact --nocapture + #[test] + fn test_adjust_protocol_liquidity_happy() { + // test case: tao_delta, alpha_delta [ - // Repeat the protocol liquidity at maximum range: Expect all the same values - ( - min_price, - max_price, - 2_000_000_000_u64, - 1_000_000_000_u64, - 4_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range: Expect the same alpha - ( - current_price_high, - max_price, - 2_000_000_000_u64, - 0, - 4_000_000_000, - ), - // Repeat the protocol liquidity at min to current range: Expect all the same tao - ( - min_price, - current_price_low, - 2_000_000_000_u64, - 1_000_000_000, - 0, - ), - // Half to double price - just some sane wothdraw amounts - (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), - // Both below price - tao is non-zero, alpha is zero - (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), - // Both above price - tao is zero, alpha is non-zero - (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), + (0_u64, 0_u64), + (0_u64, 1_u64), + (1_u64, 0_u64), + (1_u64, 1_u64), + (0_u64, 10_u64), + (10_u64, 0_u64), + (10_u64, 10_u64), + (0_u64, 100_u64), + (100_u64, 0_u64), + (100_u64, 100_u64), + (0_u64, 1_000_u64), + (1_000_u64, 0_u64), + (1_000_u64, 1_000_u64), + (1_000_000_u64, 0_u64), + (0_u64, 1_000_000_u64), + (1_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_u64), + (1_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_u64, 2_u64), + (2_u64, 1_u64), + (10_u64, 20_u64), + (20_u64, 10_u64), + (100_u64, 200_u64), + (200_u64, 100_u64), + (1_000_u64, 2_000_u64), + (2_000_u64, 1_000_u64), + (1_000_000_u64, 2_000_000_u64), + (2_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 2_000_000_000_u64), + (2_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 2_000_000_000_000_u64), + (2_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_234_567_u64, 2_432_765_u64), + (1_234_567_u64, 2_432_765_890_u64), ] .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) - .for_each( - |(netuid, price_low, price_high, liquidity, expected_tao, expected_alpha)| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - // Get tick infos and liquidity before adding (to account for protocol liquidity) - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - let tick_high_info_before = - Ticks::::get(netuid, tick_high).unwrap_or_default(); - let liquidity_before = CurrentLiquidity::::get(netuid); - - // Add liquidity - let (position_id, tao, alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - assert_abs_diff_eq!(tao, expected_tao, epsilon = tao / 1000); - assert_abs_diff_eq!(alpha, expected_alpha, epsilon = alpha / 1000); - - // Check that low and high ticks appear in the state and are properly updated - let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - let expected_liquidity_net_low = liquidity as i128; - let expected_liquidity_gross_low = liquidity; - let expected_liquidity_net_high = -(liquidity as i128); - let expected_liquidity_gross_high = liquidity; - - assert_eq!( - tick_low_info.liquidity_net - tick_low_info_before.liquidity_net, - expected_liquidity_net_low, - ); - assert_eq!( - tick_low_info.liquidity_gross - tick_low_info_before.liquidity_gross, - expected_liquidity_gross_low, - ); - assert_eq!( - tick_high_info.liquidity_net - tick_high_info_before.liquidity_net, - expected_liquidity_net_high, - ); - assert_eq!( - tick_high_info.liquidity_gross - tick_high_info_before.liquidity_gross, - expected_liquidity_gross_high, + .for_each(|(tao_delta, alpha_delta)| { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + let tao_delta = TaoBalance::from(tao_delta); + let alpha_delta = AlphaBalance::from(alpha_delta); + + // Initialize reserves and price + let tao = TaoBalance::from(1_000_000_000_000_u64); + let alpha = AlphaBalance::from(4_000_000_000_000_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + let price_before = Swap::current_price(netuid); + + // Adjust reserves + Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + TaoReserve::set_mock_reserve(netuid, tao + tao_delta); + AlphaReserve::set_mock_reserve(netuid, alpha + alpha_delta); + + // Check that price didn't change + let price_after = Swap::current_price(netuid); + assert_abs_diff_eq!( + price_before.to_num::(), + price_after.to_num::(), + epsilon = price_before.to_num::() / 1_000_000_000_000. ); - // Liquidity position at correct ticks - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 1 + // Check that reserve weight was properly updated + let new_tao = u64::from(tao + tao_delta) as f64; + let new_alpha = u64::from(alpha + alpha_delta) as f64; + let expected_quote_weight = + new_tao / (new_alpha * price_before.to_num::() + new_tao); + let expected_quote_weight_delta = expected_quote_weight - 0.5; + let res_weights = SwapBalancer::::get(netuid); + let actual_quote_weight_delta = + perquintill_to_f64(res_weights.get_quote_weight()) - 0.5; + let eps = expected_quote_weight / 1_000_000_000_000.; + assert_abs_diff_eq!( + expected_quote_weight_delta, + actual_quote_weight_delta, + epsilon = eps ); + }); + }); + } - let position = - Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - assert_eq!(position.liquidity, liquidity); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - assert_eq!(position.fees_alpha, 0); - assert_eq!(position.fees_tao, 0); - - // Current liquidity is updated only when price range includes the current price - let expected_liquidity = - if (price_high > current_price) && (price_low <= current_price) { - liquidity_before + liquidity - } else { - liquidity_before - }; - - assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) - }, - ); - }); -} - -#[test] -fn test_add_liquidity_max_limit_enforced() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let liquidity = 2_000_000_000_u64; - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - let limit = MaxPositions::get() as usize; - - for _ in 0..limit { - Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - TickIndex::MIN, - TickIndex::MAX, - liquidity, - ) - .unwrap(); - } - - let test_result = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - TickIndex::MIN, - TickIndex::MAX, - liquidity, - ); - - assert_err!(test_result, Error::::MaxPositionsExceeded); - }); -} - -#[test] -fn test_add_liquidity_out_of_bounds() { - new_test_ext().execute_with(|| { + /// This test case verifies that small gradual injections (like emissions in every block) + /// in the worst case + /// - Do not cause price to change + /// - Result in the same weight change as one large injection + /// + /// This is a long test that only tests validity of weights math. Run again if changing + /// Balancer::update_weights_for_added_liquidity + /// + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_deltas --exact --nocapture + #[ignore] + #[test] + fn test_adjust_protocol_liquidity_deltas() { + // The number of times (blocks) over which gradual injections will be made + // One year price drift due to precision is under 1e-6 + const ITERATIONS: u64 = 2_700_000; + const PRICE_PRECISION: f64 = 0.000_001; + const PREC_LARGE_DELTA: f64 = 0.001; + const WEIGHT_PRECISION: f64 = 0.000_000_000_000_000_001; + + let initial_tao_reserve = TaoBalance::from(1_000_000_000_000_000_u64); + let initial_alpha_reserve = AlphaBalance::from(10_000_000_000_000_000_u64); + + // test case: tao_delta, alpha_delta, price_precision [ - // For our tests, we'll construct TickIndex values that are intentionally - // outside the valid range for testing purposes only - ( - TickIndex::new_unchecked(TickIndex::MIN.get() - 1), - TickIndex::MAX, - 1_000_000_000_u64, - ), - ( - TickIndex::MIN, - TickIndex::new_unchecked(TickIndex::MAX.get() + 1), - 1_000_000_000_u64, - ), - ( - TickIndex::new_unchecked(TickIndex::MIN.get() - 1), - TickIndex::new_unchecked(TickIndex::MAX.get() + 1), - 1_000_000_000_u64, - ), - ( - TickIndex::new_unchecked(TickIndex::MIN.get() - 100), - TickIndex::new_unchecked(TickIndex::MAX.get() + 100), - 1_000_000_000_u64, - ), - // Inverted ticks: high < low - ( - TickIndex::new_unchecked(-900), - TickIndex::new_unchecked(-1000), - 1_000_000_000_u64, - ), - // Equal ticks: high == low - ( - TickIndex::new_unchecked(-10_000), - TickIndex::new_unchecked(-10_000), - 1_000_000_000_u64, - ), + (0_u64, 0_u64, PRICE_PRECISION), + (0_u64, 1_u64, PRICE_PRECISION), + (1_u64, 0_u64, PRICE_PRECISION), + (1_u64, 1_u64, PRICE_PRECISION), + (0_u64, 10_u64, PRICE_PRECISION), + (10_u64, 0_u64, PRICE_PRECISION), + (10_u64, 10_u64, PRICE_PRECISION), + (0_u64, 100_u64, PRICE_PRECISION), + (100_u64, 0_u64, PRICE_PRECISION), + (100_u64, 100_u64, PRICE_PRECISION), + (0_u64, 987_u64, PRICE_PRECISION), + (987_u64, 0_u64, PRICE_PRECISION), + (876_u64, 987_u64, PRICE_PRECISION), + (0_u64, 1_000_u64, PRICE_PRECISION), + (1_000_u64, 0_u64, PRICE_PRECISION), + (1_000_u64, 1_000_u64, PRICE_PRECISION), + (0_u64, 1_234_u64, PRICE_PRECISION), + (1_234_u64, 0_u64, PRICE_PRECISION), + (1_234_u64, 4_321_u64, PRICE_PRECISION), + (1_234_000_u64, 4_321_000_u64, PREC_LARGE_DELTA), + (1_234_u64, 4_321_000_u64, PREC_LARGE_DELTA), ] .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) - .for_each(|(netuid, tick_low, tick_high, liquidity)| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - assert_err!( - Swap::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity - ), - Error::::InvalidTickRange, - ); - }); - }); -} + .for_each(|(tao_delta, alpha_delta, price_precision)| { + new_test_ext().execute_with(|| { + let netuid1 = NetUid::from(1); + + let tao_delta = TaoBalance::from(tao_delta); + let alpha_delta = AlphaBalance::from(alpha_delta); + + // Initialize realistically large reserves + let mut tao = initial_tao_reserve; + let mut alpha = initial_alpha_reserve; + TaoReserve::set_mock_reserve(netuid1, tao); + AlphaReserve::set_mock_reserve(netuid1, alpha); + let price_before = Swap::current_price(netuid1); + + // Adjust reserves gradually + for _ in 0..ITERATIONS { + Swap::adjust_protocol_liquidity(netuid1, tao_delta, alpha_delta); + tao += tao_delta; + alpha += alpha_delta; + TaoReserve::set_mock_reserve(netuid1, tao); + AlphaReserve::set_mock_reserve(netuid1, alpha); + } + // Check that price didn't change + let price_after = Swap::current_price(netuid1); + assert_abs_diff_eq!( + price_before.to_num::(), + price_after.to_num::(), + epsilon = price_precision + ); -#[test] -fn test_add_liquidity_over_balance() { - new_test_ext().execute_with(|| { - let coldkey_account_id = 3; - let hotkey_account_id = 1002; + ///////////////////////// + // Now do one-time big injection with another netuid and compare weights + let netuid2 = NetUid::from(2); + + // Initialize same large reserves + TaoReserve::set_mock_reserve(netuid2, initial_tao_reserve); + AlphaReserve::set_mock_reserve(netuid2, initial_alpha_reserve); + + // Adjust reserves by one large amount at once + let tao_delta_once = TaoBalance::from(ITERATIONS * u64::from(tao_delta)); + let alpha_delta_once = AlphaBalance::from(ITERATIONS * u64::from(alpha_delta)); + Swap::adjust_protocol_liquidity(netuid2, tao_delta_once, alpha_delta_once); + TaoReserve::set_mock_reserve(netuid2, initial_tao_reserve + tao_delta_once); + AlphaReserve::set_mock_reserve(netuid2, initial_alpha_reserve + alpha_delta_once); + + // Compare reserve weights for netuid 1 and 2 + let res_weights1 = SwapBalancer::::get(netuid1); + let res_weights2 = SwapBalancer::::get(netuid2); + let actual_quote_weight1 = perquintill_to_f64(res_weights1.get_quote_weight()); + let actual_quote_weight2 = perquintill_to_f64(res_weights2.get_quote_weight()); + assert_abs_diff_eq!( + actual_quote_weight1, + actual_quote_weight2, + epsilon = WEIGHT_PRECISION + ); + }); + }); + } + /// Should work ok when initial alpha is zero + /// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::dispatchables::test_adjust_protocol_liquidity_zero_alpha --exact --nocapture + #[test] + fn test_adjust_protocol_liquidity_zero_alpha() { + // test case: tao_delta, alpha_delta [ - // Lower than price (not enough tao) - (0.1, 0.2, 100_000_000_000_u64), - // Higher than price (not enough alpha) - (0.3, 0.4, 100_000_000_000_u64), - // Around the price (not enough both) - (0.1, 0.4, 100_000_000_000_u64), + (0_u64, 0_u64), + (0_u64, 1_u64), + (1_u64, 0_u64), + (1_u64, 1_u64), + (0_u64, 10_u64), + (10_u64, 0_u64), + (10_u64, 10_u64), + (0_u64, 100_u64), + (100_u64, 0_u64), + (100_u64, 100_u64), + (0_u64, 1_000_u64), + (1_000_u64, 0_u64), + (1_000_u64, 1_000_u64), + (1_000_000_u64, 0_u64), + (0_u64, 1_000_000_u64), + (1_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_u64), + (1_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 0_u64), + (0_u64, 1_000_000_000_000_u64), + (1_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_u64, 2_u64), + (2_u64, 1_u64), + (10_u64, 20_u64), + (20_u64, 10_u64), + (100_u64, 200_u64), + (200_u64, 100_u64), + (1_000_u64, 2_000_u64), + (2_000_u64, 1_000_u64), + (1_000_000_u64, 2_000_000_u64), + (2_000_000_u64, 1_000_000_u64), + (1_000_000_000_u64, 2_000_000_000_u64), + (2_000_000_000_u64, 1_000_000_000_u64), + (1_000_000_000_000_u64, 2_000_000_000_000_u64), + (2_000_000_000_000_u64, 1_000_000_000_000_u64), + (1_234_567_u64, 2_432_765_u64), + (1_234_567_u64, 2_432_765_890_u64), ] .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2)) - .for_each(|(netuid, price_low, price_high, liquidity)| { - // Calculate ticks - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - assert_err!( - Pallet::::do_add_liquidity( - netuid, - &coldkey_account_id, - &hotkey_account_id, - tick_low, - tick_high, - liquidity - ), - Error::::InsufficientBalance, - ); + .for_each(|(tao_delta, alpha_delta)| { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + let tao_delta = TaoBalance::from(tao_delta); + let alpha_delta = AlphaBalance::from(alpha_delta); + + // Initialize reserves and price + // broken state: Zero price because of zero alpha reserve + let tao = TaoBalance::from(1_000_000_000_000_u64); + let alpha = AlphaBalance::from(0_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + let price_before = Swap::current_price(netuid); + assert_eq!(price_before, U64F64::from_num(0)); + let new_tao = u64::from(tao + tao_delta) as f64; + let new_alpha = u64::from(alpha + alpha_delta) as f64; + + // Adjust reserves + Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + TaoReserve::set_mock_reserve(netuid, tao + tao_delta); + AlphaReserve::set_mock_reserve(netuid, alpha + alpha_delta); + + let res_weights = SwapBalancer::::get(netuid); + let actual_quote_weight = perquintill_to_f64(res_weights.get_quote_weight()); + + // Check that price didn't change + let price_after = Swap::current_price(netuid); + if new_alpha == 0. { + // If the pool state is still broken (∆x = 0), no change + assert_eq!(actual_quote_weight, 0.5); + assert_eq!(price_after, U64F64::from_num(0)); + } else { + // Price got fixed + let expected_price = new_tao / new_alpha; + assert_abs_diff_eq!( + expected_price, + price_after.to_num::(), + epsilon = price_before.to_num::() / 1_000_000_000_000. + ); + assert_eq!(actual_quote_weight, 0.5); + } + }); }); - }); + } } -// cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_remove_liquidity_basic --exact --show-output #[test] -fn test_remove_liquidity_basic() { +fn test_swap_initialization() { new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - assert_eq!(max_tick, TickIndex::MAX); + let netuid = NetUid::from(1); - let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); + // Setup reserves + let tao = TaoBalance::from(1_000_000_000u64); + let alpha = AlphaBalance::from(4_000_000_000u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); - // As a user add liquidity with all possible corner cases - // - Initial price is 0.25 - // - liquidity is expressed in RAO units - // Test case is (price_low, price_high, liquidity, tao, alpha) - [ - // Repeat the protocol liquidity at maximum range: Expect all the same values - ( - min_price, - max_price, - 2_000_000_000_u64, - 1_000_000_000_u64, - 4_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range: Expect the same alpha - ( - current_price_high, - max_price, - 2_000_000_000_u64, - 0, - 4_000_000_000, - ), - // Repeat the protocol liquidity at min to current range: Expect all the same tao - ( - min_price, - current_price_low, - 2_000_000_000_u64, - 1_000_000_000, - 0, - ), - // Half to double price - just some sane wothdraw amounts - (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000), - // Both below price - tao is non-zero, alpha is zero - (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0), - // Both above price - tao is zero, alpha is non-zero - (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000), - ] - .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3, v.4)) - .for_each(|(netuid, price_low, price_high, liquidity, tao, alpha)| { - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let liquidity_before = CurrentLiquidity::::get(netuid); - - // Add liquidity - let (position_id, _, _) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Remove liquidity - let remove_result = - Pallet::::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id) - .unwrap(); - assert_abs_diff_eq!(remove_result.tao.to_u64(), tao, epsilon = tao / 1000); - assert_abs_diff_eq!( - u64::from(remove_result.alpha), - alpha, - epsilon = alpha / 1000 - ); - assert_eq!(remove_result.fee_tao, TaoBalance::ZERO); - assert_eq!(remove_result.fee_alpha, AlphaBalance::ZERO); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); + assert!(PalSwapInitialized::::get(netuid)); - // Liquidity position is removed - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 0 - ); - assert!(Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).is_none()); + // Verify current price is set + let price = Pallet::::current_price(netuid); + let expected_price = U64F64::from_num(0.25_f64); + assert_abs_diff_eq!( + price.to_num::(), + expected_price.to_num::(), + epsilon = 0.000000001 + ); - // Current liquidity is updated (back where it was) - assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); - }); + // Verify that swap reserve weight is initialized + let reserve_weight = SwapBalancer::::get(netuid); + assert_eq!( + reserve_weight.get_quote_weight(), + Perquintill::from_rational(1_u64, 2_u64), + ); }); } #[test] -fn test_remove_liquidity_nonexisting_position() { +fn test_swap_initialization_with_price() { new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - assert_eq!(max_tick.get(), TickIndex::MAX.get()); - - let liquidity = 2_000_000_000_u64; let netuid = NetUid::from(1); - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(min_price); - let tick_high = price_to_tick(max_price); + // Setup reserves, tao / alpha = 0.25 + let tao = TaoBalance::from(1_000_000_000u64); + let alpha = AlphaBalance::from(4_000_000_000u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - assert_ok!(Pallet::::do_add_liquidity( + // Initialize with 0.2 price + assert_ok!(Pallet::::maybe_initialize_palswap( netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, + Some(U64F64::from(1u16) / U64F64::from(5u16)) )); + assert!(PalSwapInitialized::::get(netuid)); - assert!(Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID) > 0); - - // Remove liquidity - assert_err!( - Pallet::::do_remove_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - PositionId::new::() - ), - Error::::LiquidityNotFound, + // Verify current price is set to 0.2 + let price = Pallet::::current_price(netuid); + let expected_price = U64F64::from_num(0.2_f64); + assert_abs_diff_eq!( + price.to_num::(), + expected_price.to_num::(), + epsilon = 0.000000001 ); }); } -// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_modify_position_basic --exact --show-output -#[test] -fn test_modify_position_basic() { - new_test_ext().execute_with(|| { - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - let limit_price = 1000.0_f64; - assert_eq!(max_tick, TickIndex::MAX); - let (current_price_low, _current_price_high) = get_ticked_prices_around_current_price(); - - // As a user add liquidity with all possible corner cases - // - Initial price is 0.25 - // - liquidity is expressed in RAO units - // Test case is (price_low, price_high, liquidity, tao, alpha) - [ - // Repeat the protocol liquidity at current to max range: Expect the same alpha - ( - current_price_low, - max_price, - 2_000_000_000_u64, - 4_000_000_000, - ), - ] - .into_iter() - .enumerate() - .map(|(n, v)| (NetUid::from(n as u16 + 1), v.0, v.1, v.2, v.3)) - .for_each(|(netuid, price_low, price_high, liquidity, alpha)| { - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - let (position_id, _, _) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Get tick infos before the swap/update - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap(); - - // Swap to create fees on the position - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - let order = GetAlphaForTao::with_amount(liquidity / 10); - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - - // Modify liquidity (also causes claiming of fees) - let liquidity_before = CurrentLiquidity::::get(netuid); - let modify_result = Pallet::::do_modify_position( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - position_id, - -((liquidity / 10) as i64), - ) - .unwrap(); - assert_abs_diff_eq!( - u64::from(modify_result.alpha), - alpha / 10, - epsilon = alpha / 1000 - ); - - // Block author may get all fees - // assert!(modify_result.fee_tao > TaoBalance::ZERO); - // assert_eq!(modify_result.fee_alpha, AlphaBalance::ZERO); - - // Liquidity position is reduced - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 1 - ); - - // Current liquidity is reduced with modify_position - assert!(CurrentLiquidity::::get(netuid) < liquidity_before); - - // Position liquidity is reduced - let position = - Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); - assert_eq!(position.liquidity, liquidity * 9 / 10); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - - // Tick liquidity is updated properly for low and high position ticks - let tick_low_info_after = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info_after = Ticks::::get(netuid, tick_high).unwrap(); - - assert_eq!( - tick_low_info_before.liquidity_net - (liquidity / 10) as i128, - tick_low_info_after.liquidity_net, - ); - assert_eq!( - tick_low_info_before.liquidity_gross - (liquidity / 10), - tick_low_info_after.liquidity_gross, - ); - assert_eq!( - tick_high_info_before.liquidity_net + (liquidity / 10) as i128, - tick_high_info_after.liquidity_net, - ); - assert_eq!( - tick_high_info_before.liquidity_gross - (liquidity / 10), - tick_high_info_after.liquidity_gross, - ); - - // Modify liquidity again (ensure fees aren't double-collected) - let modify_result = Pallet::::do_modify_position( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - position_id, - -((liquidity / 100) as i64), - ) - .unwrap(); - - assert_abs_diff_eq!( - u64::from(modify_result.alpha), - alpha / 100, - epsilon = alpha / 1000 - ); - assert_eq!(modify_result.fee_tao, TaoBalance::ZERO); - assert_eq!(modify_result.fee_alpha, AlphaBalance::ZERO); - }); - }); -} - -// cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_basic --exact --show-output +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_swap_basic --exact --nocapture #[test] fn test_swap_basic() { new_test_ext().execute_with(|| { @@ -713,855 +423,278 @@ fn test_swap_basic() { netuid: NetUid, order: Order, limit_price: f64, - output_amount: u64, price_should_grow: bool, ) where Order: OrderT, - Order::PaidIn: GlobalFeeInfo, BasicSwapStep: SwapStep, { - // Consumed liquidity ticks - let tick_low = TickIndex::MIN; - let tick_high = TickIndex::MAX; - let liquidity = order.amount().to_u64(); + let swap_amount = order.amount().to_u64(); // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Get tick infos before the swap - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - let tick_high_info_before = Ticks::::get(netuid, tick_high).unwrap_or_default(); - let liquidity_before = CurrentLiquidity::::get(netuid); + // Price is 0.25 + let initial_tao_reserve = TaoBalance::from(1_000_000_000_u64); + let initial_alpha_reserve = AlphaBalance::from(4_000_000_000_u64); + TaoReserve::set_mock_reserve(netuid, initial_tao_reserve); + AlphaReserve::set_mock_reserve(netuid, initial_alpha_reserve); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); // Get current price - let current_price = Pallet::::current_price(netuid); + let current_price_before = Pallet::::current_price(netuid); + + // Get reserves + let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); + let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); + + // Expected fee amount + let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; + let expected_fee = (swap_amount as f64 * fee_rate) as u64; + + // Calculate expected output amount using f64 math + // This is a simple case when w1 = w2 = 0.5, so there's no + // exponentiation needed + let x = alpha_reserve as f64; + let y = tao_reserve as f64; + let expected_output_amount = if price_should_grow { + x * (1.0 - y / (y + (swap_amount - expected_fee) as f64)) + } else { + y * (1.0 - x / (x + (swap_amount - expected_fee) as f64)) + }; // Swap - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); + let limit_price_fixed = U64F64::from_num(limit_price); let swap_result = - Pallet::::do_swap(netuid, order.clone(), sqrt_limit_price, false, false) + Pallet::::do_swap(netuid, order.clone(), limit_price_fixed, false, false) .unwrap(); assert_abs_diff_eq!( swap_result.amount_paid_out.to_u64(), - output_amount, - epsilon = output_amount / 100 + expected_output_amount as u64, + epsilon = 1 ); assert_abs_diff_eq!( swap_result.paid_in_reserve_delta() as u64, - liquidity, - epsilon = liquidity / 10 + (swap_amount - expected_fee), + epsilon = 1 ); assert_abs_diff_eq!( swap_result.paid_out_reserve_delta() as i64, - -(output_amount as i64), - epsilon = output_amount as i64 / 10 - ); - - // Check that low and high ticks' fees were updated properly, and liquidity values were not updated - let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - let expected_liquidity_net_low = tick_low_info_before.liquidity_net; - let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; - let expected_liquidity_net_high = tick_high_info_before.liquidity_net; - let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; - assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); - assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); - assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); - assert_eq!( - tick_high_info.liquidity_gross, - expected_liquidity_gross_high, + -(expected_output_amount as i64), + epsilon = 1 ); - // Expected fee amount - let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - let expected_fee = (liquidity as f64 * fee_rate) as u64; - - // Global fees should be updated - // let actual_global_fee = (order.amount().global_fee(netuid).to_num::() - // * (liquidity_before as f64)) as u64; - - assert!((swap_result.fee_paid.to_u64() as i64 - expected_fee as i64).abs() <= 1); - - // All fees go to block builder - // assert!((actual_global_fee as i64 - expected_fee as i64).abs() <= 1); - - // Tick fees should be updated - - // Liquidity position should not be updated - let protocol_id = Pallet::::protocol_account_id(); - let positions = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - let position = positions.first().unwrap(); - - assert_eq!( - position.liquidity, - helpers_128bit::sqrt( - TaoReserve::reserve(netuid.into()).to_u64() as u128 - * AlphaReserve::reserve(netuid.into()).to_u64() as u128 - ) as u64 - ); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - assert_eq!(position.fees_alpha, 0); - assert_eq!(position.fees_tao, 0); - - // Current liquidity is not updated - assert_eq!(CurrentLiquidity::::get(netuid), liquidity_before); + // Update reserves (because it happens outside of do_swap in stake_utils) + if price_should_grow { + TaoReserve::set_mock_reserve( + netuid, + TaoBalance::from( + (u64::from(initial_tao_reserve) as i128 + + swap_result.paid_in_reserve_delta()) as u64, + ), + ); + AlphaReserve::set_mock_reserve( + netuid, + AlphaBalance::from( + (u64::from(initial_alpha_reserve) as i128 + + swap_result.paid_out_reserve_delta()) as u64, + ), + ); + } else { + TaoReserve::set_mock_reserve( + netuid, + TaoBalance::from( + (u64::from(initial_tao_reserve) as i128 + + swap_result.paid_out_reserve_delta()) as u64, + ), + ); + AlphaReserve::set_mock_reserve( + netuid, + AlphaBalance::from( + (u64::from(initial_alpha_reserve) as i128 + + swap_result.paid_in_reserve_delta()) as u64, + ), + ); + } // Assert that price movement is in correct direction - let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); let current_price_after = Pallet::::current_price(netuid); - assert_eq!(current_price_after >= current_price, price_should_grow); - - // Assert that current tick is updated - let current_tick = CurrentTick::::get(netuid); - let expected_current_tick = - TickIndex::from_sqrt_price_bounded(sqrt_current_price_after); - assert_eq!(current_tick, expected_current_tick); + assert_eq!( + current_price_after >= current_price_before, + price_should_grow + ); } // Current price is 0.25 // Test case is (order_type, liquidity, limit_price, output_amount) - perform_test( - 1.into(), - GetAlphaForTao::with_amount(1_000), - 1000.0, - 3990, - true, - ); + perform_test(1.into(), GetAlphaForTao::with_amount(1_000), 1000.0, true); + perform_test(1.into(), GetAlphaForTao::with_amount(2_000), 1000.0, true); + perform_test(1.into(), GetAlphaForTao::with_amount(123_456), 1000.0, true); + perform_test(2.into(), GetTaoForAlpha::with_amount(1_000), 0.0001, false); + perform_test(2.into(), GetTaoForAlpha::with_amount(2_000), 0.0001, false); perform_test( 2.into(), - GetTaoForAlpha::with_amount(1_000), + GetTaoForAlpha::with_amount(123_456), 0.0001, - 250, false, ); perform_test( 3.into(), - GetAlphaForTao::with_amount(500_000_000), + GetAlphaForTao::with_amount(1_000_000_000), 1000.0, - 2_000_000_000, true, ); - }); -} - -// In this test the swap starts and ends within one (large liquidity) position -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_swap_single_position --exact --show-output -#[test] -fn test_swap_single_position() { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - let netuid = NetUid::from(1); - assert_eq!(max_tick, TickIndex::MAX); - - let mut current_price_low = 0_f64; - let mut current_price_high = 0_f64; - let mut current_price = 0_f64; - new_test_ext().execute_with(|| { - let (low, high) = get_ticked_prices_around_current_price(); - current_price_low = low; - current_price_high = high; - current_price = Pallet::::current_price(netuid).to_num::(); - }); - - macro_rules! perform_test { - ($order_t:ident, - $price_low_offset:expr, - $price_high_offset:expr, - $position_liquidity:expr, - $liquidity_fraction:expr, - $limit_price:expr, - $price_should_grow:expr - ) => { - new_test_ext().execute_with(|| { - let price_low_offset = $price_low_offset; - let price_high_offset = $price_high_offset; - let position_liquidity = $position_liquidity; - let order_liquidity_fraction = $liquidity_fraction; - let limit_price = $limit_price; - let price_should_grow = $price_should_grow; - - ////////////////////////////////////////////// - // Initialize pool and add the user position - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); - let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); - let protocol_liquidity = (tao_reserve as f64 * alpha_reserve as f64).sqrt(); - - // Add liquidity - let current_price = Pallet::::current_price(netuid).to_num::(); - let sqrt_current_price = AlphaSqrtPrice::::get(netuid).to_num::(); - - let price_low = price_low_offset + current_price; - let price_high = price_high_offset + current_price; - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - position_liquidity, - ) - .unwrap(); - - // Liquidity position at correct ticks - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 1 - ); - - // Get tick infos before the swap - let tick_low_info_before = Ticks::::get(netuid, tick_low).unwrap_or_default(); - let tick_high_info_before = - Ticks::::get(netuid, tick_high).unwrap_or_default(); - let liquidity_before = CurrentLiquidity::::get(netuid); - assert_abs_diff_eq!( - liquidity_before as f64, - protocol_liquidity + position_liquidity as f64, - epsilon = liquidity_before as f64 / 1000. - ); - - ////////////////////////////////////////////// - // Swap - - // Calculate the expected output amount for the cornercase of one step - let order_liquidity = order_liquidity_fraction * position_liquidity as f64; - - let output_amount = >::approx_expected_swap_output( - sqrt_current_price, - liquidity_before as f64, - order_liquidity, - ); - - // Do the swap - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - let order = $order_t::with_amount(order_liquidity as u64); - let swap_result = - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - assert_abs_diff_eq!( - swap_result.amount_paid_out.to_u64() as f64, - output_amount, - epsilon = output_amount / 10. - ); - - if order_liquidity_fraction <= 0.001 { - assert_abs_diff_eq!( - swap_result.paid_in_reserve_delta() as i64, - order_liquidity as i64, - epsilon = order_liquidity as i64 / 10 - ); - assert_abs_diff_eq!( - swap_result.paid_out_reserve_delta() as i64, - -(output_amount as i64), - epsilon = output_amount as i64 / 10 - ); - } - - // Assert that price movement is in correct direction - let current_price_after = Pallet::::current_price(netuid); - assert_eq!(price_should_grow, current_price_after > current_price); - - // Assert that for small amounts price stays within the user position - if (order_liquidity_fraction <= 0.001) - && (price_low_offset > 0.0001) - && (price_high_offset > 0.0001) - { - assert!(current_price_after <= price_high); - assert!(current_price_after >= price_low); - } - - // Check that low and high ticks' fees were updated properly - let tick_low_info = Ticks::::get(netuid, tick_low).unwrap(); - let tick_high_info = Ticks::::get(netuid, tick_high).unwrap(); - let expected_liquidity_net_low = tick_low_info_before.liquidity_net; - let expected_liquidity_gross_low = tick_low_info_before.liquidity_gross; - let expected_liquidity_net_high = tick_high_info_before.liquidity_net; - let expected_liquidity_gross_high = tick_high_info_before.liquidity_gross; - assert_eq!(tick_low_info.liquidity_net, expected_liquidity_net_low,); - assert_eq!(tick_low_info.liquidity_gross, expected_liquidity_gross_low,); - assert_eq!(tick_high_info.liquidity_net, expected_liquidity_net_high,); - assert_eq!( - tick_high_info.liquidity_gross, - expected_liquidity_gross_high, - ); - - // Expected fee amount - do not test, all fees go to block builder - // let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - // let expected_fee = (order_liquidity - order_liquidity / (1.0 + fee_rate)) as u64; - - // // // Global fees should be updated - // let actual_global_fee = ($order_t::with_amount(0) - // .amount() - // .global_fee(netuid) - // .to_num::() - // * (liquidity_before as f64)) as u64; - - // assert_abs_diff_eq!( - // swap_result.fee_paid.to_u64(), - // expected_fee, - // epsilon = expected_fee / 10 - // ); - // assert_abs_diff_eq!(actual_global_fee, expected_fee, epsilon = expected_fee / 10); - - // Tick fees should be updated - - // Liquidity position should not be updated - let positions = - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .collect::>(); - let position = positions.first().unwrap(); - - assert_eq!(position.liquidity, position_liquidity,); - assert_eq!(position.tick_low, tick_low); - assert_eq!(position.tick_high, tick_high); - assert_eq!(position.fees_alpha, 0); - assert_eq!(position.fees_tao, 0); - }); - }; - } - - // Current price is 0.25 - // The test case is based on the current price and position prices are defined as a price - // offset from the current price - // Outer part of test case is Position: (price_low_offset, price_high_offset, liquidity) - [ - // Very localized position at the current price - (-0.1, 0.1, 500_000_000_000_u64), - // Repeat the protocol liquidity at maximum range - ( - min_price - current_price, - max_price - current_price, - 2_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range - ( - current_price_high - current_price, - max_price - current_price, - 2_000_000_000_u64, - ), - // Repeat the protocol liquidity at min to current range - ( - min_price - current_price, - current_price_low - current_price, - 2_000_000_000_u64, - ), - // Half to double price - (-0.125, 0.25, 2_000_000_000_u64), - // A few other price ranges and liquidity volumes - (-0.1, 0.1, 2_000_000_000_u64), - (-0.1, 0.1, 10_000_000_000_u64), - (-0.1, 0.1, 100_000_000_000_u64), - (-0.01, 0.01, 100_000_000_000_u64), - (-0.001, 0.001, 100_000_000_000_u64), - ] - .into_iter() - .for_each( - |(price_low_offset, price_high_offset, position_liquidity)| { - // Inner part of test case is Order: (order_type, order_liquidity, limit_price) - // order_liquidity is represented as a fraction of position_liquidity - for liquidity_fraction in [0.0001, 0.001, 0.01, 0.1, 0.2, 0.5] { - perform_test!( - GetAlphaForTao, - price_low_offset, - price_high_offset, - position_liquidity, - liquidity_fraction, - 1000.0_f64, - true - ); - perform_test!( - GetTaoForAlpha, - price_low_offset, - price_high_offset, - position_liquidity, - liquidity_fraction, - 0.0001_f64, - false - ); - } - }, - ); -} - -// This test is a sanity check for swap and multiple positions -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_multiple_positions --exact --show-output --nocapture -#[test] -fn test_swap_multiple_positions() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let max_tick = price_to_tick(max_price); - let netuid = NetUid::from(1); - assert_eq!(max_tick, TickIndex::MAX); - - ////////////////////////////////////////////// - // Initialize pool and add the user position - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add liquidity - let current_price = Pallet::::current_price(netuid).to_num::(); - - // Current price is 0.25 - // All positions below are placed at once - [ - // Very localized position at the current price - (-0.1, 0.1, 500_000_000_000_u64), - // Repeat the protocol liquidity at maximum range - ( - min_price - current_price, - max_price - current_price, - 2_000_000_000_u64, - ), - // Repeat the protocol liquidity at current to max range - (0.0, max_price - current_price, 2_000_000_000_u64), - // Repeat the protocol liquidity at min to current range - (min_price - current_price, 0.0, 2_000_000_000_u64), - // Half to double price - (-0.125, 0.25, 2_000_000_000_u64), - // A few other price ranges and liquidity volumes - (-0.1, 0.1, 2_000_000_000_u64), - (-0.1, 0.1, 10_000_000_000_u64), - (-0.1, 0.1, 100_000_000_000_u64), - (-0.01, 0.01, 100_000_000_000_u64), - (-0.001, 0.001, 100_000_000_000_u64), - // A few (overlapping) positions up the range - (0.01, 0.02, 100_000_000_000_u64), - (0.02, 0.03, 100_000_000_000_u64), - (0.03, 0.04, 100_000_000_000_u64), - (0.03, 0.05, 100_000_000_000_u64), - // A few (overlapping) positions down the range - (-0.02, -0.01, 100_000_000_000_u64), - (-0.03, -0.02, 100_000_000_000_u64), - (-0.04, -0.03, 100_000_000_000_u64), - (-0.05, -0.03, 100_000_000_000_u64), - ] - .into_iter() - .for_each( - |(price_low_offset, price_high_offset, position_liquidity)| { - let price_low = price_low_offset + current_price; - let price_high = price_high_offset + current_price; - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - let (_position_id, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - position_liquidity, - ) - .unwrap(); - }, + perform_test( + 3.into(), + GetAlphaForTao::with_amount(10_000_000_000_u64), + 1000.0, + true, ); - - macro_rules! perform_test { - ($order_t:ident, $order_liquidity:expr, $limit_price:expr, $should_price_grow:expr) => { - ////////////////////////////////////////////// - // Swap - let order_liquidity = $order_liquidity; - let limit_price = $limit_price; - let should_price_grow = $should_price_grow; - - let sqrt_current_price = AlphaSqrtPrice::::get(netuid); - let current_price = (sqrt_current_price * sqrt_current_price).to_num::(); - let liquidity_before = CurrentLiquidity::::get(netuid); - let output_amount = >::approx_expected_swap_output( - sqrt_current_price.to_num(), - liquidity_before as f64, - order_liquidity as f64, - ); - - // Do the swap - let sqrt_limit_price = SqrtPrice::from_num((limit_price).sqrt()); - let order = $order_t::with_amount(order_liquidity); - let swap_result = - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - assert_abs_diff_eq!( - swap_result.amount_paid_out.to_u64() as f64, - output_amount, - epsilon = output_amount / 10. - ); - - let tao_reserve = TaoReserve::reserve(netuid.into()).to_u64(); - let alpha_reserve = AlphaReserve::reserve(netuid.into()).to_u64(); - let output_amount = output_amount as u64; - - assert!(output_amount > 0); - - if alpha_reserve > order_liquidity && tao_reserve > order_liquidity { - assert_abs_diff_eq!( - swap_result.paid_in_reserve_delta() as i64, - order_liquidity as i64, - epsilon = order_liquidity as i64 / 100 - ); - assert_abs_diff_eq!( - swap_result.paid_out_reserve_delta() as i64, - -(output_amount as i64), - epsilon = output_amount as i64 / 100 - ); - } - - // Assert that price movement is in correct direction - let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - let current_price_after = - (sqrt_current_price_after * sqrt_current_price_after).to_num::(); - assert_eq!(should_price_grow, current_price_after > current_price); - }; - } - - // All these orders are executed without swap reset - for order_liquidity in [ - (100_000_u64), - (1_000_000), - (10_000_000), - (100_000_000), - (200_000_000), - (500_000_000), - (1_000_000_000), - (10_000_000_000), - ] { - perform_test!(GetAlphaForTao, order_liquidity, 1000.0_f64, true); - perform_test!(GetTaoForAlpha, order_liquidity, 0.0001_f64, false); - } - - // Current price shouldn't be much different from the original - let sqrt_current_price_after = AlphaSqrtPrice::::get(netuid); - let current_price_after = - (sqrt_current_price_after * sqrt_current_price_after).to_num::(); - assert_abs_diff_eq!( - current_price, - current_price_after, - epsilon = current_price / 10. - ) }); } // cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_swap_precision_edge_case --exact --show-output #[test] fn test_swap_precision_edge_case() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(123); // 123 is netuid with low edge case liquidity - let order = GetTaoForAlpha::with_amount(1_000_000_000_000_000_000_u64); - let tick_low = TickIndex::MIN; - - let sqrt_limit_price: SqrtPrice = tick_low.try_to_sqrt_price().unwrap(); - - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Swap - let swap_result = - Pallet::::do_swap(netuid, order, sqrt_limit_price, false, true).unwrap(); - - assert!(swap_result.amount_paid_out > TaoBalance::ZERO); - }); -} - -// cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_price_tick_price_roundtrip --exact --show-output -#[test] -fn test_price_tick_price_roundtrip() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); + // Test case: tao_reserve, alpha_reserve, swap_amount + [ + (1_000_u64, 1_000_u64, 999_500_u64), + (1_000_000_u64, 1_000_000_u64, 999_500_000_u64), + ] + .into_iter() + .for_each(|(tao_reserve, alpha_reserve, swap_amount)| { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + let order = GetTaoForAlpha::with_amount(swap_amount); - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); + // Very low reserves + TaoReserve::set_mock_reserve(netuid, TaoBalance::from(tao_reserve)); + AlphaReserve::set_mock_reserve(netuid, AlphaBalance::from(alpha_reserve)); - let current_price = SqrtPrice::from_num(0.500_000_512_192_122_7); - let tick = TickIndex::try_from_sqrt_price(current_price).unwrap(); + // Minimum possible limit price + let limit_price: U64F64 = get_min_price(); + println!("limit_price = {:?}", limit_price); - let round_trip_price = TickIndex::try_to_sqrt_price(&tick).unwrap(); - assert!(round_trip_price <= current_price); + // Swap + let swap_result = + Pallet::::do_swap(netuid, order, limit_price, false, true).unwrap(); - let roundtrip_tick = TickIndex::try_from_sqrt_price(round_trip_price).unwrap(); - assert!(tick == roundtrip_tick); + assert!(swap_result.amount_paid_out > TaoBalance::ZERO); + }); }); } #[test] fn test_convert_deltas() { new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - for (sqrt_price, delta_in, expected_buy, expected_sell) in [ - (SqrtPrice::from_num(1.5), 1, 0, 2), - (SqrtPrice::from_num(1.5), 10000, 4444, 22500), - (SqrtPrice::from_num(1.5), 1000000, 444444, 2250000), - ( - SqrtPrice::from_num(1.5), - u64::MAX, - 2000000000000, - 3000000000000, - ), - ( - TickIndex::MIN.as_sqrt_price_bounded(), - 1, - 18406523739291577836, - 465, - ), - (TickIndex::MIN.as_sqrt_price_bounded(), 10000, u64::MAX, 465), - ( - TickIndex::MIN.as_sqrt_price_bounded(), - 1000000, - u64::MAX, - 465, - ), - ( - TickIndex::MIN.as_sqrt_price_bounded(), - u64::MAX, - u64::MAX, - 464, - ), - ( - TickIndex::MAX.as_sqrt_price_bounded(), - 1, - 0, - 18406523745214495085, - ), - (TickIndex::MAX.as_sqrt_price_bounded(), 10000, 0, u64::MAX), - (TickIndex::MAX.as_sqrt_price_bounded(), 1000000, 0, u64::MAX), - ( - TickIndex::MAX.as_sqrt_price_bounded(), - u64::MAX, - 2000000000000, - u64::MAX, - ), + for (tao, alpha, w_quote, delta_in) in [ + (1500, 1000, 0.5, 1), + (1500, 1000, 0.5, 10000), + (1500, 1000, 0.5, 1000000), + (1500, 1000, 0.5, u64::MAX), + (1, 1000000, 0.5, 1), + (1, 1000000, 0.5, 10000), + (1, 1000000, 0.5, 1000000), + (1, 1000000, 0.5, u64::MAX), + (1000000, 1, 0.5, 1), + (1000000, 1, 0.5, 10000), + (1000000, 1, 0.5, 1000000), + (1000000, 1, 0.5, u64::MAX), + (1500, 1000, 0.50000001, 1), + (1500, 1000, 0.50000001, 10000), + (1500, 1000, 0.50000001, 1000000), + (1500, 1000, 0.50000001, u64::MAX), + (1, 1000000, 0.50000001, 1), + (1, 1000000, 0.50000001, 10000), + (1, 1000000, 0.50000001, 1000000), + (1, 1000000, 0.50000001, u64::MAX), + (1000000, 1, 0.50000001, 1), + (1000000, 1, 0.50000001, 10000), + (1000000, 1, 0.50000001, 1000000), + (1000000, 1, 0.50000001, u64::MAX), + (1500, 1000, 0.49999999, 1), + (1500, 1000, 0.49999999, 10000), + (1500, 1000, 0.49999999, 1000000), + (1500, 1000, 0.49999999, u64::MAX), + (1, 1000000, 0.49999999, 1), + (1, 1000000, 0.49999999, 10000), + (1, 1000000, 0.49999999, 1000000), + (1, 1000000, 0.49999999, u64::MAX), + (1000000, 1, 0.49999999, 1), + (1000000, 1, 0.49999999, 10000), + (1000000, 1, 0.49999999, 1000000), + (1000000, 1, 0.49999999, u64::MAX), + // Low quote weight + (1500, 1000, 0.1, 1), + (1500, 1000, 0.1, 10000), + (1500, 1000, 0.1, 1000000), + (1500, 1000, 0.1, u64::MAX), + (1, 1000000, 0.1, 1), + (1, 1000000, 0.1, 10000), + (1, 1000000, 0.1, 1000000), + (1, 1000000, 0.1, u64::MAX), + (1000000, 1, 0.1, 1), + (1000000, 1, 0.1, 10000), + (1000000, 1, 0.1, 1000000), + (1000000, 1, 0.1, u64::MAX), + // High quote weight + (1500, 1000, 0.9, 1), + (1500, 1000, 0.9, 10000), + (1500, 1000, 0.9, 1000000), + (1500, 1000, 0.9, u64::MAX), + (1, 1000000, 0.9, 1), + (1, 1000000, 0.9, 10000), + (1, 1000000, 0.9, 1000000), + (1, 1000000, 0.9, u64::MAX), + (1000000, 1, 0.9, 1), + (1000000, 1, 0.9, 10000), + (1000000, 1, 0.9, 1000000), + (1000000, 1, 0.9, u64::MAX), ] { - { - AlphaSqrtPrice::::insert(netuid, sqrt_price); + // Initialize reserves and weights + let netuid = NetUid::from(1); + TaoReserve::set_mock_reserve(netuid, TaoBalance::from(tao)); + AlphaReserve::set_mock_reserve(netuid, AlphaBalance::from(alpha)); + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); + + let w_accuracy = 1_000_000_000_f64; + let w_quote_pt = + Perquintill::from_rational((w_quote * w_accuracy) as u128, w_accuracy as u128); + let bal = Balancer::new(w_quote_pt).unwrap(); + SwapBalancer::::insert(netuid, bal); + + // Calculate expected swap results (buy and sell) using f64 math + let y = tao as f64; + let x = alpha as f64; + let d = delta_in as f64; + let w1_div_w2 = (1. - w_quote) / w_quote; + let w2_div_w1 = w_quote / (1. - w_quote); + let expected_sell = y * (1. - (x / (x + d)).powf(w1_div_w2)); + let expected_buy = x * (1. - (y / (y + d)).powf(w2_div_w1)); - assert_abs_diff_eq!( + assert_abs_diff_eq!( + u64::from( BasicSwapStep::::convert_deltas( netuid, delta_in.into() - ), - expected_sell.into(), - epsilon = 2.into() - ); - assert_abs_diff_eq!( + ) + ), + expected_sell as u64, + epsilon = 2u64 + ); + assert_abs_diff_eq!( + u64::from( BasicSwapStep::::convert_deltas( netuid, delta_in.into() - ), - expected_buy.into(), - epsilon = 2.into() - ); - } + ) + ), + expected_buy as u64, + epsilon = 2u64 + ); } }); } -// #[test] -// fn test_user_liquidity_disabled() { -// new_test_ext().execute_with(|| { -// // Use a netuid above 100 since our mock enables liquidity for 0-100 -// let netuid = NetUid::from(101); -// let tick_low = TickIndex::new_unchecked(-1000); -// let tick_high = TickIndex::new_unchecked(1000); -// let position_id = PositionId::from(1); -// let liquidity = 1_000_000_000; -// let liquidity_delta = 500_000_000; - -// assert!(!EnabledUserLiquidity::::get(netuid)); - -// assert_noop!( -// Swap::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity -// ), -// Error::::UserLiquidityDisabled -// ); - -// assert_noop!( -// Swap::do_remove_liquidity(netuid, &OK_COLDKEY_ACCOUNT_ID, position_id), -// Error::::LiquidityNotFound -// ); - -// assert_noop!( -// Swap::modify_position( -// RuntimeOrigin::signed(OK_COLDKEY_ACCOUNT_ID), -// OK_HOTKEY_ACCOUNT_ID, -// netuid, -// position_id, -// liquidity_delta -// ), -// Error::::UserLiquidityDisabled -// ); - -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid, -// true -// )); - -// let position_id = Swap::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap() -// .0; - -// assert_ok!(Swap::do_modify_position( -// netuid.into(), -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// position_id, -// liquidity_delta, -// )); - -// assert_ok!(Swap::do_remove_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// position_id, -// )); -// }); -// } - -// This test is pointless: All fees go to block author -// Test correctness of swap fees: -// - Fees are distribued to (concentrated) liquidity providers -// -// #[test] -// fn test_swap_fee_correctness() { -// new_test_ext().execute_with(|| { -// let min_price = tick_to_price(TickIndex::MIN); -// let max_price = tick_to_price(TickIndex::MAX); -// let netuid = NetUid::from(1); - -// // Provide very spread liquidity at the range from min to max that matches protocol liquidity -// let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - -// assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - -// // Calculate ticks -// let tick_low = price_to_tick(min_price); -// let tick_high = price_to_tick(max_price); - -// // Add user liquidity -// let (position_id, _tao, _alpha) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .unwrap(); - -// // Swap buy and swap sell -// Pallet::::do_swap( -// netuid, -// GetAlphaForTao::with_amount(liquidity / 10), -// u64::MAX.into(), -// false, -// false, -// ) -// .unwrap(); -// Pallet::::do_swap( -// netuid, -// GetTaoForAlpha::with_amount(liquidity / 10), -// 0_u64.into(), -// false, -// false, -// ) -// .unwrap(); - -// // Get user position -// let mut position = -// Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID, position_id)).unwrap(); -// assert_eq!(position.liquidity, liquidity); -// assert_eq!(position.tick_low, tick_low); -// assert_eq!(position.tick_high, tick_high); - -// // Check that 50% of fees were credited to the position -// let fee_rate = FeeRate::::get(NetUid::from(netuid)) as f64 / u16::MAX as f64; -// let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); -// let expected_fee = (fee_rate * (liquidity / 10) as f64 * 0.5) as u64; - -// assert_abs_diff_eq!(actual_fee_tao, expected_fee, epsilon = 1,); -// assert_abs_diff_eq!(actual_fee_alpha, expected_fee, epsilon = 1,); -// }); -// } - -#[test] -fn test_current_liquidity_updates() { - let netuid = NetUid::from(1); - let liquidity = 1_000_000_000; - - // Get current price - let (current_price, current_price_low, current_price_high) = - new_test_ext().execute_with(|| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - let sqrt_current_price = AlphaSqrtPrice::::get(netuid); - let current_price = (sqrt_current_price * sqrt_current_price).to_num::(); - let (current_price_low, current_price_high) = get_ticked_prices_around_current_price(); - (current_price, current_price_low, current_price_high) - }); - - // Test case: (price_low, price_high, expect_to_update) - [ - // Current price is out of position range (lower), no current lq update - (current_price * 2., current_price * 3., false), - // Current price is out of position range (higher), no current lq update - (current_price / 3., current_price / 2., false), - // Current price is just below position range, no current lq update - (current_price_high, current_price * 3., false), - // Position lower edge is just below the current price, current lq updates - (current_price_low, current_price * 3., true), - // Current price is exactly at lower edge of position range, current lq updates - (current_price, current_price * 3., true), - // Current price is exactly at higher edge of position range, no current lq update - (current_price / 2., current_price, false), - ] - .into_iter() - .for_each(|(price_low, price_high, expect_to_update)| { - new_test_ext().execute_with(|| { - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Calculate ticks (assuming tick math is tested separately) - let tick_low = price_to_tick(price_low); - let tick_high = price_to_tick(price_high); - let liquidity_before = CurrentLiquidity::::get(netuid); - - // Add liquidity - assert_ok!(Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - )); - - // Current liquidity is updated only when price range includes the current price - let expected_liquidity = if (price_high > current_price) && (price_low <= current_price) - { - assert!(expect_to_update); - liquidity_before + liquidity - } else { - assert!(!expect_to_update); - liquidity_before - }; - - assert_eq!(CurrentLiquidity::::get(netuid), expected_liquidity) - }); - }); -} - #[test] fn test_rollback_works() { new_test_ext().execute_with(|| { @@ -1588,794 +721,48 @@ fn test_rollback_works() { }) } -/// Test correctness of swap fees: -/// - New LP is not eligible to previously accrued fees -/// -/// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_new_lp_doesnt_get_old_fees --exact --show-output -#[test] -fn test_new_lp_doesnt_get_old_fees() { - new_test_ext().execute_with(|| { - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let netuid = NetUid::from(1); - - // Provide very spread liquidity at the range from min to max that matches protocol liquidity - let liquidity = 2_000_000_000_000_u64; // 1x of protocol liquidity - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Calculate ticks - let tick_low = price_to_tick(min_price); - let tick_high = price_to_tick(max_price); - - // Add user liquidity - Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .unwrap(); - - // Swap buy and swap sell - Pallet::::do_swap( - netuid, - GetAlphaForTao::with_amount(liquidity / 10), - u64::MAX.into(), - false, - false, - ) - .unwrap(); - Pallet::::do_swap( - netuid, - GetTaoForAlpha::with_amount(liquidity / 10), - 0_u64.into(), - false, - false, - ) - .unwrap(); - - // Add liquidity from a different user to a new tick - let (position_id_2, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID_2, - &OK_HOTKEY_ACCOUNT_ID_2, - tick_low.next().unwrap(), - tick_high.prev().unwrap(), - liquidity, - ) - .unwrap(); - - // Get user position - let mut position = - Positions::::get((netuid, OK_COLDKEY_ACCOUNT_ID_2, position_id_2)).unwrap(); - assert_eq!(position.liquidity, liquidity); - assert_eq!(position.tick_low, tick_low.next().unwrap()); - assert_eq!(position.tick_high, tick_high.prev().unwrap()); - - // Check that collected fees are 0 - let (actual_fee_tao, actual_fee_alpha) = position.collect_fees(); - assert_abs_diff_eq!(actual_fee_tao, 0, epsilon = 1); - assert_abs_diff_eq!(actual_fee_alpha, 0, epsilon = 1); - }); -} - -// fn bbox(t: U64F64, a: U64F64, b: U64F64) -> U64F64 { -// if t < a { -// a -// } else if t > b { -// b -// } else { -// t -// } -// } - -// fn print_current_price(netuid: NetUid) { -// let current_sqrt_price = AlphaSqrtPrice::::get(netuid).to_num::(); -// let current_price = current_sqrt_price * current_sqrt_price; -// log::trace!("Current price: {current_price:.6}"); -// } - -// All fees go to block builder -// RUST_LOG=pallet_subtensor_swap=trace cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_wrapping_fees --exact --show-output --nocapture -// #[test] -// fn test_wrapping_fees() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(WRAPPING_FEES_NETUID); -// let position_1_low_price = 0.20; -// let position_1_high_price = 0.255; -// let position_2_low_price = 0.255; -// let position_2_high_price = 0.257; -// assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - -// Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// price_to_tick(position_1_low_price), -// price_to_tick(position_1_high_price), -// 1_000_000_000_u64, -// ) -// .unwrap(); - -// print_current_price(netuid); - -// let order = GetTaoForAlpha::with_amount(800_000_000); -// let sqrt_limit_price = SqrtPrice::from_num(0.000001); -// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - -// let order = GetAlphaForTao::with_amount(1_850_000_000); -// let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); - -// print_current_price(netuid); - -// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); - -// print_current_price(netuid); - -// let add_liquidity_result = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// &OK_COLDKEY_ACCOUNT_ID_RICH, -// price_to_tick(position_2_low_price), -// price_to_tick(position_2_high_price), -// 1_000_000_000_u64, -// ) -// .unwrap(); - -// let order = GetTaoForAlpha::with_amount(1_800_000_000); -// let sqrt_limit_price = SqrtPrice::from_num(0.000001); - -// let initial_sqrt_price = AlphaSqrtPrice::::get(netuid); -// Pallet::::do_swap(netuid, order, sqrt_limit_price, false, false).unwrap(); -// let final_sqrt_price = AlphaSqrtPrice::::get(netuid); - -// print_current_price(netuid); - -// let mut position = -// Positions::::get((netuid, &OK_COLDKEY_ACCOUNT_ID_RICH, add_liquidity_result.0)) -// .unwrap(); - -// let initial_box_price = bbox( -// initial_sqrt_price, -// position.tick_low.try_to_sqrt_price().unwrap(), -// position.tick_high.try_to_sqrt_price().unwrap(), -// ); - -// let final_box_price = bbox( -// final_sqrt_price, -// position.tick_low.try_to_sqrt_price().unwrap(), -// position.tick_high.try_to_sqrt_price().unwrap(), -// ); - -// let fee_rate = FeeRate::::get(netuid) as f64 / u16::MAX as f64; - -// log::trace!("fee_rate: {fee_rate:.6}"); -// log::trace!("position.liquidity: {}", position.liquidity); -// log::trace!( -// "initial_box_price: {:.6}", -// initial_box_price.to_num::() -// ); -// log::trace!("final_box_price: {:.6}", final_box_price.to_num::()); - -// let expected_fee_tao = ((fee_rate / (1.0 - fee_rate)) -// * (position.liquidity as f64) -// * (final_box_price.to_num::() - initial_box_price.to_num::())) -// as u64; - -// let expected_fee_alpha = ((fee_rate / (1.0 - fee_rate)) -// * (position.liquidity as f64) -// * ((1.0 / final_box_price.to_num::()) - (1.0 / initial_box_price.to_num::()))) -// as u64; - -// log::trace!("Expected ALPHA fee: {:.6}", expected_fee_alpha as f64); - -// let (fee_tao, fee_alpha) = position.collect_fees(); - -// log::trace!("Collected fees: TAO: {fee_tao}, ALPHA: {fee_alpha}"); - -// assert_abs_diff_eq!(fee_tao, expected_fee_tao, epsilon = 1); -// assert_abs_diff_eq!(fee_alpha, expected_fee_alpha, epsilon = 1); -// }); -// } - -/// Test that price moves less with provided liquidity -/// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_less_price_movement --exact --show-output -#[test] -fn test_less_price_movement() { - let netuid = NetUid::from(1); - let mut last_end_price = U96F32::from_num(0); - let initial_stake_liquidity = 1_000_000_000; - let swapped_liquidity = 1_000_000; - - // Test case is (order_type, provided_liquidity) - // Testing algorithm: - // - Stake initial_stake_liquidity - // - Provide liquidity if iteration provides lq - // - Buy or sell - // - Save end price if iteration doesn't provide lq - macro_rules! perform_test { - ($order_t:ident, $provided_liquidity:expr, $limit_price:expr, $should_price_shrink:expr) => { - let provided_liquidity = $provided_liquidity; - let should_price_shrink = $should_price_shrink; - let limit_price = $limit_price; - new_test_ext().execute_with(|| { - // Setup swap - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Buy Alpha - assert_ok!(Pallet::::do_swap( - netuid, - GetAlphaForTao::with_amount(initial_stake_liquidity), - SqrtPrice::from_num(10_000_000_000_u64), - false, - false - )); - - // Get current price - let start_price = Pallet::::current_price(netuid); - - // Add liquidity if this test iteration provides - if provided_liquidity > 0 { - let tick_low = price_to_tick(start_price.to_num::() * 0.5); - let tick_high = price_to_tick(start_price.to_num::() * 1.5); - assert_ok!(Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - provided_liquidity, - )); - } - - // Swap - let sqrt_limit_price = SqrtPrice::from_num(limit_price); - assert_ok!(Pallet::::do_swap( - netuid, - $order_t::with_amount(swapped_liquidity), - sqrt_limit_price, - false, - false - )); - - let end_price = Pallet::::current_price(netuid); - - // Save end price if iteration doesn't provide or compare with previous end price if - // it does - if provided_liquidity > 0 { - assert_eq!(should_price_shrink, end_price < last_end_price); - } else { - last_end_price = end_price; - } - }); - }; - } - - for provided_liquidity in [0, 1_000_000_000_000_u64] { - perform_test!(GetAlphaForTao, provided_liquidity, 1000.0_f64, true); - } - for provided_liquidity in [0, 1_000_000_000_000_u64] { - perform_test!(GetTaoForAlpha, provided_liquidity, 0.001_f64, false); +#[allow(dead_code)] +fn bbox(t: U64F64, a: U64F64, b: U64F64) -> U64F64 { + if t < a { + a + } else if t > b { + b + } else { + t } } -// TODO: Revise when user liquidity is available -// #[test] -// fn test_swap_subtoken_disabled() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(SUBTOKEN_DISABLED_NETUID); // Use a netuid not used elsewhere -// let price_low = 0.1; -// let price_high = 0.2; -// let tick_low = price_to_tick(price_low); -// let tick_high = price_to_tick(price_high); -// let liquidity = 1_000_000_u64; - -// assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - -// assert_noop!( -// Pallet::::add_liquidity( -// RuntimeOrigin::signed(OK_COLDKEY_ACCOUNT_ID), -// OK_HOTKEY_ACCOUNT_ID, -// netuid, -// tick_low, -// tick_high, -// liquidity, -// ), -// Error::::SubtokenDisabled -// ); - -// assert_noop!( -// Pallet::::modify_position( -// RuntimeOrigin::signed(OK_COLDKEY_ACCOUNT_ID), -// OK_HOTKEY_ACCOUNT_ID, -// netuid, -// PositionId::from(0), -// liquidity as i64, -// ), -// Error::::SubtokenDisabled -// ); -// }); -// } - -#[test] -fn test_liquidate_v3_removes_positions_ticks_and_state() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - // Initialize V3 (creates protocol position, ticks, price, liquidity) - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - assert!(SwapV3Initialized::::get(netuid)); - - // Enable user LP - assert_ok!(Swap::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid.into(), - true - )); - - // Add a user position across the full range to ensure ticks/bitmap are populated. - let min_price = tick_to_price(TickIndex::MIN); - let max_price = tick_to_price(TickIndex::MAX); - let tick_low = price_to_tick(min_price); - let tick_high = price_to_tick(max_price); - let liquidity = 2_000_000_000_u64; - - let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - liquidity, - ) - .expect("add liquidity"); - - // Accrue some global fees so we can verify fee storage is cleared later. - let sqrt_limit_price = SqrtPrice::from_num(1_000_000.0); - assert_ok!(Pallet::::do_swap( - netuid, - GetAlphaForTao::with_amount(1_000_000), - sqrt_limit_price, - false, - false - )); - - // Sanity: protocol & user positions exist, ticks exist, liquidity > 0 - let protocol_id = Pallet::::protocol_account_id(); - let prot_positions = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!(!prot_positions.is_empty()); - - let user_positions = Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .collect::>(); - assert_eq!(user_positions.len(), 1); - - assert!(Ticks::::get(netuid, TickIndex::MIN).is_some()); - assert!(Ticks::::get(netuid, TickIndex::MAX).is_some()); - assert!(CurrentLiquidity::::get(netuid) > 0); - - let had_bitmap_words = TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_some(); - assert!(had_bitmap_words); - - // ACT: users-only liquidation then protocol clear - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // ASSERT: positions cleared (both user and protocol) - assert_eq!( - Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), - 0 - ); - let prot_positions_after = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!(prot_positions_after.is_empty()); - let user_positions_after = - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .collect::>(); - assert!(user_positions_after.is_empty()); - - // ASSERT: ticks cleared - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!(Ticks::::get(netuid, TickIndex::MIN).is_none()); - assert!(Ticks::::get(netuid, TickIndex::MAX).is_none()); - - // ASSERT: fee globals cleared - assert!(!FeeGlobalTao::::contains_key(netuid)); - assert!(!FeeGlobalAlpha::::contains_key(netuid)); - - // ASSERT: price/tick/liquidity flags cleared - assert!(!AlphaSqrtPrice::::contains_key(netuid)); - assert!(!CurrentTick::::contains_key(netuid)); - assert!(!CurrentLiquidity::::contains_key(netuid)); - assert!(!SwapV3Initialized::::contains_key(netuid)); - - // ASSERT: active tick bitmap cleared - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - - // ASSERT: knobs removed on dereg - assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); - }); +#[allow(dead_code)] +fn print_current_price(netuid: NetUid) { + let current_price = Pallet::::current_price(netuid); + log::trace!("Current price: {current_price:.6}"); } -// V3 path with user liquidity disabled at teardown: -// must still remove positions and clear state (after protocol clear). -// #[test] -// fn test_liquidate_v3_with_user_liquidity_disabled() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(101); - -// assert_ok!(Pallet::::maybe_initialize_v3(netuid)); -// assert!(SwapV3Initialized::::get(netuid)); - -// // Enable temporarily to add a user position -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid.into(), -// true -// )); - -// let min_price = tick_to_price(TickIndex::MIN); -// let max_price = tick_to_price(TickIndex::MAX); -// let tick_low = price_to_tick(min_price); -// let tick_high = price_to_tick(max_price); -// let liquidity = 1_000_000_000_u64; - -// let (_pos_id, _tao, _alpha) = Pallet::::do_add_liquidity( -// netuid, -// &OK_COLDKEY_ACCOUNT_ID, -// &OK_HOTKEY_ACCOUNT_ID, -// tick_low, -// tick_high, -// liquidity, -// ) -// .expect("add liquidity"); - -// // Disable user LP *before* liquidation; removal must ignore this flag. -// assert_ok!(Swap::toggle_user_liquidity( -// RuntimeOrigin::root(), -// netuid.into(), -// false -// )); - -// // Users-only dissolve, then clear protocol liquidity/state. -// assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); -// assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - -// // ASSERT: positions & ticks gone, state reset -// assert_eq!( -// Pallet::::count_positions(netuid, &OK_COLDKEY_ACCOUNT_ID), -// 0 -// ); -// assert!( -// Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) -// .next() -// .is_none() -// ); -// assert!(Ticks::::iter_prefix(netuid).next().is_none()); -// assert!( -// TickIndexBitmapWords::::iter_prefix((netuid,)) -// .next() -// .is_none() -// ); -// assert!(!SwapV3Initialized::::contains_key(netuid)); -// assert!(!AlphaSqrtPrice::::contains_key(netuid)); -// assert!(!CurrentTick::::contains_key(netuid)); -// assert!(!CurrentLiquidity::::contains_key(netuid)); -// assert!(!FeeGlobalTao::::contains_key(netuid)); -// assert!(!FeeGlobalAlpha::::contains_key(netuid)); - -// // `EnabledUserLiquidity` is removed by protocol clear stage. -// assert!(!EnabledUserLiquidity::::contains_key(netuid)); -// }); -// } - -/// Non‑V3 path: V3 not initialized (no positions); function must still clear any residual storages and succeed. +/// Simple palswap path: PalSwap is initialized, but no positions, only protocol; function +/// must still clear any residual storages and succeed. +/// TODO: Revise when user liquidity is available #[test] -fn test_liquidate_non_v3_uninitialized_ok_and_clears() { +fn test_liquidate_pal_simple_ok_and_clears() { new_test_ext().execute_with(|| { let netuid = NetUid::from(202); - // Sanity: V3 is not initialized - assert!(!SwapV3Initialized::::get(netuid)); - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); + // Insert map values + FeeRate::::insert(netuid, 1_000); + PalSwapInitialized::::insert(netuid, true); + let w_quote_pt = Perquintill::from_rational(1u128, 2u128); + let bal = Balancer::new(w_quote_pt).unwrap(); + SwapBalancer::::insert(netuid, bal); - // ACT - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); + // Sanity: PalSwap is not initialized + assert!(PalSwapInitialized::::get(netuid)); - // ASSERT: Defensive clears leave no residues and do not panic - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); + // ACT + assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); // All single-key maps should not have the key after liquidation - assert!(!FeeGlobalTao::::contains_key(netuid)); - assert!(!FeeGlobalAlpha::::contains_key(netuid)); - assert!(!CurrentLiquidity::::contains_key(netuid)); - assert!(!CurrentTick::::contains_key(netuid)); - assert!(!AlphaSqrtPrice::::contains_key(netuid)); - assert!(!SwapV3Initialized::::contains_key(netuid)); assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); - }); -} - -#[test] -fn test_liquidate_idempotent() { - // V3 flavor - new_test_ext().execute_with(|| { - let netuid = NetUid::from(7); - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Add a small user position - assert_ok!(Swap::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid.into(), - true - )); - let tick_low = price_to_tick(0.2); - let tick_high = price_to_tick(0.3); - assert_ok!(Pallet::::do_add_liquidity( - netuid, - &OK_COLDKEY_ACCOUNT_ID, - &OK_HOTKEY_ACCOUNT_ID, - tick_low, - tick_high, - 123_456_789 - )); - - // Users-only liquidations are idempotent. - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // Now clear protocol liquidity/state—also idempotent. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // State remains empty - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); - - // Non‑V3 flavor - new_test_ext().execute_with(|| { - let netuid = NetUid::from(8); - - // Never initialize V3; both calls no-op and succeed. - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - assert!( - Positions::::iter_prefix_values((netuid, OK_COLDKEY_ACCOUNT_ID)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); -} - -#[test] -fn refund_alpha_single_provider_exact() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(11); - let cold = OK_COLDKEY_ACCOUNT_ID; - let hot = OK_HOTKEY_ACCOUNT_ID; - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // --- Create an alpha‑only position (range entirely above current tick → TAO = 0, ALPHA > 0). - let ct = CurrentTick::::get(netuid); - let tick_low = ct.next().expect("current tick should not be MAX in tests"); - let tick_high = TickIndex::MAX; - - let liquidity = 1_000_000_u64; - let (_pos_id, tao_needed, alpha_needed) = - Pallet::::do_add_liquidity(netuid, &cold, &hot, tick_low, tick_high, liquidity) - .expect("add alpha-only liquidity"); - assert_eq!(tao_needed, 0, "alpha-only position must not require TAO"); - assert!(alpha_needed > 0, "alpha-only position must require ALPHA"); - - // --- Snapshot BEFORE we withdraw funds (baseline for conservation). - let alpha_before_hot = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot); - let alpha_before_owner = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let alpha_before_total = alpha_before_hot + alpha_before_owner; - - // --- Mimic extrinsic bookkeeping: withdraw α and record provided reserve. - ::BalanceOps::decrease_stake( - &cold, - &hot, - netuid.into(), - alpha_needed.into(), - ) - .expect("decrease ALPHA"); - AlphaReserve::increase_provided(netuid.into(), alpha_needed.into()); - - // --- Act: users‑only dissolve. - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // --- Assert: total α conserved to owner (may be staked to validator). - let alpha_after_hot = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot); - let alpha_after_owner = - ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let alpha_after_total = alpha_after_hot + alpha_after_owner; - assert_eq!( - alpha_after_total, alpha_before_total, - "ALPHA principal must be conserved to the account" - ); - - // Clear protocol liquidity and V3 state now. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - - // --- State is cleared. - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert_eq!(Pallet::::count_positions(netuid, &cold), 0); - assert!(!SwapV3Initialized::::contains_key(netuid)); - }); -} - -#[test] -fn refund_alpha_multiple_providers_proportional_to_principal() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(12); - let c1 = OK_COLDKEY_ACCOUNT_ID; - let h1 = OK_HOTKEY_ACCOUNT_ID; - let c2 = OK_COLDKEY_ACCOUNT_ID_2; - let h2 = OK_HOTKEY_ACCOUNT_ID_2; - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Use the same "above current tick" trick for alpha‑only positions. - let ct = CurrentTick::::get(netuid); - let tick_low = ct.next().expect("current tick should not be MAX in tests"); - let tick_high = TickIndex::MAX; - - // Provider #1 (smaller α) - let liq1 = 700_000_u64; - let (_p1, t1, a1) = - Pallet::::do_add_liquidity(netuid, &c1, &h1, tick_low, tick_high, liq1) - .expect("add alpha-only liquidity #1"); - assert_eq!(t1, 0); - assert!(a1 > 0); - - // Provider #2 (larger α) - let liq2 = 2_100_000_u64; - let (_p2, t2, a2) = - Pallet::::do_add_liquidity(netuid, &c2, &h2, tick_low, tick_high, liq2) - .expect("add alpha-only liquidity #2"); - assert_eq!(t2, 0); - assert!(a2 > 0); - - // Baselines BEFORE withdrawing - let a1_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); - let a1_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); - let a1_before = a1_before_hot + a1_before_owner; - - let a2_before_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); - let a2_before_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); - let a2_before = a2_before_hot + a2_before_owner; - - // Withdraw alpha and account reserves for each provider. - ::BalanceOps::decrease_stake(&c1, &h1, netuid.into(), a1.into()) - .expect("decrease alpha #1"); - AlphaReserve::increase_provided(netuid.into(), a1.into()); - - ::BalanceOps::decrease_stake(&c2, &h2, netuid.into(), a2.into()) - .expect("decrease alpha #2"); - AlphaReserve::increase_provided(netuid.into(), a2.into()); - - // Act - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // Each owner is restored to their exact baseline. - let a1_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c1, &h1); - let a1_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c1, &c1); - let a1_after = a1_after_hot + a1_after_owner; - assert_eq!( - a1_after, a1_before, - "owner #1 must receive their α principal back" - ); - - let a2_after_hot = ::BalanceOps::alpha_balance(netuid.into(), &c2, &h2); - let a2_after_owner = ::BalanceOps::alpha_balance(netuid.into(), &c2, &c2); - let a2_after = a2_after_hot + a2_after_owner; - assert_eq!( - a2_after, a2_before, - "owner #2 must receive their α principal back" - ); - }); -} - -#[test] -fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(13); - let cold = OK_COLDKEY_ACCOUNT_ID; - let hot1 = OK_HOTKEY_ACCOUNT_ID; - let hot2 = OK_HOTKEY_ACCOUNT_ID_2; - - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - - // Two alpha‑only positions on different hotkeys of the same owner. - let ct = CurrentTick::::get(netuid); - let tick_low = ct.next().expect("current tick should not be MAX in tests"); - let tick_high = TickIndex::MAX; - - let (_p1, _t1, a1) = - Pallet::::do_add_liquidity(netuid, &cold, &hot1, tick_low, tick_high, 900_000) - .expect("add alpha-only pos (hot1)"); - let (_p2, _t2, a2) = - Pallet::::do_add_liquidity(netuid, &cold, &hot2, tick_low, tick_high, 1_500_000) - .expect("add alpha-only pos (hot2)"); - assert!(a1 > 0 && a2 > 0); - - // Baseline BEFORE: sum over (cold,hot1) + (cold,hot2) + (cold,cold). - let before_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); - let before_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); - let before_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let before_total = before_hot1 + before_hot2 + before_owner; - - // Withdraw alpha from both hotkeys; track provided‑reserve. - ::BalanceOps::decrease_stake(&cold, &hot1, netuid.into(), a1.into()) - .expect("decr alpha #hot1"); - AlphaReserve::increase_provided(netuid.into(), a1.into()); - - ::BalanceOps::decrease_stake(&cold, &hot2, netuid.into(), a2.into()) - .expect("decr alpha #hot2"); - AlphaReserve::increase_provided(netuid.into(), a2.into()); - - // Act - assert_ok!(Pallet::::do_dissolve_all_liquidity_providers(netuid)); - - // The total α "owned" by the coldkey is conserved (credit may land on (cold,cold)). - let after_hot1 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot1); - let after_hot2 = ::BalanceOps::alpha_balance(netuid.into(), &cold, &hot2); - let after_owner = ::BalanceOps::alpha_balance(netuid.into(), &cold, &cold); - let after_total = after_hot1 + after_hot2 + after_owner; - - assert_eq!( - after_total, before_total, - "owner’s α must be conserved across hot ledgers + (owner,owner)" - ); + assert!(!PalSwapInitialized::::contains_key(netuid)); + assert!(!SwapBalancer::::contains_key(netuid)); }); } @@ -2383,249 +770,100 @@ fn refund_alpha_same_cold_multiple_hotkeys_conserved_to_owner() { fn test_clear_protocol_liquidity_green_path() { new_test_ext().execute_with(|| { // --- Arrange --- - let netuid = NetUid::from(55); - - // Ensure the "user liquidity enabled" flag exists so we can verify it's removed later. - assert_ok!(Pallet::::toggle_user_liquidity( - RuntimeOrigin::root(), - netuid, - true - )); - - // Initialize V3 state; this should set price/tick flags and create a protocol position. - assert_ok!(Pallet::::maybe_initialize_v3(netuid)); - assert!( - SwapV3Initialized::::get(netuid), - "V3 must be initialized" - ); + let netuid = NetUid::from(1); - // Sanity: protocol positions exist before clearing. - let protocol_id = Pallet::::protocol_account_id(); - let prot_positions_before = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); + // Initialize swap state + assert_ok!(Pallet::::maybe_initialize_palswap(netuid, None)); assert!( - !prot_positions_before.is_empty(), - "protocol positions should exist after V3 init" + PalSwapInitialized::::get(netuid), + "Swap must be initialized" ); // --- Act --- // Green path: just clear protocol liquidity and wipe all V3 state. assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - // --- Assert: all protocol positions removed --- - let prot_positions_after = - Positions::::iter_prefix_values((netuid, protocol_id)).collect::>(); - assert!( - prot_positions_after.is_empty(), - "protocol positions must be removed by do_clear_protocol_liquidity" - ); - - // --- Assert: V3 data wiped (idempotent even if some maps were empty) --- - // Ticks / active tick bitmap - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none(), - "active tick bitmap words must be cleared" - ); - - // Fee globals - assert!(!FeeGlobalTao::::contains_key(netuid)); - assert!(!FeeGlobalAlpha::::contains_key(netuid)); - - // Price / tick / liquidity / flags - assert!(!AlphaSqrtPrice::::contains_key(netuid)); - assert!(!CurrentTick::::contains_key(netuid)); - assert!(!CurrentLiquidity::::contains_key(netuid)); - assert!(!SwapV3Initialized::::contains_key(netuid)); + // Flags + assert!(!PalSwapInitialized::::contains_key(netuid)); // Knobs removed assert!(!FeeRate::::contains_key(netuid)); - assert!(!EnabledUserLiquidity::::contains_key(netuid)); // --- And it's idempotent --- assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); - assert!( - Positions::::iter_prefix_values((netuid, protocol_id)) - .next() - .is_none() - ); - assert!(Ticks::::iter_prefix(netuid).next().is_none()); - assert!( - TickIndexBitmapWords::::iter_prefix((netuid,)) - .next() - .is_none() - ); - assert!(!SwapV3Initialized::::contains_key(netuid)); + assert!(!PalSwapInitialized::::contains_key(netuid)); }); } -fn as_tuple( - (t_used, a_used, t_rem, a_rem): (TaoBalance, AlphaBalance, TaoBalance, AlphaBalance), -) -> (u64, u64, u64, u64) { - ( - u64::from(t_used), - u64::from(a_used), - u64::from(t_rem), - u64::from(a_rem), - ) -} - +// cargo test --package pallet-subtensor-swap --lib -- pallet::tests::test_migrate_swapv3_to_balancer --exact --nocapture #[test] -fn proportional_when_price_is_one_and_tao_is_plenty() { - // sqrt_price = 1.0 => price = 1.0 - let sqrt = U64F64::from_num(1u64); - let amount_tao: TaoBalance = 10u64.into(); - let amount_alpha: AlphaBalance = 3u64.into(); - - // alpha * price = 3 * 1 = 3 <= amount_tao(10) - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (3, 3, 7, 0)); -} +fn test_migrate_swapv3_to_balancer() { + use crate::migrations::migrate_swapv3_to_balancer::deprecated_swap_maps; + use substrate_fixed::types::U64F64; -#[test] -fn proportional_when_price_is_one_and_alpha_is_excess() { - // sqrt_price = 1.0 => price = 1.0 - let sqrt = U64F64::from_num(1u64); - let amount_tao: TaoBalance = 5u64.into(); - let amount_alpha: AlphaBalance = 10u64.into(); - - // tao is limiting: alpha_equiv = floor(5 / 1) = 5 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (5, 5, 0, 5)); -} + new_test_ext().execute_with(|| { + let migration = + crate::migrations::migrate_swapv3_to_balancer::migrate_swapv3_to_balancer::; + let netuid = NetUid::from(1); -#[test] -fn proportional_with_higher_price_and_alpha_limiting() { - // Choose sqrt_price = 2.0 => price = 4.0 (since implementation squares it) - let sqrt = U64F64::from_num(2u64); - let amount_tao: TaoBalance = 85u64.into(); - let amount_alpha: AlphaBalance = 20u64.into(); - - // tao_equivalent = alpha * price = 20 * 4 = 80 < 85 => alpha limits tao - // remainders: tao 5, alpha 0 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (80, 20, 5, 0)); -} + // Insert deprecated maps values + deprecated_swap_maps::AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1.23)); + deprecated_swap_maps::ScrapReservoirTao::::insert(netuid, TaoBalance::from(9876)); + deprecated_swap_maps::ScrapReservoirAlpha::::insert(netuid, AlphaBalance::from(9876)); -#[test] -fn proportional_with_higher_price_and_tao_limiting() { - // Choose sqrt_price = 2.0 => price = 4.0 (since implementation squares it) - let sqrt = U64F64::from_num(2u64); - let amount_tao: TaoBalance = 50u64.into(); - let amount_alpha: AlphaBalance = 20u64.into(); - - // tao_equivalent = alpha * price = 20 * 4 = 80 > 50 => tao limits alpha - // alpha_equivalent = floor(50 / 4) = 12 - // remainders: tao 0, alpha 20 - 12 = 8 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (50, 12, 0, 8)); -} + // Insert reserves that do not match the 1.23 price + TaoReserve::set_mock_reserve(netuid, TaoBalance::from(1_000_000_000)); + AlphaReserve::set_mock_reserve(netuid, AlphaBalance::from(4_000_000_000_u64)); -#[test] -fn zero_price_uses_no_tao_and_all_alpha() { - // sqrt_price = 0 => price = 0 - let sqrt = U64F64::from_num(0u64); - let amount_tao: TaoBalance = 42u64.into(); - let amount_alpha: AlphaBalance = 17u64.into(); - - // tao_equivalent = 17 * 0 = 0 <= 42 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (0, 17, 42, 0)); -} + // Run migration + migration(); -#[test] -fn rounding_down_behavior_when_dividing_by_price() { - // sqrt_price = 2.0 => price = 4.0 - let sqrt = U64F64::from_num(2u64); - let amount_tao: TaoBalance = 13u64.into(); - let amount_alpha: AlphaBalance = 100u64.into(); - - // tao is limiting; alpha_equiv = floor(13 / 4) = 3 - // remainders: tao 0, alpha 100 - 3 = 97 - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (13, 3, 0, 97)); -} + // Test that values are removed from state + assert!(!deprecated_swap_maps::AlphaSqrtPrice::::contains_key( + netuid + )); + assert!(!deprecated_swap_maps::ScrapReservoirAlpha::::contains_key(netuid)); -#[test] -fn exact_fit_when_tao_matches_alpha_times_price() { - // sqrt_price = 1.0 => price = 1.0 - let sqrt = U64F64::from_num(1u64); - let amount_tao: TaoBalance = 9u64.into(); - let amount_alpha: AlphaBalance = 9u64.into(); - - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, amount_tao, amount_alpha); - assert_eq!(as_tuple(out), (9, 9, 0, 0)); + // Test that subnet price is still 1.23^2 + assert_abs_diff_eq!( + Swap::current_price(netuid).to_num::(), + 1.23 * 1.23, + epsilon = 0.1 + ); + }); } #[test] -fn handles_zero_balances() { - let sqrt = U64F64::from_num(1u64); - - // Zero TAO, some alpha - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, 0u64.into(), 7u64.into()); - // tao limits; alpha_equiv = floor(0 / 1) = 0 - assert_eq!(as_tuple(out), (0, 0, 0, 7)); - - // Some TAO, zero alpha - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, 7u64.into(), 0u64.into()); - // tao_equiv = 0 * 1 = 0 <= 7 - assert_eq!(as_tuple(out), (0, 0, 7, 0)); - - // Both zero - let out = - Pallet::::get_proportional_alpha_tao_and_remainders(sqrt, 0u64.into(), 0u64.into()); - assert_eq!(as_tuple(out), (0, 0, 0, 0)); -} +fn test_migrate_swapv3_to_balancer_falls_back_to_default_when_price_init_fails() { + use crate::migrations::migrate_swapv3_to_balancer::deprecated_swap_maps; + use substrate_fixed::types::U64F64; -#[test] -fn adjust_protocol_liquidity_uses_and_sets_scrap_reservoirs() { new_test_ext().execute_with(|| { - // --- Arrange - let netuid: NetUid = 1u16.into(); - // Price = 1.0 (since sqrt_price^2 = 1), so proportional match is 1:1 - AlphaSqrtPrice::::insert(netuid, U64F64::saturating_from_num(1u64)); - - // Start with some non-zero scrap reservoirs - ScrapReservoirTao::::insert(netuid, TaoBalance::from(7u64)); - ScrapReservoirAlpha::::insert(netuid, AlphaBalance::from(5u64)); - - // Create a minimal protocol position so the function’s body executes. - let protocol = Pallet::::protocol_account_id(); - let position = Position::new( - PositionId::from(0), - netuid, - TickIndex::MIN, - TickIndex::MAX, - 0, - ); - // Ensure collect_fees() returns (0,0) via zeroed fees in `position` (default). - Positions::::insert((netuid, protocol, position.id), position.clone()); + let migration = + crate::migrations::migrate_swapv3_to_balancer::migrate_swapv3_to_balancer::; + let migration_name = + frame_support::BoundedVec::truncate_from(b"migrate_swapv3_to_balancer".to_vec()); + let netuid = NetUid::from(1); - // --- Act - // No external deltas or fees; only reservoirs should be considered. - // With price=1, the exact proportional pair uses 5 alpha and 5 tao, - // leaving tao scrap = 7 - 5 = 2, alpha scrap = 5 - 5 = 0. - Pallet::::adjust_protocol_liquidity(netuid, 0u64.into(), 0u64.into()); + deprecated_swap_maps::AlphaSqrtPrice::::insert(netuid, U64F64::from_num(1)); + deprecated_swap_maps::ScrapReservoirTao::::insert(netuid, TaoBalance::from(9876)); + deprecated_swap_maps::ScrapReservoirAlpha::::insert(netuid, AlphaBalance::from(9876)); - // --- Assert: reservoirs were READ (used in proportional calc) and then SET (updated) - assert_eq!( - ScrapReservoirTao::::get(netuid), - TaoBalance::from(2u64) - ); + TaoReserve::set_mock_reserve(netuid, TaoBalance::from(1)); + AlphaReserve::set_mock_reserve(netuid, AlphaBalance::from(1_000_000_000_000_u64)); + + migration(); + + assert!(!deprecated_swap_maps::AlphaSqrtPrice::::contains_key( + netuid + )); + assert!(!deprecated_swap_maps::ScrapReservoirTao::::contains_key(netuid)); + assert!(!deprecated_swap_maps::ScrapReservoirAlpha::::contains_key(netuid)); + assert!(PalSwapInitialized::::get(netuid)); assert_eq!( - ScrapReservoirAlpha::::get(netuid), - AlphaBalance::from(0u64) + SwapBalancer::::get(netuid).get_quote_weight(), + Perquintill::from_rational(1_u64, 2_u64) ); + assert!(HasMigrationRun::::get(&migration_name)); }); } diff --git a/pallets/swap/src/position.rs b/pallets/swap/src/position.rs deleted file mode 100644 index 5a57928a93..0000000000 --- a/pallets/swap/src/position.rs +++ /dev/null @@ -1,198 +0,0 @@ -use core::marker::PhantomData; - -use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; -use frame_support::pallet_prelude::*; -use safe_math::*; -use substrate_fixed::types::{I64F64, U64F64}; -use subtensor_macros::freeze_struct; -use subtensor_runtime_common::NetUid; - -use crate::SqrtPrice; -use crate::pallet::{Config, Error, FeeGlobalAlpha, FeeGlobalTao, LastPositionId}; -use crate::tick::TickIndex; - -/// Position designates one liquidity position. -/// -/// Alpha price is expressed in rao units per one 10^9 unit. For example, -/// price 1_000_000 is equal to 0.001 TAO per Alpha. -#[freeze_struct("27a1bf8c59480f0")] -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen, Default)] -#[scale_info(skip_type_params(T))] -pub struct Position { - /// Unique ID of the position - pub id: PositionId, - /// Network identifier - pub netuid: NetUid, - /// Tick index for lower boundary of price - pub tick_low: TickIndex, - /// Tick index for higher boundary of price - pub tick_high: TickIndex, - /// Position liquidity - pub liquidity: u64, - /// Fees accrued by the position in quote currency (TAO) relative to global fees - pub fees_tao: I64F64, - /// Fees accrued by the position in base currency (Alpha) relative to global fees - pub fees_alpha: I64F64, - /// Phantom marker for generic Config type - pub _phantom: PhantomData, -} - -impl Position { - pub fn new( - id: PositionId, - netuid: NetUid, - tick_low: TickIndex, - tick_high: TickIndex, - liquidity: u64, - ) -> Self { - let mut position = Position { - id, - netuid, - tick_low, - tick_high, - liquidity, - fees_tao: I64F64::saturating_from_num(0), - fees_alpha: I64F64::saturating_from_num(0), - _phantom: PhantomData, - }; - - position.fees_tao = position.fees_in_range(true); - position.fees_alpha = position.fees_in_range(false); - - position - } - - /// Converts position to token amounts - /// - /// returns tuple of (TAO, Alpha) - /// - /// Pseudocode: - /// if self.sqrt_price_curr < sqrt_pa: - /// tao = 0 - /// alpha = L * (1 / sqrt_pa - 1 / sqrt_pb) - /// elif self.sqrt_price_curr > sqrt_pb: - /// tao = L * (sqrt_pb - sqrt_pa) - /// alpha = 0 - /// else: - /// tao = L * (self.sqrt_price_curr - sqrt_pa) - /// alpha = L * (1 / self.sqrt_price_curr - 1 / sqrt_pb) - /// - pub fn to_token_amounts(&self, sqrt_price_curr: SqrtPrice) -> Result<(u64, u64), Error> { - let one = U64F64::saturating_from_num(1); - - let sqrt_price_low = self - .tick_low - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let sqrt_price_high = self - .tick_high - .try_to_sqrt_price() - .map_err(|_| Error::::InvalidTickRange)?; - let liquidity_fixed = U64F64::saturating_from_num(self.liquidity); - - Ok(if sqrt_price_curr < sqrt_price_low { - ( - 0, - liquidity_fixed - .saturating_mul( - one.safe_div(sqrt_price_low) - .saturating_sub(one.safe_div(sqrt_price_high)), - ) - .saturating_to_num::(), - ) - } else if sqrt_price_curr > sqrt_price_high { - ( - liquidity_fixed - .saturating_mul(sqrt_price_high.saturating_sub(sqrt_price_low)) - .saturating_to_num::(), - 0, - ) - } else { - ( - liquidity_fixed - .saturating_mul(sqrt_price_curr.saturating_sub(sqrt_price_low)) - .saturating_to_num::(), - liquidity_fixed - .saturating_mul( - one.safe_div(sqrt_price_curr) - .saturating_sub(one.safe_div(sqrt_price_high)), - ) - .saturating_to_num::(), - ) - }) - } - - /// Collect fees for a position - /// Updates the position - pub fn collect_fees(&mut self) -> (u64, u64) { - let fee_tao_agg = self.fees_in_range(true); - let fee_alpha_agg = self.fees_in_range(false); - - let mut fee_tao = fee_tao_agg.saturating_sub(self.fees_tao); - let mut fee_alpha = fee_alpha_agg.saturating_sub(self.fees_alpha); - - self.fees_tao = fee_tao_agg; - self.fees_alpha = fee_alpha_agg; - - let liquidity_frac = I64F64::saturating_from_num(self.liquidity); - - fee_tao = liquidity_frac.saturating_mul(fee_tao); - fee_alpha = liquidity_frac.saturating_mul(fee_alpha); - - ( - fee_tao.saturating_to_num::(), - fee_alpha.saturating_to_num::(), - ) - } - - /// Get fees in a position's range - /// - /// If quote flag is true, Tao is returned, otherwise alpha. - fn fees_in_range(&self, quote: bool) -> I64F64 { - if quote { - I64F64::saturating_from_num(FeeGlobalTao::::get(self.netuid)) - } else { - I64F64::saturating_from_num(FeeGlobalAlpha::::get(self.netuid)) - } - .saturating_sub(self.tick_low.fees_below::(self.netuid, quote)) - .saturating_sub(self.tick_high.fees_above::(self.netuid, quote)) - } -} - -#[freeze_struct("8501fa251c9d74c")] -#[derive( - Clone, - Copy, - Decode, - DecodeWithMemTracking, - Default, - Encode, - Eq, - MaxEncodedLen, - PartialEq, - RuntimeDebug, - TypeInfo, -)] -pub struct PositionId(u128); - -impl PositionId { - /// Create a new position ID - pub fn new() -> Self { - let new = LastPositionId::::get().saturating_add(1); - LastPositionId::::put(new); - - Self(new) - } -} - -impl From for PositionId { - fn from(value: u128) -> Self { - Self(value) - } -} - -impl From for u128 { - fn from(value: PositionId) -> Self { - value.0 - } -} diff --git a/pallets/swap/src/tick.rs b/pallets/swap/src/tick.rs deleted file mode 100644 index d3493fde45..0000000000 --- a/pallets/swap/src/tick.rs +++ /dev/null @@ -1,2198 +0,0 @@ -//! The math is adapted from github.com/0xKitsune/uniswap-v3-math -use core::cmp::Ordering; -use core::convert::TryFrom; -use core::error::Error; -use core::fmt; -use core::hash::Hash; -use core::ops::{Add, AddAssign, BitOr, Deref, Neg, Shl, Shr, Sub, SubAssign}; - -use alloy_primitives::{I256, U256}; -use codec::{Decode, DecodeWithMemTracking, Encode, Error as CodecError, Input, MaxEncodedLen}; -use frame_support::pallet_prelude::*; -use safe_math::*; -use sp_std::vec; -use sp_std::vec::Vec; -use substrate_fixed::types::{I64F64, U64F64}; -use subtensor_macros::freeze_struct; -use subtensor_runtime_common::NetUid; - -use crate::SqrtPrice; -use crate::pallet::{ - Config, CurrentTick, FeeGlobalAlpha, FeeGlobalTao, TickIndexBitmapWords, Ticks, -}; - -const U256_1: U256 = U256::from_limbs([1, 0, 0, 0]); -const U256_2: U256 = U256::from_limbs([2, 0, 0, 0]); -const U256_3: U256 = U256::from_limbs([3, 0, 0, 0]); -const U256_4: U256 = U256::from_limbs([4, 0, 0, 0]); -const U256_5: U256 = U256::from_limbs([5, 0, 0, 0]); -const U256_6: U256 = U256::from_limbs([6, 0, 0, 0]); -const U256_7: U256 = U256::from_limbs([7, 0, 0, 0]); -const U256_8: U256 = U256::from_limbs([8, 0, 0, 0]); -const U256_15: U256 = U256::from_limbs([15, 0, 0, 0]); -const U256_16: U256 = U256::from_limbs([16, 0, 0, 0]); -const U256_32: U256 = U256::from_limbs([32, 0, 0, 0]); -const U256_64: U256 = U256::from_limbs([64, 0, 0, 0]); -const U256_127: U256 = U256::from_limbs([127, 0, 0, 0]); -const U256_128: U256 = U256::from_limbs([128, 0, 0, 0]); -const U256_255: U256 = U256::from_limbs([255, 0, 0, 0]); - -const U256_256: U256 = U256::from_limbs([256, 0, 0, 0]); -const U256_512: U256 = U256::from_limbs([512, 0, 0, 0]); -const U256_1024: U256 = U256::from_limbs([1024, 0, 0, 0]); -const U256_2048: U256 = U256::from_limbs([2048, 0, 0, 0]); -const U256_4096: U256 = U256::from_limbs([4096, 0, 0, 0]); -const U256_8192: U256 = U256::from_limbs([8192, 0, 0, 0]); -const U256_16384: U256 = U256::from_limbs([16384, 0, 0, 0]); -const U256_32768: U256 = U256::from_limbs([32768, 0, 0, 0]); -const U256_65536: U256 = U256::from_limbs([65536, 0, 0, 0]); -const U256_131072: U256 = U256::from_limbs([131072, 0, 0, 0]); -const U256_262144: U256 = U256::from_limbs([262144, 0, 0, 0]); -const U256_524288: U256 = U256::from_limbs([524288, 0, 0, 0]); - -const U256_MAX_TICK: U256 = U256::from_limbs([887272, 0, 0, 0]); - -const MIN_TICK: i32 = -887272; -const MAX_TICK: i32 = -MIN_TICK; - -const MIN_SQRT_RATIO: U256 = U256::from_limbs([4295128739, 0, 0, 0]); -const MAX_SQRT_RATIO: U256 = - U256::from_limbs([6743328256752651558, 17280870778742802505, 4294805859, 0]); - -const SQRT_10001: I256 = I256::from_raw(U256::from_limbs([11745905768312294533, 13863, 0, 0])); -const TICK_LOW: I256 = I256::from_raw(U256::from_limbs([ - 6552757943157144234, - 184476617836266586, - 0, - 0, -])); -const TICK_HIGH: I256 = I256::from_raw(U256::from_limbs([ - 4998474450511881007, - 15793544031827761793, - 0, - 0, -])); - -/// Tick is the price range determined by tick index (not part of this struct, but is the key at -/// which the Tick is stored in state hash maps). Tick struct stores liquidity and fee information. -/// -/// - Net liquidity -/// - Gross liquidity -/// - Fees (above global) in both currencies -#[freeze_struct("ff1bce826e64c4aa")] -#[derive(Debug, Default, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, PartialEq, Eq)] -pub struct Tick { - pub liquidity_net: i128, - pub liquidity_gross: u64, - pub fees_out_tao: I64F64, - pub fees_out_alpha: I64F64, -} - -impl Tick { - pub fn liquidity_net_as_u64(&self) -> u64 { - self.liquidity_net.abs().min(u64::MAX as i128) as u64 - } -} - -/// Struct representing a tick index -#[freeze_struct("13c1f887258657f2")] -#[derive( - Debug, - Default, - Clone, - Copy, - Encode, - DecodeWithMemTracking, - TypeInfo, - MaxEncodedLen, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, -)] -pub struct TickIndex(i32); - -impl Decode for TickIndex { - fn decode(input: &mut I) -> Result { - let raw = i32::decode(input)?; - TickIndex::new(raw).map_err(|_| "TickIndex out of bounds".into()) - } -} - -impl Add for TickIndex { - type Output = Self; - - #[allow(clippy::arithmetic_side_effects)] - fn add(self, rhs: Self) -> Self::Output { - // Note: This assumes the result is within bounds. - // For a safer implementation, consider using checked_add. - Self::new_unchecked(self.get() + rhs.get()) - } -} - -impl Sub for TickIndex { - type Output = Self; - - #[allow(clippy::arithmetic_side_effects)] - fn sub(self, rhs: Self) -> Self::Output { - // Note: This assumes the result is within bounds. - // For a safer implementation, consider using checked_sub. - Self::new_unchecked(self.get() - rhs.get()) - } -} - -impl AddAssign for TickIndex { - #[allow(clippy::arithmetic_side_effects)] - fn add_assign(&mut self, rhs: Self) { - *self = Self::new_unchecked(self.get() + rhs.get()); - } -} - -impl SubAssign for TickIndex { - #[allow(clippy::arithmetic_side_effects)] - fn sub_assign(&mut self, rhs: Self) { - *self = Self::new_unchecked(self.get() - rhs.get()); - } -} - -impl TryFrom for TickIndex { - type Error = TickMathError; - - fn try_from(value: i32) -> Result { - Self::new(value) - } -} - -impl Deref for TickIndex { - type Target = i32; - - fn deref(&self) -> &Self::Target { - // Using get() would create an infinite recursion, so this is one place where we need direct - // field access. This is safe because Self::Target is i32, which is exactly what we're - // storing - &self.0 - } -} - -/// Extension trait to make working with TryFrom more ergonomic -pub trait TryIntoTickIndex { - /// Convert an i32 into a TickIndex, with bounds checking - fn into_tick_index(self) -> Result; -} - -impl TryIntoTickIndex for i32 { - fn into_tick_index(self) -> Result { - TickIndex::try_from(self) - } -} - -impl TickIndex { - /// Minimum value of the tick index - /// The tick_math library uses different bitness, so we have to divide by 2. - /// It's unsafe to change this value to something else. - pub const MIN: Self = Self(MIN_TICK.saturating_div(2)); - - /// Maximum value of the tick index - /// The tick_math library uses different bitness, so we have to divide by 2. - /// It's unsafe to change this value to something else. - pub const MAX: Self = Self(MAX_TICK.saturating_div(2)); - - /// All tick indexes are offset by this value for storage needs - /// so that tick indexes are positive, which simplifies bit logic - const OFFSET: Self = Self(MAX_TICK); - - /// The MIN sqrt price, which is caclculated at Self::MIN - pub fn min_sqrt_price() -> SqrtPrice { - SqrtPrice::saturating_from_num(0.0000000002328350195) - } - - /// The MAX sqrt price, which is calculated at Self::MAX - #[allow(clippy::excessive_precision)] - pub fn max_sqrt_price() -> SqrtPrice { - SqrtPrice::saturating_from_num(4294886577.20989222513899790805) - } - - /// Get fees above a tick - pub fn fees_above(&self, netuid: NetUid, quote: bool) -> I64F64 { - let current_tick = Self::current_bounded::(netuid); - - let tick = Ticks::::get(netuid, *self).unwrap_or_default(); - if *self <= current_tick { - if quote { - I64F64::saturating_from_num(FeeGlobalTao::::get(netuid)) - .saturating_sub(tick.fees_out_tao) - } else { - I64F64::saturating_from_num(FeeGlobalAlpha::::get(netuid)) - .saturating_sub(tick.fees_out_alpha) - } - } else if quote { - tick.fees_out_tao - } else { - tick.fees_out_alpha - } - } - - /// Get fees below a tick - pub fn fees_below(&self, netuid: NetUid, quote: bool) -> I64F64 { - let current_tick = Self::current_bounded::(netuid); - - let tick = Ticks::::get(netuid, *self).unwrap_or_default(); - if *self <= current_tick { - if quote { - tick.fees_out_tao - } else { - tick.fees_out_alpha - } - } else if quote { - I64F64::saturating_from_num(FeeGlobalTao::::get(netuid)) - .saturating_sub(tick.fees_out_tao) - } else { - I64F64::saturating_from_num(FeeGlobalAlpha::::get(netuid)) - .saturating_sub(tick.fees_out_alpha) - } - } - - /// Get the current tick index for a subnet, ensuring it's within valid bounds - pub fn current_bounded(netuid: NetUid) -> Self { - let current_tick = CurrentTick::::get(netuid); - if current_tick > Self::MAX { - Self::MAX - } else if current_tick < Self::MIN { - Self::MIN - } else { - current_tick - } - } - - /// Converts a sqrt price to a tick index, ensuring it's within valid bounds - /// - /// If the price is outside the valid range, this function will return the appropriate boundary - /// tick index (MIN or MAX) instead of an error. - /// - /// # Arguments - /// * `sqrt_price` - The square root price to convert to a tick index - /// - /// # Returns - /// * `TickIndex` - A tick index that is guaranteed to be within valid bounds - pub fn from_sqrt_price_bounded(sqrt_price: SqrtPrice) -> Self { - match Self::try_from_sqrt_price(sqrt_price) { - Ok(index) => index, - Err(_) => { - let max_price = Self::MAX.as_sqrt_price_bounded(); - - if sqrt_price > max_price { - Self::MAX - } else { - Self::MIN - } - } - } - } - - /// Converts a tick index to a sqrt price, ensuring it's within valid bounds - /// - /// Unlike try_to_sqrt_price which returns an error for boundary indices, this function - /// guarantees a valid sqrt price by using fallback values if conversion fails. - /// - /// # Returns - /// * `SqrtPrice` - A sqrt price that is guaranteed to be a valid value - pub fn as_sqrt_price_bounded(&self) -> SqrtPrice { - self.try_to_sqrt_price().unwrap_or_else(|_| { - if *self >= Self::MAX { - Self::max_sqrt_price() - } else { - Self::min_sqrt_price() - } - }) - } - - /// Creates a new TickIndex instance with bounds checking - pub fn new(value: i32) -> Result { - if !(Self::MIN.0..=Self::MAX.0).contains(&value) { - Err(TickMathError::TickOutOfBounds) - } else { - Ok(Self(value)) - } - } - - /// Creates a new TickIndex without bounds checking - /// Use this function with caution, only when you're certain the value is valid - pub fn new_unchecked(value: i32) -> Self { - Self(value) - } - - /// Get the inner value - pub fn get(&self) -> i32 { - self.0 - } - - /// Creates a TickIndex from an offset representation (u32) - /// - /// # Arguments - /// * `offset_index` - An offset index (u32 value) representing a tick index - /// - /// # Returns - /// * `Result` - The corresponding TickIndex if within valid bounds - pub fn from_offset_index(offset_index: u32) -> Result { - // while it's safe, we use saturating math to mute the linter and just in case - let signed_index = ((offset_index as i64).saturating_sub(Self::OFFSET.get() as i64)) as i32; - Self::new(signed_index) - } - - /// Get the next tick index (incrementing by 1) - pub fn next(&self) -> Result { - Self::new(self.0.saturating_add(1)) - } - - /// Get the previous tick index (decrementing by 1) - pub fn prev(&self) -> Result { - Self::new(self.0.saturating_sub(1)) - } - - /// Add a value to this tick index with bounds checking - pub fn checked_add(&self, value: i32) -> Result { - Self::new(self.0.saturating_add(value)) - } - - /// Subtract a value from this tick index with bounds checking - pub fn checked_sub(&self, value: i32) -> Result { - Self::new(self.0.saturating_sub(value)) - } - - /// Add a value to this tick index, saturating at the bounds instead of overflowing - pub fn saturating_add(&self, value: i32) -> Self { - match self.checked_add(value) { - Ok(result) => result, - Err(_) => { - if value > 0 { - Self::MAX - } else { - Self::MIN - } - } - } - } - - /// Subtract a value from this tick index, saturating at the bounds instead of overflowing - pub fn saturating_sub(&self, value: i32) -> Self { - match self.checked_sub(value) { - Ok(result) => result, - Err(_) => { - if value > 0 { - Self::MIN - } else { - Self::MAX - } - } - } - } - - /// Divide the tick index by a value with bounds checking - #[allow(clippy::arithmetic_side_effects)] - pub fn checked_div(&self, value: i32) -> Result { - if value == 0 { - return Err(TickMathError::DivisionByZero); - } - Self::new(self.0.saturating_div(value)) - } - - /// Divide the tick index by a value, saturating at the bounds - pub fn saturating_div(&self, value: i32) -> Self { - if value == 0 { - return Self::MAX; // Return MAX for division by zero - } - match self.checked_div(value) { - Ok(result) => result, - Err(_) => { - if (self.0 < 0 && value > 0) || (self.0 > 0 && value < 0) { - Self::MIN - } else { - Self::MAX - } - } - } - } - - /// Multiply the tick index by a value with bounds checking - pub fn checked_mul(&self, value: i32) -> Result { - // Check for potential overflow - match self.0.checked_mul(value) { - Some(result) => Self::new(result), - None => Err(TickMathError::Overflow), - } - } - - /// Multiply the tick index by a value, saturating at the bounds - pub fn saturating_mul(&self, value: i32) -> Self { - match self.checked_mul(value) { - Ok(result) => result, - Err(_) => { - if (self.0 < 0 && value > 0) || (self.0 > 0 && value < 0) { - Self::MIN - } else { - Self::MAX - } - } - } - } - - /// Converts tick index into SQRT of lower price of this tick In order to find the higher price - /// of this tick, call tick_index_to_sqrt_price(tick_idx + 1) - pub fn try_to_sqrt_price(&self) -> Result { - // because of u256->u128 conversion we have twice less values for min/max ticks - if !(Self::MIN..=Self::MAX).contains(self) { - return Err(TickMathError::TickOutOfBounds); - } - get_sqrt_ratio_at_tick(self.0).and_then(u256_q64_96_to_u64f64) - } - - /// Converts SQRT price to tick index - /// Because the tick is the range of prices [sqrt_lower_price, sqrt_higher_price), the resulting - /// tick index matches the price by the following inequality: - /// sqrt_lower_price <= sqrt_price < sqrt_higher_price - pub fn try_from_sqrt_price(sqrt_price: SqrtPrice) -> Result { - // price in the native Q64.96 integer format - let price_x96 = u64f64_to_u256_q64_96(sqrt_price); - - // first‑pass estimate from the log calculation - let mut tick = get_tick_at_sqrt_ratio(price_x96)?; - - // post‑verification, *both* directions - let price_at_tick = get_sqrt_ratio_at_tick(tick)?; - if price_at_tick > price_x96 { - tick = tick.saturating_sub(1); // estimate was too high - } else { - // it may still be one too low - let price_at_tick_plus = get_sqrt_ratio_at_tick(tick.saturating_add(1))?; - if price_at_tick_plus <= price_x96 { - tick = tick.saturating_add(1); // step up when required - } - } - - tick.into_tick_index() - } -} - -pub struct ActiveTickIndexManager(PhantomData); - -impl ActiveTickIndexManager { - pub fn insert(netuid: NetUid, index: TickIndex) { - // Check the range - if (index < TickIndex::MIN) || (index > TickIndex::MAX) { - return; - } - - // Convert to bitmap representation - let bitmap = TickIndexBitmap::from(index); - - // Update layer words - let mut word0_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Top, - bitmap.word_at(LayerLevel::Top), - )); - let mut word1_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - )); - let mut word2_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - )); - - // Set bits in each layer - word0_value |= bitmap.bit_mask(LayerLevel::Top); - word1_value |= bitmap.bit_mask(LayerLevel::Middle); - word2_value |= bitmap.bit_mask(LayerLevel::Bottom); - - // Update the storage - TickIndexBitmapWords::::set( - (netuid, LayerLevel::Top, bitmap.word_at(LayerLevel::Top)), - word0_value, - ); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - ), - word1_value, - ); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - ), - word2_value, - ); - } - - pub fn remove(netuid: NetUid, index: TickIndex) { - // Check the range - if (index < TickIndex::MIN) || (index > TickIndex::MAX) { - return; - } - - // Convert to bitmap representation - let bitmap = TickIndexBitmap::from(index); - - // Update layer words - let mut word0_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Top, - bitmap.word_at(LayerLevel::Top), - )); - let mut word1_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - )); - let mut word2_value = TickIndexBitmapWords::::get(( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - )); - - // Turn the bit off (& !bit) and save as needed - word2_value &= !bitmap.bit_mask(LayerLevel::Bottom); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Bottom, - bitmap.word_at(LayerLevel::Bottom), - ), - word2_value, - ); - - if word2_value == 0 { - word1_value &= !bitmap.bit_mask(LayerLevel::Middle); - TickIndexBitmapWords::::set( - ( - netuid, - LayerLevel::Middle, - bitmap.word_at(LayerLevel::Middle), - ), - word1_value, - ); - } - - if word1_value == 0 { - word0_value &= !bitmap.bit_mask(LayerLevel::Top); - TickIndexBitmapWords::::set( - (netuid, LayerLevel::Top, bitmap.word_at(LayerLevel::Top)), - word0_value, - ); - } - } - - pub fn find_closest_lower(netuid: NetUid, index: TickIndex) -> Option { - Self::find_closest(netuid, index, true) - } - - pub fn find_closest_higher(netuid: NetUid, index: TickIndex) -> Option { - Self::find_closest(netuid, index, false) - } - - fn find_closest(netuid: NetUid, index: TickIndex, lower: bool) -> Option { - // Check the range - if (index < TickIndex::MIN) || (index > TickIndex::MAX) { - return None; - } - - // Convert to bitmap representation - let bitmap = TickIndexBitmap::from(index); - let mut found = false; - let mut result: u32 = 0; - - // Layer positions from bitmap - let layer0_word = bitmap.word_at(LayerLevel::Top); - let layer0_bit = bitmap.bit_at(LayerLevel::Top); - let layer1_word = bitmap.word_at(LayerLevel::Middle); - let layer1_bit = bitmap.bit_at(LayerLevel::Middle); - let layer2_word = bitmap.word_at(LayerLevel::Bottom); - let layer2_bit = bitmap.bit_at(LayerLevel::Bottom); - - // Find the closest active bits in layer 0, then 1, then 2 - - /////////////// - // Level 0 - let word0 = TickIndexBitmapWords::::get((netuid, LayerLevel::Top, layer0_word)); - let closest_bits_l0 = - TickIndexBitmap::find_closest_active_bit_candidates(word0, layer0_bit, lower); - - for closest_bit_l0 in closest_bits_l0.iter() { - /////////////// - // Level 1 - let word1_index = TickIndexBitmap::layer_to_index(BitmapLayer::new(0, *closest_bit_l0)); - - // Layer 1 words are different, shift the bit to the word edge - let start_from_l1_bit = match word1_index.cmp(&layer1_word) { - Ordering::Less => 127, - Ordering::Greater => 0, - _ => layer1_bit, - }; - let word1_value = - TickIndexBitmapWords::::get((netuid, LayerLevel::Middle, word1_index)); - let closest_bits_l1 = TickIndexBitmap::find_closest_active_bit_candidates( - word1_value, - start_from_l1_bit, - lower, - ); - - for closest_bit_l1 in closest_bits_l1.iter() { - /////////////// - // Level 2 - let word2_index = - TickIndexBitmap::layer_to_index(BitmapLayer::new(word1_index, *closest_bit_l1)); - - // Layer 2 words are different, shift the bit to the word edge - let start_from_l2_bit = match word2_index.cmp(&layer2_word) { - Ordering::Less => 127, - Ordering::Greater => 0, - _ => layer2_bit, - }; - - let word2_value = - TickIndexBitmapWords::::get((netuid, LayerLevel::Bottom, word2_index)); - - let closest_bits_l2 = TickIndexBitmap::find_closest_active_bit_candidates( - word2_value, - start_from_l2_bit, - lower, - ); - - if !closest_bits_l2.is_empty() { - // The active tick is found, restore its full index and return - let offset_found_index = TickIndexBitmap::layer_to_index(BitmapLayer::new( - word2_index, - // it's safe to unwrap, because the len is > 0, but to prevent errors in - // refactoring, we use default fallback here for extra safety - closest_bits_l2.first().copied().unwrap_or_default(), - )); - - if lower { - if (offset_found_index > result) || (!found) { - result = offset_found_index; - found = true; - } - } else if (offset_found_index < result) || (!found) { - result = offset_found_index; - found = true; - } - } - } - } - - if !found { - return None; - } - - // Convert the result offset_index back to a tick index - TickIndex::from_offset_index(result).ok() - } - - pub fn tick_is_active(netuid: NetUid, tick: TickIndex) -> bool { - Self::find_closest_lower(netuid, tick).unwrap_or(TickIndex::MAX) == tick - } -} - -/// Represents the three layers in the Uniswap V3 bitmap structure -#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)] -pub enum LayerLevel { - /// Top layer (highest level of the hierarchy) - Top = 0, - /// Middle layer - Middle = 1, - /// Bottom layer (contains the actual ticks) - Bottom = 2, -} - -#[freeze_struct("4015a04919eb5e2e")] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)] -pub(crate) struct BitmapLayer { - word: u32, - bit: u32, -} - -impl BitmapLayer { - pub fn new(word: u32, bit: u32) -> Self { - Self { word, bit } - } -} - -/// A bitmap representation of a tick index position across the three-layer structure -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct TickIndexBitmap { - /// The position in layer 0 (top layer) - layer0: BitmapLayer, - /// The position in layer 1 (middle layer) - layer1: BitmapLayer, - /// The position in layer 2 (bottom layer) - layer2: BitmapLayer, -} - -impl TickIndexBitmap { - /// Helper function to convert a bitmap index to a (word, bit) tuple in a bitmap layer using - /// safe methods - /// - /// Note: This function operates on bitmap navigation indices, NOT tick indices. - /// It converts a flat index within the bitmap structure to a (word, bit) position. - fn index_to_layer(index: u32) -> BitmapLayer { - let word = index.safe_div(128); - let bit = index.checked_rem(128).unwrap_or_default(); - BitmapLayer { word, bit } - } - - /// Converts a position (word, bit) within a layer to a word index in the next layer down - /// Note: This returns a bitmap navigation index, NOT a tick index - pub(crate) fn layer_to_index(layer: BitmapLayer) -> u32 { - layer.word.saturating_mul(128).saturating_add(layer.bit) - } - - /// Get the mask for a bit in the specified layer - pub(crate) fn bit_mask(&self, layer: LayerLevel) -> u128 { - match layer { - LayerLevel::Top => 1u128 << self.layer0.bit, - LayerLevel::Middle => 1u128 << self.layer1.bit, - LayerLevel::Bottom => 1u128 << self.layer2.bit, - } - } - - /// Get the word for the specified layer - pub(crate) fn word_at(&self, layer: LayerLevel) -> u32 { - match layer { - LayerLevel::Top => self.layer0.word, - LayerLevel::Middle => self.layer1.word, - LayerLevel::Bottom => self.layer2.word, - } - } - - /// Get the bit for the specified layer - pub(crate) fn bit_at(&self, layer: LayerLevel) -> u32 { - match layer { - LayerLevel::Top => self.layer0.bit, - LayerLevel::Middle => self.layer1.bit, - LayerLevel::Bottom => self.layer2.bit, - } - } - - /// Finds the closest active bit in a bitmap word, and if the active bit exactly matches the - /// requested bit, then it finds the next one as well - /// - /// # Arguments - /// * `word` - The bitmap word to search within - /// * `bit` - The bit position to start searching from - /// * `lower` - If true, search for lower bits (decreasing bit position), if false, search for - /// higher bits (increasing bit position) - /// - /// # Returns - /// * Exact match: Vec with [next_bit, bit] - /// * Non-exact match: Vec with [closest_bit] - /// * No match: Empty Vec - pub(crate) fn find_closest_active_bit_candidates( - word: u128, - bit: u32, - lower: bool, - ) -> Vec { - let mut result = vec![]; - let mut mask: u128 = 1_u128.wrapping_shl(bit); - let mut active_bit: u32 = bit; - - while mask > 0 { - if mask & word != 0 { - result.push(active_bit); - if active_bit != bit { - break; - } - } - - mask = if lower { - active_bit = active_bit.saturating_sub(1); - mask.wrapping_shr(1) - } else { - active_bit = active_bit.saturating_add(1); - mask.wrapping_shl(1) - }; - } - - result - } -} - -impl From for TickIndexBitmap { - fn from(tick_index: TickIndex) -> Self { - // Convert to offset index (internal operation only) - let offset_index = (tick_index.get().saturating_add(TickIndex::OFFSET.get())) as u32; - - // Calculate layer positions - let layer2 = Self::index_to_layer(offset_index); - let layer1 = Self::index_to_layer(layer2.word); - let layer0 = Self::index_to_layer(layer1.word); - - Self { - layer0, - layer1, - layer2, - } - } -} - -#[allow(clippy::arithmetic_side_effects)] -fn get_sqrt_ratio_at_tick(tick: i32) -> Result { - let abs_tick = if tick < 0 { - U256::from(tick.neg()) - } else { - U256::from(tick) - }; - - if abs_tick > U256_MAX_TICK { - return Err(TickMathError::TickOutOfBounds); - } - - let mut ratio = if abs_tick & (U256_1) != U256::ZERO { - U256::from_limbs([12262481743371124737, 18445821805675392311, 0, 0]) - } else { - U256::from_limbs([0, 0, 1, 0]) - }; - - if !(abs_tick & U256_2).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 6459403834229662010, - 18444899583751176498, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_4).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 17226890335427755468, - 18443055278223354162, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_8).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 2032852871939366096, - 18439367220385604838, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_16).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 14545316742740207172, - 18431993317065449817, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_32).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 5129152022828963008, - 18417254355718160513, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_64).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 4894419605888772193, - 18387811781193591352, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_128).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 1280255884321894483, - 18329067761203520168, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_256).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 15924666964335305636, - 18212142134806087854, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_512).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 8010504389359918676, - 17980523815641551639, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_1024).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 10668036004952895731, - 17526086738831147013, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_2048).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 4878133418470705625, - 16651378430235024244, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_4096).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 9537173718739605541, - 15030750278693429944, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_8192).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 9972618978014552549, - 12247334978882834399, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_16384).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 10428997489610666743, - 8131365268884726200, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_32768).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 9305304367709015974, - 3584323654723342297, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_65536).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 14301143598189091785, - 696457651847595233, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_131072).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 7393154844743099908, - 26294789957452057, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_262144).is_zero() { - ratio = (ratio.saturating_mul(U256::from_limbs([ - 2209338891292245656, - 37481735321082, - 0, - 0, - ]))) >> 128 - } - if !(abs_tick & U256_524288).is_zero() { - ratio = - (ratio.saturating_mul(U256::from_limbs([10518117631919034274, 76158723, 0, 0]))) >> 128 - } - - if tick > 0 { - ratio = U256::MAX / ratio; - } - - let shifted: U256 = ratio >> 32; - let ceil = if ratio & U256::from((1u128 << 32) - 1) != U256::ZERO { - shifted.saturating_add(U256_1) - } else { - shifted - }; - Ok(ceil) -} - -#[allow(clippy::arithmetic_side_effects)] -fn get_tick_at_sqrt_ratio(sqrt_price_x_96: U256) -> Result { - if !(sqrt_price_x_96 >= MIN_SQRT_RATIO && sqrt_price_x_96 < MAX_SQRT_RATIO) { - return Err(TickMathError::SqrtPriceOutOfBounds); - } - - let ratio: U256 = sqrt_price_x_96.shl(32); - let mut r = ratio; - let mut msb = U256::ZERO; - - let mut f = if r > U256::from_limbs([18446744073709551615, 18446744073709551615, 0, 0]) { - U256_1.shl(U256_7) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256::from_limbs([18446744073709551615, 0, 0, 0]) { - U256_1.shl(U256_6) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256::from_limbs([4294967295, 0, 0, 0]) { - U256_1.shl(U256_5) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256::from_limbs([65535, 0, 0, 0]) { - U256_1.shl(U256_4) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_255 { - U256_1.shl(U256_3) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_15 { - U256_1.shl(U256_2) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_3 { - U256_1.shl(U256_1) - } else { - U256::ZERO - }; - msb = msb.bitor(f); - r = r.shr(f); - - f = if r > U256_1 { U256_1 } else { U256::ZERO }; - - msb = msb.bitor(f); - - r = if msb >= U256_128 { - ratio.shr(msb.saturating_sub(U256_127)) - } else { - ratio.shl(U256_127.saturating_sub(msb)) - }; - - let mut log_2: I256 = - (I256::from_raw(msb).saturating_sub(I256::from_limbs([128, 0, 0, 0]))).shl(64); - - for i in (51..=63).rev() { - r = r.overflowing_mul(r).0.shr(U256_127); - let f: U256 = r.shr(128); - log_2 = log_2.bitor(I256::from_raw(f.shl(i))); - - r = r.shr(f); - } - - r = r.overflowing_mul(r).0.shr(U256_127); - let f: U256 = r.shr(128); - log_2 = log_2.bitor(I256::from_raw(f.shl(50))); - - let log_sqrt10001 = log_2.wrapping_mul(SQRT_10001); - - let tick_low = (log_sqrt10001.saturating_sub(TICK_LOW) >> 128_u8).low_i32(); - - let tick_high = (log_sqrt10001.saturating_add(TICK_HIGH) >> 128_u8).low_i32(); - - let tick = if tick_low == tick_high { - tick_low - } else if get_sqrt_ratio_at_tick(tick_high)? <= sqrt_price_x_96 { - tick_high - } else { - tick_low - }; - - Ok(tick) -} - -// Convert U64F64 to U256 in Q64.96 format (Uniswap's sqrt price format) -fn u64f64_to_u256_q64_96(value: U64F64) -> U256 { - u64f64_to_u256(value, 96) -} - -/// Convert U64F64 to U256 -/// -/// # Arguments -/// * `value` - The U64F64 value to convert -/// * `target_fractional_bits` - Number of fractional bits in the target U256 format -/// -/// # Returns -/// * `U256` - Converted value -#[allow(clippy::arithmetic_side_effects)] -fn u64f64_to_u256(value: U64F64, target_fractional_bits: u32) -> U256 { - let raw = U256::from(value.to_bits()); - - match target_fractional_bits.cmp(&64) { - Ordering::Less => raw >> (64 - target_fractional_bits), - Ordering::Greater => raw.saturating_shl((target_fractional_bits - 64) as usize), - Ordering::Equal => raw, - } -} - -/// Convert U256 in Q64.96 format (Uniswap's sqrt price format) to U64F64 -fn u256_q64_96_to_u64f64(value: U256) -> Result { - q_to_u64f64(value, 96) -} - -#[allow(clippy::arithmetic_side_effects)] -fn q_to_u64f64(x: U256, frac_bits: u32) -> Result { - let diff = frac_bits.saturating_sub(64) as usize; - - // 1. shift right diff bits - let shifted = if diff != 0 { x >> diff } else { x }; - - // 2. **round up** if we threw away any 1‑bits - let mask = if diff != 0 { - (U256_1.saturating_shl(diff)).saturating_sub(U256_1) - } else { - U256::ZERO - }; - let rounded = if diff != 0 && (x & mask) != U256::ZERO { - shifted.saturating_add(U256_1) - } else { - shifted - }; - - // 3. check that it fits in 128 bits and transmute - if (rounded >> 128) != U256::ZERO { - return Err(TickMathError::Overflow); - } - Ok(U64F64::from_bits(rounded.to::())) -} - -#[derive(Debug, PartialEq, Eq)] -pub enum TickMathError { - TickOutOfBounds, - SqrtPriceOutOfBounds, - ConversionError, - Overflow, - DivisionByZero, -} - -impl fmt::Display for TickMathError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::TickOutOfBounds => f.write_str("The given tick is outside of the minimum/maximum values."), - Self::SqrtPriceOutOfBounds =>f.write_str("Second inequality must be < because the price can never reach the price at the max tick"), - Self::ConversionError => f.write_str("Error converting from one number type into another"), - Self::Overflow => f.write_str("Number overflow in arithmetic operation"), - Self::DivisionByZero => f.write_str("Division by zero is not allowed") - } - } -} - -impl Error for TickMathError {} - -#[allow(clippy::unwrap_used)] -#[cfg(test)] -mod tests { - use safe_math::FixedExt; - use std::{ops::Sub, str::FromStr}; - - use super::*; - use crate::mock::*; - - #[test] - fn test_get_sqrt_ratio_at_tick_bounds() { - // the function should return an error if the tick is out of bounds - if let Err(err) = get_sqrt_ratio_at_tick(MIN_TICK - 1) { - assert!(matches!(err, TickMathError::TickOutOfBounds)); - } else { - panic!("get_qrt_ratio_at_tick did not respect lower tick bound") - } - if let Err(err) = get_sqrt_ratio_at_tick(MAX_TICK + 1) { - assert!(matches!(err, TickMathError::TickOutOfBounds)); - } else { - panic!("get_qrt_ratio_at_tick did not respect upper tick bound") - } - } - - #[test] - fn test_get_sqrt_ratio_at_tick_values() { - // test individual values for correct results - assert_eq!( - get_sqrt_ratio_at_tick(MIN_TICK).unwrap(), - U256::from(4295128739u64), - "sqrt ratio at min incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(MIN_TICK + 1).unwrap(), - U256::from(4295343490u64), - "sqrt ratio at min + 1 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(MAX_TICK - 1).unwrap(), - U256::from_str("1461373636630004318706518188784493106690254656249").unwrap(), - "sqrt ratio at max - 1 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(MAX_TICK).unwrap(), - U256::from_str("1461446703485210103287273052203988822378723970342").unwrap(), - "sqrt ratio at max incorrect" - ); - // checking hard coded values against solidity results - assert_eq!( - get_sqrt_ratio_at_tick(50).unwrap(), - U256::from(79426470787362580746886972461u128), - "sqrt ratio at 50 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(100).unwrap(), - U256::from(79625275426524748796330556128u128), - "sqrt ratio at 100 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(250).unwrap(), - U256::from(80224679980005306637834519095u128), - "sqrt ratio at 250 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(500).unwrap(), - U256::from(81233731461783161732293370115u128), - "sqrt ratio at 500 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(1000).unwrap(), - U256::from(83290069058676223003182343270u128), - "sqrt ratio at 1000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(2500).unwrap(), - U256::from(89776708723587163891445672585u128), - "sqrt ratio at 2500 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(3000).unwrap(), - U256::from(92049301871182272007977902845u128), - "sqrt ratio at 3000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(4000).unwrap(), - U256::from(96768528593268422080558758223u128), - "sqrt ratio at 4000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(5000).unwrap(), - U256::from(101729702841318637793976746270u128), - "sqrt ratio at 5000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(50000).unwrap(), - U256::from(965075977353221155028623082916u128), - "sqrt ratio at 50000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(150000).unwrap(), - U256::from(143194173941309278083010301478497u128), - "sqrt ratio at 150000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(250000).unwrap(), - U256::from(21246587762933397357449903968194344u128), - "sqrt ratio at 250000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(500000).unwrap(), - U256::from_str("5697689776495288729098254600827762987878").unwrap(), - "sqrt ratio at 500000 incorrect" - ); - assert_eq!( - get_sqrt_ratio_at_tick(738203).unwrap(), - U256::from_str("847134979253254120489401328389043031315994541").unwrap(), - "sqrt ratio at 738203 incorrect" - ); - } - - #[test] - fn test_get_tick_at_sqrt_ratio() { - //throws for too low - let result = get_tick_at_sqrt_ratio(MIN_SQRT_RATIO.sub(U256_1)); - assert_eq!( - result.unwrap_err().to_string(), - "Second inequality must be < because the price can never reach the price at the max tick" - ); - - //throws for too high - let result = get_tick_at_sqrt_ratio(MAX_SQRT_RATIO); - assert_eq!( - result.unwrap_err().to_string(), - "Second inequality must be < because the price can never reach the price at the max tick" - ); - - //ratio of min tick - let result = get_tick_at_sqrt_ratio(MIN_SQRT_RATIO).unwrap(); - assert_eq!(result, MIN_TICK); - - //ratio of min tick + 1 - let result = get_tick_at_sqrt_ratio(U256::from_str("4295343490").unwrap()).unwrap(); - assert_eq!(result, MIN_TICK + 1); - } - - #[test] - fn test_roundtrip() { - for tick_index in [ - MIN_TICK + 1, // we can't use extremes because of rounding during roundtrip conversion - -1000, - -100, - -10, - -4, - -2, - 0, - 2, - 4, - 10, - 100, - 1000, - MAX_TICK - 1, - ] - .iter() - { - let sqrt_price = get_sqrt_ratio_at_tick(*tick_index).unwrap(); - let round_trip_tick_index = get_tick_at_sqrt_ratio(sqrt_price).unwrap(); - assert_eq!(round_trip_tick_index, *tick_index); - } - } - - #[test] - fn test_u256_to_u64f64_q64_96() { - // Test tick 0 (sqrt price = 1.0 * 2^96) - let tick0_sqrt_price = U256::from(1u128 << 96); - let fixed_price = u256_q64_96_to_u64f64(tick0_sqrt_price).unwrap(); - - // Should be 1.0 in U64F64 - assert_eq!(fixed_price, U64F64::from_num(1.0)); - - // Round trip back to U256 Q64.96 - let back_to_u256 = u64f64_to_u256_q64_96(fixed_price); - assert_eq!(back_to_u256, tick0_sqrt_price); - } - - #[test] - fn test_tick_index_to_sqrt_price() { - let tick_spacing = SqrtPrice::from_num(1.0001); - - // check tick bounds - assert_eq!( - TickIndex(MIN_TICK).try_to_sqrt_price(), - Err(TickMathError::TickOutOfBounds) - ); - - assert_eq!( - TickIndex(MAX_TICK).try_to_sqrt_price(), - Err(TickMathError::TickOutOfBounds), - ); - - assert!( - TickIndex::MAX.try_to_sqrt_price().unwrap().abs_diff( - TickIndex::new_unchecked(TickIndex::MAX.get() + 1).as_sqrt_price_bounded() - ) < SqrtPrice::from_num(1e-6) - ); - - assert!( - TickIndex::MIN.try_to_sqrt_price().unwrap().abs_diff( - TickIndex::new_unchecked(TickIndex::MIN.get() - 1).as_sqrt_price_bounded() - ) < SqrtPrice::from_num(1e-6) - ); - - // At tick index 0, the sqrt price should be 1.0 - let sqrt_price = TickIndex(0).try_to_sqrt_price().unwrap(); - assert_eq!(sqrt_price, SqrtPrice::from_num(1.0)); - - let sqrt_price = TickIndex(2).try_to_sqrt_price().unwrap(); - assert!(sqrt_price.abs_diff(tick_spacing) < SqrtPrice::from_num(1e-10)); - - let sqrt_price = TickIndex(4).try_to_sqrt_price().unwrap(); - // Calculate the expected value: (1 + TICK_SPACING/1e9 + 1.0)^2 - let expected = tick_spacing * tick_spacing; - assert!(sqrt_price.abs_diff(expected) < SqrtPrice::from_num(1e-10)); - - // Test with tick index 10 - let sqrt_price = TickIndex(10).try_to_sqrt_price().unwrap(); - // Calculate the expected value: (1 + TICK_SPACING/1e9 + 1.0)^5 - let expected = tick_spacing.checked_pow(5).unwrap(); - assert!( - sqrt_price.abs_diff(expected) < SqrtPrice::from_num(1e-10), - "diff: {}", - sqrt_price.abs_diff(expected), - ); - } - - #[test] - fn test_sqrt_price_to_tick_index() { - let tick_spacing = SqrtPrice::from_num(1.0001); - let tick_index = TickIndex::try_from_sqrt_price(SqrtPrice::from_num(1.0)).unwrap(); - assert_eq!(tick_index, TickIndex::new_unchecked(0)); - - // Test with sqrt price equal to tick_spacing_tao (should be tick index 2) - let epsilon = SqrtPrice::from_num(0.0000000000000001); - assert!( - TickIndex::new_unchecked(2) - .as_sqrt_price_bounded() - .abs_diff(tick_spacing) - < epsilon - ); - - // Test with sqrt price equal to tick_spacing_tao^2 (should be tick index 4) - let sqrt_price = tick_spacing * tick_spacing; - assert!( - TickIndex::new_unchecked(4) - .as_sqrt_price_bounded() - .abs_diff(sqrt_price) - < epsilon - ); - - // Test with sqrt price equal to tick_spacing_tao^5 (should be tick index 10) - let sqrt_price = tick_spacing.checked_pow(5).unwrap(); - assert!( - TickIndex::new_unchecked(10) - .as_sqrt_price_bounded() - .abs_diff(sqrt_price) - < epsilon - ); - } - - #[test] - fn test_roundtrip_tick_index_sqrt_price() { - for i32_value in [ - TickIndex::MIN.get(), - -1000, - -100, - -10, - -4, - -2, - 0, - 2, - 4, - 10, - 100, - 1000, - TickIndex::MAX.get(), - ] - .into_iter() - { - let tick_index = TickIndex::new_unchecked(i32_value); - let sqrt_price = tick_index.try_to_sqrt_price().unwrap(); - let round_trip_tick_index = TickIndex::try_from_sqrt_price(sqrt_price).unwrap(); - assert_eq!(round_trip_tick_index, tick_index); - } - } - - #[test] - fn test_from_offset_index() { - // Test various tick indices - for i32_value in [ - TickIndex::MIN.get(), - -1000, - -100, - -10, - 0, - 10, - 100, - 1000, - TickIndex::MAX.get(), - ] { - let original_tick = TickIndex::new_unchecked(i32_value); - - // Calculate the offset index (adding OFFSET) - let offset_index = (i32_value + TickIndex::OFFSET.get()) as u32; - - // Convert back from offset index to tick index - let roundtrip_tick = TickIndex::from_offset_index(offset_index).unwrap(); - - // Check that we get the same tick index back - assert_eq!(original_tick, roundtrip_tick); - } - - // Test out of bounds values - let too_large = (TickIndex::MAX.get() + TickIndex::OFFSET.get() + 1) as u32; - assert!(TickIndex::from_offset_index(too_large).is_err()); - } - - #[test] - fn test_tick_price_sanity_check() { - let min_price = TickIndex::MIN.try_to_sqrt_price().unwrap(); - let max_price = TickIndex::MAX.try_to_sqrt_price().unwrap(); - - assert!(min_price > 0.); - assert!(max_price > 0.); - assert!(max_price > min_price); - assert!(min_price < 0.000001); - assert!(max_price > 10.); - - // Roundtrip conversions - let min_price_sqrt = TickIndex::MIN.try_to_sqrt_price().unwrap(); - let min_tick = TickIndex::try_from_sqrt_price(min_price_sqrt).unwrap(); - assert_eq!(min_tick, TickIndex::MIN); - - let max_price_sqrt: SqrtPrice = TickIndex::MAX.try_to_sqrt_price().unwrap(); - let max_tick = TickIndex::try_from_sqrt_price(max_price_sqrt).unwrap(); - assert_eq!(max_tick, TickIndex::MAX); - } - - #[test] - fn test_to_sqrt_price_bounded() { - assert_eq!( - TickIndex::MAX.as_sqrt_price_bounded(), - TickIndex::MAX.try_to_sqrt_price().unwrap() - ); - - assert_eq!( - TickIndex::MIN.as_sqrt_price_bounded(), - TickIndex::MIN.try_to_sqrt_price().unwrap() - ); - } - - mod active_tick_index_manager { - - use super::*; - - #[test] - fn test_tick_search_basic() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - ActiveTickIndexManager::::insert(netuid, TickIndex::MIN); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MAX) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.next().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MAX) - .is_none() - ); - assert!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .is_none() - ); - assert!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .is_none() - ); - assert!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.next().unwrap() - ) - .is_none() - ); - - ActiveTickIndexManager::::insert(netuid, TickIndex::MAX); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MAX) - .unwrap(), - TickIndex::MAX - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.next().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - }); - } - - #[test] - fn test_tick_search_sparse_queries() { - new_test_ext().execute_with(|| { - let active_index = TickIndex::MIN.saturating_add(10); - let netuid = NetUid::from(1); - - ActiveTickIndexManager::::insert(netuid, active_index); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, active_index) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.saturating_add(11) - ) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.saturating_add(12) - ) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.saturating_add(9) - ), - None - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, active_index) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.saturating_add(11) - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.saturating_add(12) - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MIN) - .unwrap(), - active_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.saturating_add(9) - ) - .unwrap(), - active_index - ); - }); - } - - #[test] - fn test_tick_search_many_lows() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - (0..1000).for_each(|i| { - ActiveTickIndexManager::::insert( - netuid, - TickIndex::MIN.saturating_add(i), - ); - }); - - for i in 0..1000 { - let test_index = TickIndex::MIN.saturating_add(i); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, test_index) - .unwrap(), - test_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, test_index) - .unwrap(), - test_index - ); - } - }); - } - - #[test] - fn test_tick_search_many_sparse() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let count = 1000; - - for i in 0..=count { - ActiveTickIndexManager::::insert( - netuid, - TickIndex::new_unchecked(i * 10), - ); - } - - for i in 1..count { - let tick = TickIndex::new_unchecked(i * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, tick).unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, tick).unwrap(), - tick - ); - for j in 1..=9 { - let before_tick = TickIndex::new_unchecked(i * 10 - j); - let after_tick = TickIndex::new_unchecked(i * 10 + j); - let prev_tick = TickIndex::new_unchecked((i - 1) * 10); - let next_tick = TickIndex::new_unchecked((i + 1) * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, before_tick) - .unwrap(), - prev_tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, after_tick) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, after_tick) - .unwrap(), - next_tick - ); - } - } - }); - } - - #[test] - fn test_tick_search_many_lows_sparse_reversed() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let count = 1000; - - for i in (0..=count).rev() { - ActiveTickIndexManager::::insert( - netuid, - TickIndex::new_unchecked(i * 10), - ); - } - - for i in 1..count { - let tick = TickIndex::new_unchecked(i * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, tick).unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, tick).unwrap(), - tick - ); - for j in 1..=9 { - let before_tick = TickIndex::new_unchecked(i * 10 - j); - let after_tick = TickIndex::new_unchecked(i * 10 + j); - let prev_tick = TickIndex::new_unchecked((i - 1) * 10); - let next_tick = TickIndex::new_unchecked((i + 1) * 10); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, before_tick) - .unwrap(), - prev_tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, after_tick) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, after_tick) - .unwrap(), - next_tick - ); - } - } - }); - } - - #[test] - fn test_tick_search_repeated_insertions() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let count = 1000; - - for _ in 0..10 { - for i in 0..=count { - let tick = TickIndex::new_unchecked(i * 10); - ActiveTickIndexManager::::insert(netuid, tick); - } - - for i in 1..count { - let tick = TickIndex::new_unchecked(i * 10); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, tick) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, tick) - .unwrap(), - tick - ); - for j in 1..=9 { - let before_tick = TickIndex::new_unchecked(i * 10 - j); - let after_tick = TickIndex::new_unchecked(i * 10 + j); - let prev_tick = TickIndex::new_unchecked((i - 1) * 10); - let next_tick = TickIndex::new_unchecked((i + 1) * 10); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - before_tick - ) - .unwrap(), - prev_tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, after_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_tick - ) - .unwrap(), - tick - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, after_tick - ) - .unwrap(), - next_tick - ); - } - } - } - }); - } - - #[test] - fn test_tick_search_full_range() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let step = 1019; - // Get the full valid tick range by subtracting MIN from MAX - let count = (TickIndex::MAX.get() - TickIndex::MIN.get()) / step; - - for i in 0..=count { - let index = TickIndex::MIN.saturating_add(i * step); - ActiveTickIndexManager::::insert(netuid, index); - } - for i in 1..count { - let index = TickIndex::MIN.saturating_add(i * step); - - let prev_index = TickIndex::new_unchecked(index.get() - step); - let next_minus_one = TickIndex::new_unchecked(index.get() + step - 1); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, prev_index) - .unwrap(), - prev_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, index).unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, next_minus_one) - .unwrap(), - index - ); - - let mid_next = TickIndex::new_unchecked(index.get() + step / 2); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, mid_next) - .unwrap(), - index - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, index).unwrap(), - index - ); - - let next_index = TickIndex::new_unchecked(index.get() + step); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, next_index) - .unwrap(), - next_index - ); - - let mid_next = TickIndex::new_unchecked(index.get() + step / 2); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, mid_next) - .unwrap(), - next_index - ); - - let next_minus_1 = TickIndex::new_unchecked(index.get() + step - 1); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, next_minus_1) - .unwrap(), - next_index - ); - for j in 1..=9 { - let before_index = TickIndex::new_unchecked(index.get() - j); - let after_index = TickIndex::new_unchecked(index.get() + j); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - before_index - ) - .unwrap(), - prev_index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, after_index) - .unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - before_index - ) - .unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - after_index - ) - .unwrap(), - next_index - ); - } - } - }); - } - - #[test] - fn test_tick_remove_basic() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - - ActiveTickIndexManager::::insert(netuid, TickIndex::MIN); - ActiveTickIndexManager::::insert(netuid, TickIndex::MAX); - ActiveTickIndexManager::::remove(netuid, TickIndex::MAX); - - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, TickIndex::MAX) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.saturating_div(2) - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MAX.prev().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_lower( - netuid, - TickIndex::MIN.next().unwrap() - ) - .unwrap(), - TickIndex::MIN - ); - - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MIN) - .unwrap(), - TickIndex::MIN - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, TickIndex::MAX), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.saturating_div(2) - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MAX.prev().unwrap() - ), - None - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher( - netuid, - TickIndex::MIN.next().unwrap() - ), - None - ); - }); - } - - #[test] - fn test_tick_remove_full_range() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(1); - let step = 1019; - // Get the full valid tick range by subtracting MIN from MAX - let count = (TickIndex::MAX.get() - TickIndex::MIN.get()) / step; - let remove_frequency = 5; // Remove every 5th tick - - // Insert ticks - for i in 0..=count { - let index = TickIndex::MIN.saturating_add(i * step); - ActiveTickIndexManager::::insert(netuid, index); - } - - // Remove some ticks - for i in 1..count { - if i % remove_frequency == 0 { - let index = TickIndex::MIN.saturating_add(i * step); - ActiveTickIndexManager::::remove(netuid, index); - } - } - - // Verify - for i in 1..count { - let index = TickIndex::MIN.saturating_add(i * step); - - if i % remove_frequency == 0 { - let lower = - ActiveTickIndexManager::::find_closest_lower(netuid, index); - let higher = - ActiveTickIndexManager::::find_closest_higher(netuid, index); - assert!(lower != Some(index)); - assert!(higher != Some(index)); - } else { - assert_eq!( - ActiveTickIndexManager::::find_closest_lower(netuid, index) - .unwrap(), - index - ); - assert_eq!( - ActiveTickIndexManager::::find_closest_higher(netuid, index) - .unwrap(), - index - ); - } - } - }); - } - } -} diff --git a/pallets/swap/src/weights.rs b/pallets/swap/src/weights.rs index 70a87eff3d..508626f6ae 100644 --- a/pallets/swap/src/weights.rs +++ b/pallets/swap/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_subtensor_swap` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-06-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervmrg6be`, CPU: `AMD EPYC 7763 64-Core Processor` +//! HOSTNAME: `runnervm3jyl0`, CPU: `AMD EPYC 9V74 80-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.xtxn9d9WXq +// --output=/tmp/tmp.BMe4BAnDcE // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -37,11 +37,6 @@ use core::marker::PhantomData; /// Weight functions needed for `pallet_subtensor_swap`. pub trait WeightInfo { fn set_fee_rate() -> Weight; - fn add_liquidity() -> Weight; - fn remove_liquidity() -> Weight; - fn modify_position() -> Weight; - fn disable_lp() -> Weight; - fn toggle_user_liquidity() -> Weight; } /// Weights for `pallet_subtensor_swap` using the Substrate node and recommended hardware. @@ -55,149 +50,11 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `529` // Estimated: `3994` - // Minimum execution time: 15_950_000 picoseconds. - Weight::from_parts(16_481_000, 3994) + // Minimum execution time: 14_491_000 picoseconds. + Weight::from_parts(15_063_000, 3994) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - fn add_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_484_000 picoseconds. - Weight::from_parts(2_675_000, 0) - } - /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:0) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::Owner` (r:1 w:0) - /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn remove_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1600` - // Estimated: `6096` - // Minimum execution time: 2_535_000 picoseconds. - Weight::from_parts(2_535_000, 6096) - } - /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) - /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::EnabledUserLiquidity` (r:1 w:0) - /// Proof: `Swap::EnabledUserLiquidity` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:0) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::Owner` (r:1 w:0) - /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn modify_position() -> Weight { - // Proof Size summary in bytes: - // Measured: `1645` - // Estimated: `6096` - // Minimum execution time: 2_484_000 picoseconds. - Weight::from_parts(2_484_000, 6096) - } - /// Storage: `Swap::EnabledUserLiquidity` (r:128 w:128) - /// Proof: `Swap::EnabledUserLiquidity` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::SwapV3Initialized` (r:128 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::Positions` (r:256 w:128) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::ValidatorTrust` (r:128 w:0) - /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::ValidatorPermit` (r:128 w:0) - /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::FeeGlobalTao` (r:128 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:128 w:0) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:256 w:256) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:128 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:128 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:128 w:128) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:128 w:128) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:128 w:128) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn disable_lp() -> Weight { - // Proof Size summary in bytes: - // Measured: `32696` - // Estimated: `670430` - // Minimum execution time: 795_151_000 picoseconds. - Weight::from_parts(795_151_000, 670430) - .saturating_add(T::DbWeight::get().reads(128_u64)) - .saturating_add(T::DbWeight::get().writes(128_u64)) - } - /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn toggle_user_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `538` - // Estimated: `4003` - // Minimum execution time: 13_054_000 picoseconds. - Weight::from_parts(13_335_000, 4003) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } } // For backwards compatibility and tests. @@ -210,147 +67,9 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `529` // Estimated: `3994` - // Minimum execution time: 15_950_000 picoseconds. - Weight::from_parts(16_481_000, 3994) + // Minimum execution time: 14_491_000 picoseconds. + Weight::from_parts(15_063_000, 3994) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - fn add_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 2_484_000 picoseconds. - Weight::from_parts(2_675_000, 0) - } - /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:0) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::Owner` (r:1 w:0) - /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetTAO` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn remove_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `1600` - // Estimated: `6096` - // Minimum execution time: 2_535_000 picoseconds. - Weight::from_parts(2_535_000, 6096) - } - /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubtokenEnabled` (r:1 w:0) - /// Proof: `SubtensorModule::SubtokenEnabled` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::EnabledUserLiquidity` (r:1 w:0) - /// Proof: `Swap::EnabledUserLiquidity` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::Positions` (r:1 w:1) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:1 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:1 w:1) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalTao` (r:1 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:1 w:0) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:2 w:2) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:1 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::Owner` (r:1 w:0) - /// Proof: `SubtensorModule::Owner` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaOut` (r:1 w:1) - /// Proof: `SubtensorModule::SubnetAlphaOut` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::StakingHotkeys` (r:1 w:1) - /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyAlpha` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeyAlpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::Alpha` (r:1 w:0) - /// Proof: `SubtensorModule::Alpha` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::AlphaV2` (r:1 w:1) - /// Proof: `SubtensorModule::AlphaV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeyShares` (r:1 w:0) - /// Proof: `SubtensorModule::TotalHotkeyShares` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::TotalHotkeySharesV2` (r:1 w:1) - /// Proof: `SubtensorModule::TotalHotkeySharesV2` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn modify_position() -> Weight { - // Proof Size summary in bytes: - // Measured: `1645` - // Estimated: `6096` - // Minimum execution time: 2_484_000 picoseconds. - Weight::from_parts(2_484_000, 6096) - } - /// Storage: `Swap::EnabledUserLiquidity` (r:128 w:128) - /// Proof: `Swap::EnabledUserLiquidity` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::SwapV3Initialized` (r:128 w:0) - /// Proof: `Swap::SwapV3Initialized` (`max_values`: None, `max_size`: Some(11), added: 2486, mode: `MaxEncodedLen`) - /// Storage: `Swap::Positions` (r:256 w:128) - /// Proof: `Swap::Positions` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::ValidatorTrust` (r:128 w:0) - /// Proof: `SubtensorModule::ValidatorTrust` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::ValidatorPermit` (r:128 w:0) - /// Proof: `SubtensorModule::ValidatorPermit` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Swap::FeeGlobalTao` (r:128 w:0) - /// Proof: `Swap::FeeGlobalTao` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentTick` (r:128 w:0) - /// Proof: `Swap::CurrentTick` (`max_values`: None, `max_size`: Some(14), added: 2489, mode: `MaxEncodedLen`) - /// Storage: `Swap::Ticks` (r:256 w:256) - /// Proof: `Swap::Ticks` (`max_values`: None, `max_size`: Some(78), added: 2553, mode: `MaxEncodedLen`) - /// Storage: `Swap::FeeGlobalAlpha` (r:128 w:0) - /// Proof: `Swap::FeeGlobalAlpha` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::AlphaSqrtPrice` (r:128 w:0) - /// Proof: `Swap::AlphaSqrtPrice` (`max_values`: None, `max_size`: Some(26), added: 2501, mode: `MaxEncodedLen`) - /// Storage: `Swap::CurrentLiquidity` (r:128 w:128) - /// Proof: `Swap::CurrentLiquidity` (`max_values`: None, `max_size`: Some(18), added: 2493, mode: `MaxEncodedLen`) - /// Storage: `SubtensorModule::SubnetTAO` (r:128 w:128) - /// Proof: `SubtensorModule::SubnetTAO` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `SubtensorModule::SubnetAlphaIn` (r:128 w:128) - /// Proof: `SubtensorModule::SubnetAlphaIn` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn disable_lp() -> Weight { - // Proof Size summary in bytes: - // Measured: `32696` - // Estimated: `670430` - // Minimum execution time: 795_151_000 picoseconds. - Weight::from_parts(795_151_000, 670430) - .saturating_add(RocksDbWeight::get().reads(128_u64)) - .saturating_add(RocksDbWeight::get().writes(128_u64)) - } - /// Storage: `SubtensorModule::NetworksAdded` (r:1 w:0) - /// Proof: `SubtensorModule::NetworksAdded` (`max_values`: None, `max_size`: None, mode: `Measured`) - fn toggle_user_liquidity() -> Weight { - // Proof Size summary in bytes: - // Measured: `538` - // Estimated: `4003` - // Minimum execution time: 13_054_000 picoseconds. - Weight::from_parts(13_335_000, 4003) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } } diff --git a/pallets/transaction-fee/Cargo.toml b/pallets/transaction-fee/Cargo.toml index 9e19bf2758..22f89b5f4c 100644 --- a/pallets/transaction-fee/Cargo.toml +++ b/pallets/transaction-fee/Cargo.toml @@ -91,4 +91,5 @@ runtime-benchmarks = [ "pallet-subtensor/runtime-benchmarks", "pallet-subtensor-swap/runtime-benchmarks", "subtensor-runtime-common/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks", ] diff --git a/pallets/transaction-fee/src/lib.rs b/pallets/transaction-fee/src/lib.rs index eab3006d79..fbbe2cd805 100644 --- a/pallets/transaction-fee/src/lib.rs +++ b/pallets/transaction-fee/src/lib.rs @@ -200,7 +200,7 @@ where ) .map(|tao_amount| (alpha_fee, tao_amount, *netuid)) .map_err(|err| { - log::error!("Error withdrawing transaction fee in alpha: {err:?}"); + log::warn!("Error withdrawing transaction fee in alpha: {err:?}"); InvalidTransaction::Payment.into() }) } else { diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 343decb8a8..7d8508c5c0 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -6,12 +6,10 @@ use crate::TransactionFeeHandler; use frame_support::pallet_prelude::Zero; use frame_support::{ PalletId, assert_ok, derive_impl, parameter_types, - traits::{Everything, Hooks, InherentBuilder, PrivilegeCmp}, + traits::{Everything, Hooks, PrivilegeCmp}, weights::IdentityFee, }; -use frame_system::{ - self as system, EnsureRoot, RawOrigin, limits, offchain::CreateTransactionBase, -}; +use frame_system::{self as system, EnsureRoot, RawOrigin, limits}; pub use pallet_subtensor::*; pub use sp_core::U256; use sp_core::{ConstU64, H256}; @@ -195,6 +193,10 @@ parameter_types! { pub const InitialMaxBurn: TaoBalance = TaoBalance::new(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 MinTempo: u16 = pallet_subtensor::MIN_TEMPO; + pub const MaxTempo: u16 = pallet_subtensor::MAX_TEMPO; + pub const MinActivityCutoffFactorMilli: u32 = pallet_subtensor::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const MaxActivityCutoffFactorMilli: u32 = pallet_subtensor::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const InitialValidatorPruneLen: u64 = 0; pub const InitialScalingLawPower: u16 = 50; pub const InitialMaxAllowedValidators: u16 = 100; @@ -233,6 +235,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = 0; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const MaxEpochsPerBlock: u8 = 32; } impl pallet_subtensor::Config for Test { @@ -281,6 +284,10 @@ impl pallet_subtensor::Config for Test { type InitialMinStake = InitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = MinTempo; + type MaxTempo = MaxTempo; + type MinActivityCutoffFactorMilli = MinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = MaxActivityCutoffFactorMilli; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; @@ -312,6 +319,7 @@ impl pallet_subtensor::Config for Test { type AuthorshipProvider = MockAuthorshipProvider; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = MaxEpochsPerBlock; type WeightInfo = (); } @@ -415,7 +423,6 @@ impl pallet_alpha_assets::Config for Test {} 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(1_000_000).unwrap(); } @@ -427,7 +434,6 @@ impl pallet_subtensor_swap::Config for Test { type TaoReserve = pallet_subtensor::TaoBalanceReserve; type AlphaReserve = pallet_subtensor::AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); @@ -523,39 +529,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Test +impl frame_system::offchain::CreateBare for Test where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Test -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - let extra: TransactionExtensions = ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(0.into()), - ); - - Some(UncheckedExtrinsic::new_signed( - call, - nonce.into(), - (), - extra, - )) + UncheckedExtrinsic::new_bare(call) } } @@ -592,8 +571,7 @@ pub fn register_ok_neuron( // Ensure reserves exist for swap/burn path, but do NOT clobber reserves if the test already set them. let reserve: u64 = 1_000_000_000_000; let tao_reserve = SubnetTAO::::get(netuid); - let alpha_reserve = - SubnetAlphaIn::::get(netuid) + SubnetAlphaInProvided::::get(netuid); + let alpha_reserve = SubnetAlphaIn::::get(netuid); if tao_reserve.is_zero() && alpha_reserve.is_zero() { setup_reserves(netuid, reserve.into(), reserve.into()); @@ -822,10 +800,6 @@ pub fn setup_subnets(sncount: u16, neurons: u16) -> TestSetup { } } -pub(crate) fn remove_stake_rate_limit_for_tests(hotkey: &U256, coldkey: &U256, netuid: NetUid) { - StakingOperationRateLimiter::::remove((hotkey, coldkey, netuid)); -} - #[allow(dead_code)] pub fn setup_stake( netuid: subtensor_runtime_common::NetUid, @@ -845,7 +819,6 @@ pub fn setup_stake( netuid, stake_amount.into(), )); - remove_stake_rate_limit_for_tests(hotkey, coldkey, netuid); } pub(crate) fn quote_remove_stake_after_alpha_fee( diff --git a/pallets/transaction-fee/src/tests/mod.rs b/pallets/transaction-fee/src/tests/mod.rs index 8203070d76..f452634cfe 100644 --- a/pallets/transaction-fee/src/tests/mod.rs +++ b/pallets/transaction-fee/src/tests/mod.rs @@ -5,12 +5,10 @@ use frame_support::dispatch::GetDispatchInfo; use frame_support::pallet_prelude::Zero; use frame_support::traits::Currency; use frame_support::{assert_err, assert_ok}; -use pallet_subtensor_swap::AlphaSqrtPrice; use sp_runtime::{ traits::{DispatchTransaction, TransactionExtension, TxBaseImplication}, transaction_validity::{InvalidTransaction, TransactionValidityError}, }; -use substrate_fixed::types::U64F64; use subtensor_runtime_common::AlphaBalance; use mock::*; @@ -709,7 +707,9 @@ fn test_remove_stake_edge_alpha() { assert_ok!(result); // Lower Alpha price to 0.0001 so that there is not enough alpha to cover tx fees - AlphaSqrtPrice::::insert(sn.subnets[0].netuid, U64F64::from_num(0.01)); + SubnetTAO::::insert(sn.subnets[0].netuid, TaoBalance::from(1_000_000)); + SubnetAlphaIn::::insert(sn.subnets[0].netuid, AlphaBalance::from(10_000_000_000_u64)); + let result_low_alpha_price = ext.validate( RuntimeOrigin::signed(sn.coldkey).into(), &call.clone(), @@ -1540,7 +1540,6 @@ fn test_add_stake_fees_go_to_block_builder() { let (_, swap_fee) = mock::swap_tao_to_alpha(sn.subnets[0].netuid, stake_amount.into()); add_balance_to_coldkey_account(&sn.coldkey, (stake_amount * 10).into()); - remove_stake_rate_limit_for_tests(&sn.hotkeys[0], &sn.coldkey, sn.subnets[0].netuid); // Stake let balance_before = Balances::free_balance(sn.coldkey); diff --git a/pallets/utility/src/weights.rs b/pallets/utility/src/weights.rs index 0287a464fe..928aef0269 100644 --- a/pallets/utility/src/weights.rs +++ b/pallets/utility/src/weights.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_subtensor_utility` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-05-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-06-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runnervm3jyl0`, CPU: `AMD EPYC 9V74 80-Core Processor` +//! HOSTNAME: `runnervm7b5n9`, CPU: `AMD EPYC 7763 64-Core Processor` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -22,7 +22,7 @@ // --no-storage-info // --no-min-squares // --no-median-slopes -// --output=/tmp/tmp.wsE00cetaq +// --output=/tmp/tmp.5J6YDU3hE3 // --template=/home/runner/work/subtensor/subtensor/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -57,10 +57,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_006_000 picoseconds. - Weight::from_parts(12_108_520, 3983) - // Standard Error: 3_458 - .saturating_add(Weight::from_parts(5_277_918, 0).saturating_mul(c.into())) + // Minimum execution time: 4_638_000 picoseconds. + Weight::from_parts(19_446_696, 3983) + // Standard Error: 1_676 + .saturating_add(Weight::from_parts(5_450_264, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -71,8 +71,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 13_480_000 picoseconds. - Weight::from_parts(13_830_000, 3983) + // Minimum execution time: 14_878_000 picoseconds. + Weight::from_parts(15_378_000, 3983) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -84,18 +84,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 3_825_000 picoseconds. - Weight::from_parts(15_244_012, 3983) - // Standard Error: 2_052 - .saturating_add(Weight::from_parts(5_505_817, 0).saturating_mul(c.into())) + // Minimum execution time: 4_588_000 picoseconds. + Weight::from_parts(13_428_230, 3983) + // Standard Error: 2_092 + .saturating_add(Weight::from_parts(5_663_250, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_398_000 picoseconds. - Weight::from_parts(5_768_000, 0) + // Minimum execution time: 6_542_000 picoseconds. + Weight::from_parts(6_893_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -106,18 +106,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 3_896_000 picoseconds. - Weight::from_parts(1_067_442, 3983) - // Standard Error: 4_142 - .saturating_add(Weight::from_parts(5_312_644, 0).saturating_mul(c.into())) + // Minimum execution time: 4_699_000 picoseconds. + Weight::from_parts(12_134_867, 3983) + // Standard Error: 3_490 + .saturating_add(Weight::from_parts(5_477_293, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } fn dispatch_as_fallible() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_498_000 picoseconds. - Weight::from_parts(5_708_000, 0) + // Minimum execution time: 6_813_000 picoseconds. + Weight::from_parts(7_113_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -127,8 +127,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 18_757_000 picoseconds. - Weight::from_parts(19_459_000, 3983) + // Minimum execution time: 21_140_000 picoseconds. + Weight::from_parts(21_831_000, 3983) .saturating_add(T::DbWeight::get().reads(2_u64)) } } @@ -144,10 +144,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 4_006_000 picoseconds. - Weight::from_parts(12_108_520, 3983) - // Standard Error: 3_458 - .saturating_add(Weight::from_parts(5_277_918, 0).saturating_mul(c.into())) + // Minimum execution time: 4_638_000 picoseconds. + Weight::from_parts(19_446_696, 3983) + // Standard Error: 1_676 + .saturating_add(Weight::from_parts(5_450_264, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -158,8 +158,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 13_480_000 picoseconds. - Weight::from_parts(13_830_000, 3983) + // Minimum execution time: 14_878_000 picoseconds. + Weight::from_parts(15_378_000, 3983) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) @@ -171,18 +171,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 3_825_000 picoseconds. - Weight::from_parts(15_244_012, 3983) - // Standard Error: 2_052 - .saturating_add(Weight::from_parts(5_505_817, 0).saturating_mul(c.into())) + // Minimum execution time: 4_588_000 picoseconds. + Weight::from_parts(13_428_230, 3983) + // Standard Error: 2_092 + .saturating_add(Weight::from_parts(5_663_250, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } fn dispatch_as() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_398_000 picoseconds. - Weight::from_parts(5_768_000, 0) + // Minimum execution time: 6_542_000 picoseconds. + Weight::from_parts(6_893_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -193,18 +193,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 3_896_000 picoseconds. - Weight::from_parts(1_067_442, 3983) - // Standard Error: 4_142 - .saturating_add(Weight::from_parts(5_312_644, 0).saturating_mul(c.into())) + // Minimum execution time: 4_699_000 picoseconds. + Weight::from_parts(12_134_867, 3983) + // Standard Error: 3_490 + .saturating_add(Weight::from_parts(5_477_293, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) } fn dispatch_as_fallible() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_498_000 picoseconds. - Weight::from_parts(5_708_000, 0) + // Minimum execution time: 6_813_000 picoseconds. + Weight::from_parts(7_113_000, 0) } /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -214,8 +214,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `518` // Estimated: `3983` - // Minimum execution time: 18_757_000 picoseconds. - Weight::from_parts(19_459_000, 3983) + // Minimum execution time: 21_140_000 picoseconds. + Weight::from_parts(21_831_000, 3983) .saturating_add(RocksDbWeight::get().reads(2_u64)) } } diff --git a/precompiles/Cargo.toml b/precompiles/Cargo.toml index c896ecb731..dd5e20dfd0 100644 --- a/precompiles/Cargo.toml +++ b/precompiles/Cargo.toml @@ -99,6 +99,7 @@ runtime-benchmarks = [ "pallet-timestamp/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "subtensor-runtime-common/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks" ] [dev-dependencies] diff --git a/precompiles/src/alpha.rs b/precompiles/src/alpha.rs index b183c5ec23..9840c42575 100644 --- a/precompiles/src/alpha.rs +++ b/precompiles/src/alpha.rs @@ -1,16 +1,16 @@ use core::marker::PhantomData; +use crate::PrecompileExt; use fp_evm::{ExitError, PrecompileFailure}; use pallet_evm::{BalanceConverter, PrecompileHandle, SubstrateBalance}; use precompile_utils::EvmResult; +use sp_runtime::{SaturatedConversion, Vec}; + +use crate::PrecompileHandleExt; use sp_core::U256; -use sp_std::vec::Vec; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{NetUid, Token}; use subtensor_swap_interface::{Order, SwapHandler}; - -use crate::PrecompileExt; - pub struct AlphaPrecompile(PhantomData); impl PrecompileExt for AlphaPrecompile @@ -34,10 +34,12 @@ where { #[precompile::public("getAlphaPrice(uint16)")] #[precompile::view] - fn get_alpha_price(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_alpha_price(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + // SubnetMechanism + SubnetAlphaIn + SubnetTAO + SwapBalancer reads + handle.record_db_reads::(4)?; let current_alpha_price = as SwapHandler>::current_alpha_price(netuid.into()); - let price = current_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let price = current_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000)); let price: SubstrateBalance = price.saturating_to_num::().into(); let price_eth = ::BalanceConverter::into_evm_balance(price) .map(|amount| amount.into_u256()) @@ -48,10 +50,12 @@ where #[precompile::public("getMovingAlphaPrice(uint16)")] #[precompile::view] - fn get_moving_alpha_price(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { - let moving_alpha_price: U96F32 = + fn get_moving_alpha_price(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + // SubnetMechanism + SubnetMovingPrice reads + handle.record_db_reads::(2)?; + let moving_alpha_price: U64F64 = pallet_subtensor::Pallet::::get_moving_alpha_price(netuid.into()); - let price = moving_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let price = moving_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000)); let price: SubstrateBalance = price.saturating_to_num::().into(); let price_eth = ::BalanceConverter::into_evm_balance(price) .map(|amount| amount.into_u256()) @@ -62,38 +66,45 @@ where #[precompile::public("getTaoInPool(uint16)")] #[precompile::view] - fn get_tao_in_pool(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_tao_in_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetTAO::::get(NetUid::from(netuid)).to_u64()) } #[precompile::public("getAlphaInPool(uint16)")] #[precompile::view] - fn get_alpha_in_pool(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_alpha_in_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetAlphaIn::::get(NetUid::from(netuid)).into()) } #[precompile::public("getAlphaOutPool(uint16)")] #[precompile::view] - fn get_alpha_out_pool(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_alpha_out_pool(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetAlphaOut::::get(NetUid::from(netuid)).into()) } #[precompile::public("getAlphaIssuance(uint16)")] #[precompile::view] - fn get_alpha_issuance(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_alpha_issuance(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + // SubnetAlphaIn + SubnetAlphaOut reads + handle.record_db_reads::(2)?; Ok(pallet_subtensor::Pallet::::get_alpha_issuance(netuid.into()).into()) } #[precompile::public("getTaoWeight()")] #[precompile::view] - fn get_tao_weight(_handle: &mut impl PrecompileHandle) -> EvmResult { + fn get_tao_weight(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; let tao_weight = pallet_subtensor::TaoWeight::::get(); Ok(U256::from(tao_weight)) } #[precompile::public("getCKBurn()")] #[precompile::view] - fn get_ck_burn(_handle: &mut impl PrecompileHandle) -> EvmResult { + fn get_ck_burn(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_db_reads::(1)?; let ck_burn = pallet_subtensor::CKBurn::::get(); Ok(U256::from(ck_burn)) } @@ -101,10 +112,13 @@ where #[precompile::public("simSwapTaoForAlpha(uint16,uint64)")] #[precompile::view] fn sim_swap_tao_for_alpha( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, tao: u64, ) -> EvmResult { + // SubnetMechanism + swap simulation reads + handle.record_db_reads::(9)?; + let order = pallet_subtensor::GetAlphaForTao::::with_amount(tao); let swap_result = as SwapHandler>::sim_swap(netuid.into(), order) @@ -117,10 +131,13 @@ where #[precompile::public("simSwapAlphaForTao(uint16,uint64)")] #[precompile::view] fn sim_swap_alpha_for_tao( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, alpha: u64, ) -> EvmResult { + // SubnetMechanism + swap simulation reads + handle.record_db_reads::(9)?; + let order = pallet_subtensor::GetTaoForAlpha::::with_amount(alpha); let swap_result = as SwapHandler>::sim_swap(netuid.into(), order) @@ -132,7 +149,8 @@ where #[precompile::public("getSubnetMechanism(uint16)")] #[precompile::view] - fn get_subnet_mechanism(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_subnet_mechanism(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetMechanism::::get(NetUid::from( netuid, ))) @@ -147,9 +165,10 @@ where #[precompile::public("getEMAPriceHalvingBlocks(uint16)")] #[precompile::view] fn get_ema_price_halving_blocks( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::EMAPriceHalvingBlocks::::get( NetUid::from(netuid), )) @@ -157,7 +176,8 @@ where #[precompile::public("getSubnetVolume(uint16)")] #[precompile::view] - fn get_subnet_volume(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_subnet_volume(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(U256::from(pallet_subtensor::SubnetVolume::::get( NetUid::from(netuid), ))) @@ -165,7 +185,8 @@ where #[precompile::public("getTaoInEmission(uint16)")] #[precompile::view] - fn get_tao_in_emission(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_tao_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(U256::from( pallet_subtensor::SubnetTaoInEmission::::get(NetUid::from(netuid)).to_u64(), )) @@ -173,7 +194,8 @@ where #[precompile::public("getAlphaInEmission(uint16)")] #[precompile::view] - fn get_alpha_in_emission(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_alpha_in_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(U256::from( pallet_subtensor::SubnetAlphaInEmission::::get(NetUid::from(netuid)).to_u64(), )) @@ -181,7 +203,8 @@ where #[precompile::public("getAlphaOutEmission(uint16)")] #[precompile::view] - fn get_alpha_out_emission(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_alpha_out_emission(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(U256::from( pallet_subtensor::SubnetAlphaOutEmission::::get(NetUid::from(netuid)).to_u64(), )) @@ -189,23 +212,38 @@ where #[precompile::public("getSumAlphaPrice()")] #[precompile::view] - fn get_sum_alpha_price(_handle: &mut impl PrecompileHandle) -> EvmResult { + fn get_sum_alpha_price(handle: &mut impl PrecompileHandle) -> EvmResult { + // NetworksAdded iteration + current_alpha_price reads + handle.record_db_reads::(1)?; + let subnet_limit = pallet_subtensor::SubnetLimit::::get().saturated_into::(); + + handle.record_db_reads::(subnet_limit)?; + + let mut sum_alpha_price: U64F64 = U64F64::from_num(0); let netuids = pallet_subtensor::NetworksAdded::::iter() .filter(|(netuid, _)| *netuid != NetUid::ROOT) + .map(|(netuid, _)| netuid) .collect::>(); - let mut sum_alpha_price: U96F32 = U96F32::from_num(0); - for (netuid, _) in netuids { - let price = as SwapHandler>::current_alpha_price( - netuid.into(), - ); + // NetworksAdded entry + current_alpha_price reads + handle.record_db_reads::( + netuids + .len() + .saturated_into::() + .saturating_mul(5) + .saturating_sub(subnet_limit), + )?; + + for netuid in netuids.iter() { + let price = + as SwapHandler>::current_alpha_price(*netuid); - if price < U96F32::from_num(1) { + if price < U64F64::from_num(1) { sum_alpha_price = sum_alpha_price.saturating_add(price); } } - let price = sum_alpha_price.saturating_mul(U96F32::from_num(1_000_000_000)); + let price = sum_alpha_price.saturating_mul(U64F64::from_num(1_000_000_000)); let price: SubstrateBalance = price.saturating_to_num::().into(); let price_eth = ::BalanceConverter::into_evm_balance(price) .map(|amount| amount.into_u256()) @@ -311,8 +349,8 @@ mod tests { let moving_alpha_price = pallet_subtensor::Pallet::::get_moving_alpha_price(dynamic_netuid); - assert!(alpha_price > U96F32::from_num(1)); - assert!(moving_alpha_price > U96F32::from_num(1)); + assert!(alpha_price > U64F64::from_num(1)); + assert!(moving_alpha_price > U64F64::from_num(1)); assert_static_call( &precompiles, @@ -457,7 +495,7 @@ mod tests { let caller = addr_from_index(1); let precompile_addr = addr_from_index(AlphaPrecompile::::INDEX); - let mut sum_alpha_price = U96F32::from_num(0); + let mut sum_alpha_price = U64F64::from_num(0); for (netuid, _) in pallet_subtensor::NetworksAdded::::iter() { if netuid.is_root() { continue; @@ -466,12 +504,12 @@ mod tests { as SwapHandler>::current_alpha_price( netuid, ); - if price < U96F32::from_num(1) { + if price < U64F64::from_num(1) { sum_alpha_price += price; } } - assert!(sum_alpha_price > U96F32::from_num(0)); + assert!(sum_alpha_price > U64F64::from_num(0)); assert_static_call( &precompiles, diff --git a/precompiles/src/crowdloan.rs b/precompiles/src/crowdloan.rs index c474ab9405..1c66d941ca 100644 --- a/precompiles/src/crowdloan.rs +++ b/precompiles/src/crowdloan.rs @@ -75,9 +75,10 @@ where #[precompile::public("getCrowdloan(uint32)")] #[precompile::view] fn get_crowdloan( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, crowdloan_id: u32, ) -> EvmResult { + handle.record_db_reads::(1)?; let crowdloan = pallet_crowdloan::Crowdloans::::get(crowdloan_id).ok_or( PrecompileFailure::Error { exit_status: ExitError::Other("Crowdloan not found".into()), @@ -105,10 +106,11 @@ where #[precompile::public("getContribution(uint32,bytes32)")] #[precompile::view] fn get_contribution( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, crowdloan_id: u32, coldkey: H256, ) -> EvmResult { + handle.record_db_reads::(1)?; let coldkey = R::AccountId::from(coldkey.0); let contribution = pallet_crowdloan::Contributions::::get(crowdloan_id, coldkey).ok_or( PrecompileFailure::Error { diff --git a/precompiles/src/extensions.rs b/precompiles/src/extensions.rs index 4a7c418c86..984b70a91b 100644 --- a/precompiles/src/extensions.rs +++ b/precompiles/src/extensions.rs @@ -3,25 +3,16 @@ extern crate alloc; use alloc::format; use frame_support::dispatch::{DispatchInfo, GetDispatchInfo, Pays, PostDispatchInfo}; -use frame_support::traits::IsSubType; use frame_system::RawOrigin; use pallet_admin_utils::{PrecompileEnable, PrecompileEnum}; use pallet_evm::{ AddressMapping, BalanceConverter, EvmBalance, ExitError, GasWeightMapping, Precompile, PrecompileFailure, PrecompileHandle, PrecompileResult, }; -use pallet_subtensor::SubtensorTransactionExtension; use precompile_utils::EvmResult; -use scale_info::TypeInfo; +use precompile_utils::prelude::RuntimeHelper; use sp_core::{H160, U256, blake2_256}; -use sp_runtime::{ - DispatchResult, - traits::{ - AsSystemOriginSigner, Dispatchable, ExtensionPostDispatchWeightHandler, - TransactionExtension, TxBaseImplication, - }, - transaction_validity::{TransactionSource, TransactionValidityError}, -}; +use sp_runtime::traits::{Dispatchable, ExtensionPostDispatchWeightHandler}; use sp_std::vec::Vec; use subtensor_runtime_common::with_evm_context; @@ -34,6 +25,23 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { ::AddressMapping::into_account_id(self.context().caller) } + fn record_db_reads(&mut self, reads: u64) -> EvmResult<()> + where + R: frame_system::Config + pallet_evm::Config, + { + self.record_cost(RuntimeHelper::::db_read_gas_cost().saturating_mul(reads))?; + Ok(()) + } + + fn record_db_writes(&mut self, writes: u64) -> EvmResult<()> + where + R: frame_system::Config + pallet_evm::Config, + { + self.record_cost(RuntimeHelper::::db_write_gas_cost().saturating_mul(writes))?; + + Ok(()) + } + fn try_convert_apparent_value(&self) -> EvmResult where R: pallet_evm::Config, @@ -56,30 +64,17 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { ) -> EvmResult<()> where R: frame_system::Config - + pallet_balances::Config + pallet_evm::Config + pallet_subtensor::Config + pallet_shield::Config - + pallet_subtensor_proxy::Config - + Send - + Sync - + TypeInfo, + + pallet_subtensor_proxy::Config, ::RuntimeCall: From, - ::RuntimeCall: GetDispatchInfo - + Dispatchable - + IsSubType> - + IsSubType> - + IsSubType> - + IsSubType>, - ::RuntimeOrigin: - From> + AsSystemOriginSigner + Clone, + ::RuntimeCall: + GetDispatchInfo + Dispatchable, + ::RuntimeOrigin: From> + Clone, { let call = ::RuntimeCall::from(call); - let mut info = GetDispatchInfo::get_dispatch_info(&call); - let subtensor_extension = SubtensorTransactionExtension::::new(); - info.extension_weight = info - .extension_weight - .saturating_add(subtensor_extension.weight(&call)); + let info = GetDispatchInfo::get_dispatch_info(&call); let target_gas = self.gas_limit(); if let Some(gas) = target_gas { @@ -99,29 +94,10 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { )?; let origin = ::RuntimeOrigin::from(origin); - let (_, val, origin) = subtensor_extension - .validate( - origin, - &call, - &info, - 0, - (), - &TxBaseImplication(()), - TransactionSource::External, - ) - .map_err(extension_error)?; - subtensor_extension - .prepare(val, &origin, &call, &info, 0) - .map_err(extension_error)?; match with_evm_context(|| call.dispatch(origin)) { Ok(mut post_info) => { post_info.set_extension_weight(&info); - let result: DispatchResult = Ok(()); - as TransactionExtension< - ::RuntimeCall, - >>::post_dispatch((), &info, &mut post_info, 0, &result) - .map_err(extension_error)?; log::debug!("Dispatch succeeded. Post info: {post_info:?}"); self.charge_and_refund_after_dispatch::(&info, &post_info)?; @@ -131,11 +107,6 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { let err_str: &'static str = e.into(); let mut post_info = e.post_info; post_info.set_extension_weight(&info); - let result: DispatchResult = Err(e.error); - as TransactionExtension< - ::RuntimeCall, - >>::post_dispatch((), &info, &mut post_info, 0, &result) - .map_err(extension_error)?; log::info!("Precompile dispatch failed. message as: {e:?}"); self.charge_and_refund_after_dispatch::(&info, &post_info)?; @@ -179,12 +150,6 @@ pub(crate) trait PrecompileHandleExt: PrecompileHandle { } } -fn extension_error(err: TransactionValidityError) -> PrecompileFailure { - PrecompileFailure::Error { - exit_status: ExitError::Other(format!("transaction extension rejected: {err:?}").into()), - } -} - impl PrecompileHandleExt for T where T: PrecompileHandle {} pub trait PrecompileExt>: Precompile { diff --git a/precompiles/src/leasing.rs b/precompiles/src/leasing.rs index 005782c776..5ebf03cb3c 100644 --- a/precompiles/src/leasing.rs +++ b/precompiles/src/leasing.rs @@ -73,7 +73,8 @@ where { #[precompile::public("getLease(uint32)")] #[precompile::view] - fn get_lease(_handle: &mut impl PrecompileHandle, lease_id: u32) -> EvmResult { + fn get_lease(handle: &mut impl PrecompileHandle, lease_id: u32) -> EvmResult { + handle.record_db_reads::(1)?; let lease = pallet_subtensor::SubnetLeases::::get(lease_id).ok_or(PrecompileFailure::Error { exit_status: ExitError::Other("Lease not found".into()), @@ -97,10 +98,11 @@ where #[precompile::public("getContributorShare(uint32,bytes32)")] #[precompile::view] fn get_contributor_share( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, lease_id: u32, contributor: H256, ) -> EvmResult<(u128, u128)> { + handle.record_db_reads::(1)?; let contributor = R::AccountId::from(contributor.0); let share = pallet_subtensor::SubnetLeaseShares::::get(lease_id, contributor); @@ -109,7 +111,8 @@ where #[precompile::public("getLeaseIdForSubnet(uint16)")] #[precompile::view] - fn get_lease_id_for_subnet(_handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_lease_id_for_subnet(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; let lease_id = pallet_subtensor::SubnetUidToLeaseId::::get(NetUid::from(netuid)).ok_or( PrecompileFailure::Error { exit_status: ExitError::Other("Lease not found for netuid".into()), diff --git a/precompiles/src/lib.rs b/precompiles/src/lib.rs index 39815a6946..cf54934d95 100644 --- a/precompiles/src/lib.rs +++ b/precompiles/src/lib.rs @@ -4,12 +4,22 @@ extern crate alloc; use core::marker::PhantomData; +use crate::extensions::*; +pub use address_mapping::AddressMappingPrecompile; +pub use alpha::AlphaPrecompile; +pub use balance_transfer::BalanceTransferPrecompile; +pub use crowdloan::CrowdloanPrecompile; +pub use ed25519::Ed25519Verify; +pub use extensions::PrecompileExt; use fp_evm::{ExitError, PrecompileFailure}; use frame_support::traits::IsSubType; use frame_support::{ dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::Decode, }; +pub use leasing::LeasingPrecompile; +pub use metagraph::MetagraphPrecompile; +pub use neuron::NeuronPrecompile; use pallet_admin_utils::PrecompileEnum; use pallet_evm::{ AddressMapping, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, @@ -21,26 +31,14 @@ use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; use pallet_subtensor_proxy as pallet_proxy; +pub use proxy::ProxyPrecompile; use sp_core::{H160, U256, crypto::ByteArray}; use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup}; -use subtensor_runtime_common::ProxyType; - -use crate::extensions::*; - -pub use address_mapping::AddressMappingPrecompile; -pub use alpha::AlphaPrecompile; -pub use balance_transfer::BalanceTransferPrecompile; -pub use crowdloan::CrowdloanPrecompile; -pub use ed25519::Ed25519Verify; -pub use extensions::PrecompileExt; -pub use leasing::LeasingPrecompile; -pub use metagraph::MetagraphPrecompile; -pub use neuron::NeuronPrecompile; -pub use proxy::ProxyPrecompile; pub use sr25519::Sr25519Verify; pub use staking::{StakingPrecompile, StakingPrecompileV2}; pub use storage_query::StorageQueryPrecompile; pub use subnet::SubnetPrecompile; +use subtensor_runtime_common::ProxyType; pub use uid_lookup::UidLookupPrecompile; pub use voting_power::VotingPowerPrecompile; diff --git a/precompiles/src/metagraph.rs b/precompiles/src/metagraph.rs index 4cffb76a4f..ec8086a87e 100644 --- a/precompiles/src/metagraph.rs +++ b/precompiles/src/metagraph.rs @@ -8,12 +8,13 @@ use sp_core::{ByteArray, H256}; use subtensor_runtime_common::{NetUid, Token}; use crate::PrecompileExt; +use crate::PrecompileHandleExt; pub struct MetagraphPrecompile(PhantomData); impl PrecompileExt for MetagraphPrecompile where - R: frame_system::Config + pallet_subtensor::Config, + R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, R::AccountId: From<[u8; 32]> + ByteArray, { const INDEX: u64 = 2050; @@ -22,12 +23,13 @@ where #[precompile_utils::precompile] impl MetagraphPrecompile where - R: frame_system::Config + pallet_subtensor::Config, + R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, R::AccountId: ByteArray, { #[precompile::public("getUidCount(uint16)")] #[precompile::view] - fn get_uid_count(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_uid_count(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::SubnetworkN::::get(NetUid::from( netuid, ))) @@ -35,7 +37,9 @@ where #[precompile::public("getStake(uint16,uint16)")] #[precompile::view] - fn get_stake(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_stake(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + // Keys + TotalHotkeyAlpha reads + handle.record_db_reads::(2)?; let hotkey = pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid.into(), uid) .map_err(|_| PrecompileFailure::Error { exit_status: ExitError::InvalidRange, @@ -60,7 +64,8 @@ where #[precompile::public("getConsensus(uint16,uint16)")] #[precompile::view] - fn get_consensus(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_consensus(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_consensus_for_uid( netuid.into(), uid, @@ -69,7 +74,8 @@ where #[precompile::public("getIncentive(uint16,uint16)")] #[precompile::view] - fn get_incentive(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_incentive(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_incentive_for_uid( netuid.into(), uid, @@ -78,7 +84,8 @@ where #[precompile::public("getDividends(uint16,uint16)")] #[precompile::view] - fn get_dividends(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_dividends(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_dividends_for_uid( netuid.into(), uid, @@ -87,13 +94,15 @@ where #[precompile::public("getEmission(uint16,uint16)")] #[precompile::view] - fn get_emission(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_emission(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_emission_for_uid(netuid.into(), uid).into()) } #[precompile::public("getVtrust(uint16,uint16)")] #[precompile::view] - fn get_vtrust(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_vtrust(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_validator_trust_for_uid( netuid.into(), uid, @@ -103,10 +112,11 @@ where #[precompile::public("getValidatorStatus(uint16,uint16)")] #[precompile::view] fn get_validator_status( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, uid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_validator_permit_for_uid( netuid.into(), uid, @@ -115,7 +125,12 @@ where #[precompile::public("getLastUpdate(uint16,uint16)")] #[precompile::view] - fn get_last_update(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_last_update( + handle: &mut impl PrecompileHandle, + netuid: u16, + uid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_last_update_for_uid( netuid.into(), uid, @@ -124,7 +139,8 @@ where #[precompile::public("getIsActive(uint16,uint16)")] #[precompile::view] - fn get_is_active(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_is_active(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Pallet::::get_active_for_uid( netuid.into(), uid, @@ -133,7 +149,9 @@ where #[precompile::public("getAxon(uint16,uint16)")] #[precompile::view] - fn get_axon(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_axon(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + // Keys + Axons reads + handle.record_db_reads::(2)?; let hotkey = pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid.into(), uid) .map_err(|_| PrecompileFailure::Error { exit_status: ExitError::Other("hotkey not found".into()), @@ -144,7 +162,8 @@ where #[precompile::public("getHotkey(uint16,uint16)")] #[precompile::view] - fn get_hotkey(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_hotkey(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + handle.record_db_reads::(1)?; pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid.into(), uid) .map(|acc| H256::from_slice(acc.as_slice())) .map_err(|_| PrecompileFailure::Error { @@ -154,7 +173,9 @@ where #[precompile::public("getColdkey(uint16,uint16)")] #[precompile::view] - fn get_coldkey(_: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + fn get_coldkey(handle: &mut impl PrecompileHandle, netuid: u16, uid: u16) -> EvmResult { + // Keys + Owner reads + handle.record_db_reads::(2)?; let hotkey = pallet_subtensor::Pallet::::get_hotkey_for_net_and_uid(netuid.into(), uid) .map_err(|_| PrecompileFailure::Error { exit_status: ExitError::InvalidRange, diff --git a/precompiles/src/mock.rs b/precompiles/src/mock.rs index 037e02d864..2cd917b43c 100644 --- a/precompiles/src/mock.rs +++ b/precompiles/src/mock.rs @@ -7,10 +7,10 @@ use core::{marker::PhantomData, num::NonZeroU64}; use fp_evm::{Context, PrecompileResult}; use frame_support::{ PalletId, derive_impl, parameter_types, - traits::{Everything, InherentBuilder, PrivilegeCmp}, + traits::{Everything, PrivilegeCmp}, weights::Weight, }; -use frame_system::{EnsureRoot, limits, offchain::CreateTransactionBase}; +use frame_system::{EnsureRoot, limits}; use pallet_evm::{ AddressMapping, BalanceConverter, EnsureAddressNever, EnsureAddressRoot, EvmBalance, PrecompileHandle, PrecompileSet, SubstrateBalance, @@ -22,7 +22,7 @@ use sp_runtime::{ testing::TestXt, traits::{BlakeTwo256, ConstU32, IdentityLookup}, }; -use substrate_fixed::types::U96F32; +use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AuthorshipInfo, NetUid, ProxyType, TaoBalance}; use crate::PrecompileExt; @@ -74,7 +74,6 @@ parameter_types! { pub const MaxContributors: u32 = 10; pub const SwapProtocolId: PalletId = PalletId(*b"ten/swap"); pub const SwapMaxFeeRate: u16 = 10000; - pub const SwapMaxPositions: u32 = 100; pub const SwapMinimumLiquidity: u64 = 1_000; pub const SwapMinimumReserve: NonZeroU64 = NonZeroU64::new(1_000_000).unwrap(); pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * @@ -117,6 +116,10 @@ parameter_types! { pub const InitialMaxBurn: TaoBalance = TaoBalance::new(1_000_000_000); pub const MinBurnUpperBound: TaoBalance = TaoBalance::new(1_000_000_000); pub const MaxBurnLowerBound: TaoBalance = TaoBalance::new(100_000_000); + pub const MinTempo: u16 = pallet_subtensor::MIN_TEMPO; + pub const MaxTempo: u16 = pallet_subtensor::MAX_TEMPO; + pub const MinActivityCutoffFactorMilli: u32 = pallet_subtensor::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const MaxActivityCutoffFactorMilli: u32 = pallet_subtensor::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const InitialValidatorPruneLen: u64 = 0; pub const InitialScalingLawPower: u16 = 50; pub const InitialMaxAllowedValidators: u16 = 100; @@ -154,6 +157,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = 0; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const MaxEpochsPerBlock: u8 = 32; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] @@ -174,6 +178,14 @@ impl frame_system::Config for Runtime { type MaxConsumers = ConstU32<16>; type Block = Block; type Nonce = u64; + type DispatchExtension = ( + pallet_subtensor::CheckColdkeySwap, + pallet_subtensor::CheckWeights, + pallet_subtensor::CheckRateLimits, + pallet_subtensor::CheckDelegateTake, + pallet_subtensor::CheckServingEndpoints, + pallet_subtensor::CheckEvmKeyAssociation, + ); } #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] @@ -286,7 +298,6 @@ impl pallet_subtensor_swap::Config for Runtime { type TaoReserve = pallet_subtensor::TaoBalanceReserve; type AlphaReserve = pallet_subtensor::AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; type WeightInfo = (); @@ -370,7 +381,7 @@ impl frame_system::offchain::SigningTypes for Runtime { type Signature = test_crypto::Signature; } -impl CreateTransactionBase for Runtime +impl frame_system::offchain::CreateTransactionBase for Runtime where RuntimeCall: From, { @@ -378,28 +389,12 @@ where type RuntimeCall = RuntimeCall; } -impl frame_system::offchain::CreateInherent for Runtime +impl frame_system::offchain::CreateBare for Runtime where RuntimeCall: From, { fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic { - UncheckedExtrinsic::new_inherent(call) - } -} - -impl frame_system::offchain::CreateSignedTransaction for Runtime -where - RuntimeCall: From, -{ - fn create_signed_transaction< - C: frame_system::offchain::AppCrypto, - >( - call: >::RuntimeCall, - _public: Self::Public, - _account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - Some(UncheckedExtrinsic::new_signed(call, nonce, (), ())) + UncheckedExtrinsic::new_bare(call) } } @@ -461,6 +456,10 @@ impl pallet_subtensor::Config for Runtime { type InitialMinStake = InitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = MinTempo; + type MaxTempo = MaxTempo; + type MinActivityCutoffFactorMilli = MinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = MaxActivityCutoffFactorMilli; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; @@ -492,6 +491,7 @@ impl pallet_subtensor::Config for Runtime { type AuthorshipProvider = MockAuthorshipProvider; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = MaxEpochsPerBlock; type WeightInfo = (); } @@ -622,8 +622,8 @@ pub(crate) fn selector_u32(signature: &str) -> u32 { u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) } -pub(crate) fn alpha_price_to_evm(price: U96F32) -> U256 { - let scaled_price = (price * U96F32::from_num(EVM_DECIMALS_FACTOR)).to_num::(); +pub(crate) fn alpha_price_to_evm(price: U64F64) -> U256 { + let scaled_price = (price * U64F64::from_num(EVM_DECIMALS_FACTOR)).to_num::(); ::BalanceConverter::into_evm_balance(scaled_price.into()) .expect("runtime balance conversion should work for alpha price") .into_u256() diff --git a/precompiles/src/neuron.rs b/precompiles/src/neuron.rs index 1397baf272..8856acdbbd 100644 --- a/precompiles/src/neuron.rs +++ b/precompiles/src/neuron.rs @@ -303,7 +303,7 @@ mod tests { pallet_subtensor::Pallet::::set_burn(netuid, REGISTRATION_BURN.into()); pallet_subtensor::Pallet::::set_max_allowed_uids(netuid, 4096); pallet_subtensor::Pallet::::set_weights_set_rate_limit(netuid, 0); - pallet_subtensor::Pallet::::set_tempo(netuid, TEMPO); + pallet_subtensor::Pallet::::set_tempo_unchecked(netuid, TEMPO); pallet_subtensor::Pallet::::set_commit_reveal_weights_enabled(netuid, true); pallet_subtensor::Pallet::::set_reveal_period(netuid, REVEAL_PERIOD) .expect("reveal period setup should succeed"); @@ -455,15 +455,21 @@ mod tests { &caller_account, ) .expect("weight commit should exist before reveal"); - let (_, _, first_reveal_block, _) = commits + // CR-v2 tuple layout: (hash, commit_epoch, commit_block, _unused). + let (_, commit_epoch, _, _) = commits .front() .copied() .expect("weight commit queue should contain the committed hash"); - System::set_block_number(u64::from( - u32::try_from(first_reveal_block) - .expect("first reveal block should fit in runtime block number"), - )); + // Put the subnet into the exact epoch in which the commit is revealable: + // `current_epoch == commit_epoch + reveal_period`. Pin `LastEpochBlock` and + // `PendingEpochAt` so `should_run_epoch` is false and the look-ahead does + // not advance past the reveal epoch. + let reveal_epoch = commit_epoch.saturating_add(REVEAL_PERIOD); + pallet_subtensor::SubnetEpochIndex::::insert(netuid, reveal_epoch); + let cur_block = pallet_subtensor::Pallet::::get_current_block_as_u64(); + pallet_subtensor::LastEpochBlock::::insert(netuid, cur_block); + pallet_subtensor::PendingEpochAt::::insert(netuid, 0u64); pallet_subtensor::Pallet::::set_stake_threshold(1); let rejected = execute_precompile( @@ -609,6 +615,48 @@ mod tests { }); } + #[test] + fn neuron_precompile_dispatch_runs_subtensor_dispatch_extensions() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5A34); + let (netuid, caller_account) = setup_registered_caller(caller); + let new_coldkey_hash = + ::Hashing::hash_of(&AccountId::new([0x99; 32])); + + pallet_subtensor::ColdkeySwapAnnouncements::::insert( + &caller_account, + (System::block_number(), new_coldkey_hash), + ); + + let rejected = execute_precompile( + &precompiles::>(), + addr_from_index(NeuronPrecompile::::INDEX), + caller, + encode_with_selector( + selector_u32("serveAxon(uint16,uint32,uint128,uint16,uint8,uint8,uint8,uint8)"), + ( + TEST_NETUID_U16, + SERVE_VERSION, + SERVE_IP, + SERVE_PORT, + SERVE_IP_TYPE, + SERVE_PROTOCOL, + SERVE_PLACEHOLDER1, + SERVE_PLACEHOLDER2, + ), + ), + U256::zero(), + ) + .expect("serve axon should route to neuron precompile"); + + assert!(rejected.is_err()); + assert!( + pallet_subtensor::Axons::::get(netuid, caller_account).is_none(), + "dispatch extension rejection must happen before the call writes endpoint metadata" + ); + }); + } + #[test] fn neuron_precompile_serve_axon_tls_sets_axon_info_and_certificate() { new_test_ext().execute_with(|| { diff --git a/precompiles/src/proxy.rs b/precompiles/src/proxy.rs index 3312b67194..78d59f5ce2 100644 --- a/precompiles/src/proxy.rs +++ b/precompiles/src/proxy.rs @@ -268,9 +268,10 @@ where #[precompile::public("getProxies(bytes32)")] #[precompile::view] pub fn get_proxies( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, account_id: H256, ) -> EvmResult> { + handle.record_db_reads::(1)?; let account_id = R::AccountId::from(account_id.0.into()); let proxies = pallet_proxy::pallet::Pallet::::proxies(account_id); diff --git a/precompiles/src/solidity/subnet.abi b/precompiles/src/solidity/subnet.abi index 4531f59246..60e8b49906 100644 --- a/precompiles/src/solidity/subnet.abi +++ b/precompiles/src/solidity/subnet.abi @@ -18,6 +18,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getActivityCutoffFactor", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -592,6 +611,24 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "factorMilli", + "type": "uint32" + } + ], + "name": "setActivityCutoffFactor", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, { "inputs": [ { @@ -1028,8 +1065,5 @@ "outputs": [], "stateMutability": "payable", "type": "function" - }, - { - "inputs" } ] diff --git a/precompiles/src/solidity/subnet.sol b/precompiles/src/solidity/subnet.sol index 4e78708d62..c454781cb5 100644 --- a/precompiles/src/solidity/subnet.sol +++ b/precompiles/src/solidity/subnet.sol @@ -113,6 +113,15 @@ interface ISubnet { uint16 activityCutoff ) external payable; + function getActivityCutoffFactor( + uint16 netuid + ) external view returns (uint32); + + function setActivityCutoffFactor( + uint16 netuid, + uint32 factorMilli + ) external payable; + function getNetworkRegistrationAllowed( uint16 netuid ) external view returns (bool); diff --git a/precompiles/src/staking.rs b/precompiles/src/staking.rs index 28e043f07b..554115ddf0 100644 --- a/precompiles/src/staking.rs +++ b/precompiles/src/staking.rs @@ -43,7 +43,7 @@ use pallet_evm::{ }; use pallet_subtensor_proxy as pallet_proxy; use precompile_utils::EvmResult; -use precompile_utils::prelude::{Address, RuntimeHelper, revert}; +use precompile_utils::prelude::{Address, revert}; use sp_core::{H160, H256, U256}; use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, StaticLookup, UniqueSaturatedInto}; use sp_std::vec; @@ -296,9 +296,11 @@ where #[precompile::public("getTotalColdkeyStake(bytes32)")] #[precompile::view] fn get_total_coldkey_stake( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, coldkey: H256, ) -> EvmResult { + // StakingHotkeys + per-hotkey stake reads + handle.record_db_reads::(2)?; let coldkey = R::AccountId::from(coldkey.0); let stake = pallet_subtensor::Pallet::::get_total_stake_for_coldkey(&coldkey); @@ -307,10 +309,9 @@ where #[precompile::public("getTotalHotkeyStake(bytes32)")] #[precompile::view] - fn get_total_hotkey_stake( - _handle: &mut impl PrecompileHandle, - hotkey: H256, - ) -> EvmResult { + fn get_total_hotkey_stake(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult { + // Per-subnet stake + alpha price reads + handle.record_db_reads::(2)?; let hotkey = R::AccountId::from(hotkey.0); let stake = pallet_subtensor::Pallet::::get_total_stake_for_hotkey(&hotkey); @@ -320,11 +321,13 @@ where #[precompile::public("getStake(bytes32,bytes32,uint256)")] #[precompile::view] fn get_stake( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, hotkey: H256, coldkey: H256, netuid: U256, ) -> EvmResult { + // Alpha share pool reads + handle.record_db_reads::(2)?; let hotkey = R::AccountId::from(hotkey.0); let coldkey = R::AccountId::from(coldkey.0); let netuid = try_u16_from_u256(netuid)?; @@ -340,7 +343,7 @@ where #[precompile::public("getAlphaStakedValidators(bytes32,uint256)")] #[precompile::view] fn get_alpha_staked_validators( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, hotkey: H256, netuid: U256, ) -> EvmResult> { @@ -350,6 +353,7 @@ where for (coldkey, netuid_in_alpha, _) in pallet_subtensor::Pallet::::alpha_iter_single_prefix(&hotkey) { + handle.record_db_reads::(1)?; if netuid == netuid_in_alpha { let key: [u8; 32] = coldkey.into(); coldkeys.push(key.into()); @@ -362,10 +366,11 @@ where #[precompile::public("getTotalAlphaStaked(bytes32,uint256)")] #[precompile::view] fn get_total_alpha_staked( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, hotkey: H256, netuid: U256, ) -> EvmResult { + handle.record_db_reads::(2)?; let hotkey = R::AccountId::from(hotkey.0); let netuid = try_u16_from_u256(netuid)?; let stake = @@ -376,7 +381,9 @@ where #[precompile::public("getNominatorMinRequiredStake()")] #[precompile::view] - fn get_nominator_min_required_stake(_handle: &mut impl PrecompileHandle) -> EvmResult { + fn get_nominator_min_required_stake(handle: &mut impl PrecompileHandle) -> EvmResult { + // NominatorMinRequiredStake + DefaultMinStake reads + handle.record_db_reads::(2)?; let stake = pallet_subtensor::Pallet::::get_nominator_min_required_stake(); Ok(stake.into()) @@ -467,10 +474,12 @@ where #[precompile::public("getTotalColdkeyStakeOnSubnet(bytes32,uint256)")] #[precompile::view] fn get_total_coldkey_stake_on_subnet( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, coldkey: H256, netuid: U256, ) -> EvmResult { + // StakingHotkeys + per-hotkey stake reads + handle.record_db_reads::(2)?; let coldkey = R::AccountId::from(coldkey.0); let netuid = try_u16_from_u256(netuid)?; let stake = pallet_subtensor::Pallet::::get_total_stake_for_coldkey_on_subnet( @@ -496,8 +505,8 @@ where amount_alpha: U256, ) -> EvmResult<()> { // AllowancesStorage write + RegisteredSubnetCounter read - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_write_gas_cost())?; + handle.record_db_reads::(1)?; + handle.record_db_writes::(1)?; let approver = handle.context().caller; let spender = spender_address.0; @@ -522,8 +531,7 @@ where origin_netuid: U256, ) -> EvmResult { // AllowancesStorage read + RegisteredSubnetCounter read - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + handle.record_db_reads::(2)?; let spender = spender_address.0; let netuid = try_u16_from_u256(origin_netuid)?; @@ -547,9 +555,8 @@ where } // AllowancesStorage read + write + RegisteredSubnetCounter read - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_write_gas_cost())?; + handle.record_db_reads::(2)?; + handle.record_db_writes::(1)?; let approver = handle.context().caller; let spender = spender_address.0; @@ -578,9 +585,8 @@ where } // AllowancesStorage read + write + RegisteredSubnetCounter read - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_write_gas_cost())?; + handle.record_db_reads::(2)?; + handle.record_db_writes::(1)?; let approver = handle.context().caller; let spender = spender_address.0; @@ -613,9 +619,8 @@ where } // AllowancesStorage read + write + RegisteredSubnetCounter read - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; - handle.record_cost(RuntimeHelper::::db_write_gas_cost())?; + handle.record_db_reads::(2)?; + handle.record_db_writes::(1)?; let counter = Self::current_subnet_counter(netuid); let approval_key = (spender, netuid, counter); @@ -780,9 +785,11 @@ where #[precompile::public("getTotalColdkeyStake(bytes32)")] #[precompile::view] fn get_total_coldkey_stake( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, coldkey: H256, ) -> EvmResult { + // StakingHotkeys + per-hotkey stake reads + handle.record_db_reads::(2)?; let coldkey = R::AccountId::from(coldkey.0); // get total stake of coldkey @@ -799,10 +806,9 @@ where #[precompile::public("getTotalHotkeyStake(bytes32)")] #[precompile::view] - fn get_total_hotkey_stake( - _handle: &mut impl PrecompileHandle, - hotkey: H256, - ) -> EvmResult { + fn get_total_hotkey_stake(handle: &mut impl PrecompileHandle, hotkey: H256) -> EvmResult { + // Per-subnet stake + alpha price reads + handle.record_db_reads::(2)?; let hotkey = R::AccountId::from(hotkey.0); // get total stake of hotkey @@ -820,11 +826,13 @@ where #[precompile::public("getStake(bytes32,bytes32,uint256)")] #[precompile::view] fn get_stake( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, hotkey: H256, coldkey: H256, netuid: U256, ) -> EvmResult { + // Alpha share pool reads + handle.record_db_reads::(2)?; let hotkey = R::AccountId::from(hotkey.0); let coldkey = R::AccountId::from(coldkey.0); let netuid = try_u16_from_u256(netuid)?; @@ -1070,11 +1078,6 @@ mod tests { fund_account(&source_account, COLDKEY_BALANCE); add_stake_v2(source, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); - pallet_subtensor::StakingOperationRateLimiter::::remove(( - hotkey.clone(), - source_account.clone(), - netuid, - )); ( netuid, @@ -1269,11 +1272,6 @@ mod tests { fund_account(&caller_account, COLDKEY_BALANCE); add_stake_v1(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); - pallet_subtensor::StakingOperationRateLimiter::::remove(( - hotkey.clone(), - caller_account.clone(), - netuid, - )); let precompiles = precompiles::>(); let precompile_addr = addr_from_index(StakingPrecompile::::INDEX); @@ -1309,11 +1307,6 @@ mod tests { fund_account(&caller_account, COLDKEY_BALANCE); add_stake_v2(caller, &hotkey, TEST_NETUID_U16, INITIAL_STAKE_RAO); - pallet_subtensor::StakingOperationRateLimiter::::remove(( - hotkey.clone(), - caller_account.clone(), - netuid, - )); let precompiles = precompiles::>(); let precompile_addr = addr_from_index(StakingPrecompileV2::::INDEX); @@ -1402,11 +1395,6 @@ mod tests { ), ) .execute_returns(()); - pallet_subtensor::StakingOperationRateLimiter::::remove(( - hotkey.clone(), - caller_account.clone(), - netuid, - )); let stake_before = stake_for(&hotkey, &caller_account, netuid); precompiles @@ -1458,11 +1446,6 @@ mod tests { ), ) .execute_returns(()); - pallet_subtensor::StakingOperationRateLimiter::::remove(( - hotkey.clone(), - caller_account.clone(), - netuid, - )); assert!(stake_for(&hotkey, &caller_account, netuid) > 0); precompiles @@ -1512,11 +1495,6 @@ mod tests { ), ) .execute_returns(()); - pallet_subtensor::StakingOperationRateLimiter::::remove(( - hotkey.clone(), - caller_account.clone(), - netuid, - )); assert!(stake_for(&hotkey, &caller_account, netuid) > 0); precompiles diff --git a/precompiles/src/subnet.rs b/precompiles/src/subnet.rs index b89d972eea..9992bd1cf3 100644 --- a/precompiles/src/subnet.rs +++ b/precompiles/src/subnet.rs @@ -161,9 +161,22 @@ where ) } + #[precompile::public("getNetworkRegistrationBlock(uint16)")] + #[precompile::view] + fn get_network_registration_block( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; + Ok(pallet_subtensor::NetworkRegisteredAt::::get( + NetUid::from(netuid), + )) + } + #[precompile::public("getServingRateLimit(uint16)")] #[precompile::view] - fn get_serving_rate_limit(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_serving_rate_limit(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::ServingRateLimit::::get(NetUid::from( netuid, ))) @@ -189,7 +202,8 @@ where #[precompile::public("getMinDifficulty(uint16)")] #[precompile::view] - fn get_min_difficulty(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_min_difficulty(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::MinDifficulty::::get(NetUid::from( netuid, ))) @@ -215,7 +229,8 @@ where #[precompile::public("getMaxDifficulty(uint16)")] #[precompile::view] - fn get_max_difficulty(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_max_difficulty(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::MaxDifficulty::::get(NetUid::from( netuid, ))) @@ -241,7 +256,8 @@ where #[precompile::public("getWeightsVersionKey(uint16)")] #[precompile::view] - fn get_weights_version_key(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_weights_version_key(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::WeightsVersionKey::::get(NetUid::from( netuid, ))) @@ -267,7 +283,11 @@ where #[precompile::public("getWeightsSetRateLimit(uint16)")] #[precompile::view] - fn get_weights_set_rate_limit(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_weights_set_rate_limit( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::WeightsSetRateLimit::::get( NetUid::from(netuid), )) @@ -286,7 +306,8 @@ where #[precompile::public("getAdjustmentAlpha(uint16)")] #[precompile::view] - fn get_adjustment_alpha(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_adjustment_alpha(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::AdjustmentAlpha::::get(NetUid::from( netuid, ))) @@ -320,7 +341,8 @@ where #[precompile::public("getImmunityPeriod(uint16)")] #[precompile::view] - fn get_immunity_period(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_immunity_period(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::ImmunityPeriod::::get(NetUid::from( netuid, ))) @@ -346,7 +368,8 @@ where #[precompile::public("getMinAllowedWeights(uint16)")] #[precompile::view] - fn get_min_allowed_weights(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_min_allowed_weights(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::MinAllowedWeights::::get(NetUid::from( netuid, ))) @@ -372,7 +395,8 @@ where #[precompile::public("getKappa(uint16)")] #[precompile::view] - fn get_kappa(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_kappa(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Kappa::::get(NetUid::from(netuid))) } @@ -392,13 +416,18 @@ where #[precompile::public("getRho(uint16)")] #[precompile::view] - fn get_rho(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_rho(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Rho::::get(NetUid::from(netuid))) } #[precompile::public("getAlphaSigmoidSteepness(uint16)")] #[precompile::view] - fn get_alpha_sigmoid_steepness(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_alpha_sigmoid_steepness( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::AlphaSigmoidSteepness::::get(NetUid::from(netuid)) as u16) } @@ -436,7 +465,8 @@ where #[precompile::public("getActivityCutoff(uint16)")] #[precompile::view] - fn get_activity_cutoff(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_activity_cutoff(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::ActivityCutoff::::get(NetUid::from( netuid, ))) @@ -460,12 +490,39 @@ where ) } + #[precompile::public("getActivityCutoffFactor(uint16)")] + #[precompile::view] + fn get_activity_cutoff_factor(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + Ok(pallet_subtensor::ActivityCutoffFactorMilli::::get( + NetUid::from(netuid), + )) + } + + #[precompile::public("setActivityCutoffFactor(uint16,uint32)")] + #[precompile::payable] + fn set_activity_cutoff_factor( + handle: &mut impl PrecompileHandle, + netuid: u16, + factor_milli: u32, + ) -> EvmResult<()> { + let call = pallet_subtensor::Call::::set_activity_cutoff_factor { + netuid: netuid.into(), + factor_milli, + }; + + handle.try_dispatch_runtime_call::( + call, + RawOrigin::Signed(handle.caller_account_id::()), + ) + } + #[precompile::public("getNetworkRegistrationAllowed(uint16)")] #[precompile::view] fn get_network_registration_allowed( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::NetworkRegistrationAllowed::::get( NetUid::from(netuid), )) @@ -492,9 +549,10 @@ where #[precompile::public("getNetworkPowRegistrationAllowed(uint16)")] #[precompile::view] fn get_network_pow_registration_allowed( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::NetworkPowRegistrationAllowed::::get( NetUid::from(netuid), )) @@ -520,7 +578,8 @@ where #[precompile::public("getMinBurn(uint16)")] #[precompile::view] - fn get_min_burn(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_min_burn(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::MinBurn::::get(NetUid::from(netuid)).to_u64()) } @@ -537,7 +596,8 @@ where #[precompile::public("getMaxBurn(uint16)")] #[precompile::view] - fn get_max_burn(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_max_burn(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::MaxBurn::::get(NetUid::from(netuid)).to_u64()) } @@ -554,7 +614,8 @@ where #[precompile::public("getDifficulty(uint16)")] #[precompile::view] - fn get_difficulty(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_difficulty(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Difficulty::::get(NetUid::from(netuid))) } @@ -578,7 +639,8 @@ where #[precompile::public("getBondsMovingAverage(uint16)")] #[precompile::view] - fn get_bonds_moving_average(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_bonds_moving_average(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::BondsMovingAverage::::get( NetUid::from(netuid), )) @@ -605,9 +667,10 @@ where #[precompile::public("getCommitRevealWeightsEnabled(uint16)")] #[precompile::view] fn get_commit_reveal_weights_enabled( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::CommitRevealWeightsEnabled::::get( NetUid::from(netuid), )) @@ -633,7 +696,11 @@ where #[precompile::public("getLiquidAlphaEnabled(uint16)")] #[precompile::view] - fn get_liquid_alpha_enabled(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_liquid_alpha_enabled( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::LiquidAlphaOn::::get(NetUid::from( netuid, ))) @@ -659,13 +726,15 @@ where #[precompile::public("getYuma3Enabled(uint16)")] #[precompile::view] - fn get_yuma3_enabled(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_yuma3_enabled(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::Yuma3On::::get(NetUid::from(netuid))) } #[precompile::public("getBondsResetEnabled(uint16)")] #[precompile::view] - fn get_bonds_reset_enabled(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_bonds_reset_enabled(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::BondsResetOn::::get(NetUid::from( netuid, ))) @@ -709,7 +778,8 @@ where #[precompile::public("getAlphaValues(uint16)")] #[precompile::view] - fn get_alpha_values(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<(u16, u16)> { + fn get_alpha_values(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult<(u16, u16)> { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::AlphaValues::::get(NetUid::from( netuid, ))) @@ -738,9 +808,10 @@ where #[precompile::public("getCommitRevealWeightsInterval(uint16)")] #[precompile::view] fn get_commit_reveal_weights_interval( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::RevealPeriodEpochs::::get( NetUid::from(netuid), )) @@ -1111,6 +1182,32 @@ mod tests { U256::from(activity_cutoff), ); + let factor_milli: u32 = 1_500; + precompiles + .prepare_test( + caller, + precompile_addr, + encode_with_selector( + selector_u32("setActivityCutoffFactor(uint16,uint32)"), + (TEST_NETUID_U16, factor_milli), + ), + ) + .execute_returns(()); + assert_eq!( + pallet_subtensor::ActivityCutoffFactorMilli::::get(netuid), + factor_milli + ); + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getActivityCutoffFactor(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(factor_milli), + ); + precompiles .prepare_test( caller, @@ -1225,4 +1322,28 @@ mod tests { ); }); } + + #[test] + fn subnet_precompile_gets_network_registered_block() { + new_test_ext().execute_with(|| { + let caller = addr_from_index(0x5003); + let netuid = setup_owner_subnet(caller); + let precompiles = precompiles::>(); + let precompile_addr = addr_from_index(SubnetPrecompile::::INDEX); + + let registration_block: u64 = 42; + pallet_subtensor::NetworkRegisteredAt::::insert(netuid, registration_block); + + assert_static_call( + &precompiles, + caller, + precompile_addr, + encode_with_selector( + selector_u32("getNetworkRegistrationBlock(uint16)"), + (TEST_NETUID_U16,), + ), + U256::from(registration_block), + ); + }); + } } diff --git a/precompiles/src/uid_lookup.rs b/precompiles/src/uid_lookup.rs index 5d87973368..dc65501ba1 100644 --- a/precompiles/src/uid_lookup.rs +++ b/precompiles/src/uid_lookup.rs @@ -6,7 +6,7 @@ use precompile_utils::{EvmResult, prelude::Address}; use sp_runtime::traits::{Dispatchable, StaticLookup}; use sp_std::vec::Vec; -use crate::PrecompileExt; +use crate::{PrecompileExt, PrecompileHandleExt}; pub struct UidLookupPrecompile(PhantomData); @@ -39,11 +39,12 @@ where #[precompile::public("uidLookup(uint16,address,uint16)")] #[precompile::view] fn uid_lookup( - _handle: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, evm_address: Address, limit: u16, ) -> EvmResult> { + handle.record_db_reads::(u64::from(limit))?; Ok(pallet_subtensor::Pallet::::uid_lookup( netuid.into(), evm_address.0, diff --git a/precompiles/src/voting_power.rs b/precompiles/src/voting_power.rs index af7896dac1..4cad7fcb89 100644 --- a/precompiles/src/voting_power.rs +++ b/precompiles/src/voting_power.rs @@ -6,6 +6,7 @@ use sp_core::{ByteArray, H256, U256}; use subtensor_runtime_common::NetUid; use crate::PrecompileExt; +use crate::PrecompileHandleExt; /// VotingPower precompile for smart contract access to validator voting power. /// @@ -15,7 +16,7 @@ pub struct VotingPowerPrecompile(PhantomData); impl PrecompileExt for VotingPowerPrecompile where - R: frame_system::Config + pallet_subtensor::Config, + R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, R::AccountId: From<[u8; 32]> + ByteArray, { const INDEX: u64 = 2061; @@ -24,7 +25,7 @@ where #[precompile_utils::precompile] impl VotingPowerPrecompile where - R: frame_system::Config + pallet_subtensor::Config, + R: frame_system::Config + pallet_subtensor::Config + pallet_evm::Config, R::AccountId: From<[u8; 32]>, { /// Get voting power for a hotkey on a subnet. @@ -44,10 +45,11 @@ where #[precompile::public("getVotingPower(uint16,bytes32)")] #[precompile::view] fn get_voting_power( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, hotkey: H256, ) -> EvmResult { + handle.record_db_reads::(1)?; let hotkey = R::AccountId::from(hotkey.0); let voting_power = pallet_subtensor::VotingPower::::get(NetUid::from(netuid), &hotkey); Ok(U256::from(voting_power)) @@ -63,9 +65,10 @@ where #[precompile::public("isVotingPowerTrackingEnabled(uint16)")] #[precompile::view] fn is_voting_power_tracking_enabled( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::VotingPowerTrackingEnabled::::get( NetUid::from(netuid), )) @@ -84,9 +87,10 @@ where #[precompile::public("getVotingPowerDisableAtBlock(uint16)")] #[precompile::view] fn get_voting_power_disable_at_block( - _: &mut impl PrecompileHandle, + handle: &mut impl PrecompileHandle, netuid: u16, ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::VotingPowerDisableAtBlock::::get( NetUid::from(netuid), )) @@ -104,7 +108,11 @@ where /// * `u64` - The alpha value (with 18 decimal precision) #[precompile::public("getVotingPowerEmaAlpha(uint16)")] #[precompile::view] - fn get_voting_power_ema_alpha(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + fn get_voting_power_ema_alpha( + handle: &mut impl PrecompileHandle, + netuid: u16, + ) -> EvmResult { + handle.record_db_reads::(1)?; Ok(pallet_subtensor::VotingPowerEmaAlpha::::get( NetUid::from(netuid), )) @@ -122,10 +130,14 @@ where /// * `u256` - The total voting power across all validators #[precompile::public("getTotalVotingPower(uint16)")] #[precompile::view] - fn get_total_voting_power(_: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { - let total: u64 = pallet_subtensor::VotingPower::::iter_prefix(NetUid::from(netuid)) - .map(|(_, voting_power)| voting_power) - .fold(0u64, |acc, vp| acc.saturating_add(vp)); + fn get_total_voting_power(handle: &mut impl PrecompileHandle, netuid: u16) -> EvmResult { + let mut total: u64 = 0; + for (_, voting_power) in + pallet_subtensor::VotingPower::::iter_prefix(NetUid::from(netuid)) + { + handle.record_db_reads::(1)?; + total = total.saturating_add(voting_power); + } Ok(U256::from(total)) } } diff --git a/pallets/swap-interface/Cargo.toml b/primitives/swap-interface/Cargo.toml similarity index 81% rename from pallets/swap-interface/Cargo.toml rename to primitives/swap-interface/Cargo.toml index e4392c6d67..5d4020edc2 100644 --- a/pallets/swap-interface/Cargo.toml +++ b/primitives/swap-interface/Cargo.toml @@ -16,6 +16,10 @@ workspace = true [features] default = ["std"] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "subtensor-runtime-common/runtime-benchmarks", +] std = [ "codec/std", "frame-support/std", diff --git a/primitives/swap-interface/src/lib.rs b/primitives/swap-interface/src/lib.rs new file mode 100644 index 0000000000..9980604707 --- /dev/null +++ b/primitives/swap-interface/src/lib.rs @@ -0,0 +1,234 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::too_many_arguments)] +use core::ops::Neg; + +use frame_support::pallet_prelude::*; +use substrate_fixed::types::U64F64; +use subtensor_macros::freeze_struct; +use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance, Token}; + +pub use order::*; + +mod order; + +pub trait SwapEngine: DefaultPriceLimit { + fn swap( + netuid: NetUid, + order: O, + price_limit: TaoBalance, + drop_fees: bool, + should_rollback: bool, + ) -> Result, DispatchError>; +} + +pub trait SwapHandler { + fn swap( + netuid: NetUid, + order: O, + price_limit: TaoBalance, + drop_fees: bool, + should_rollback: bool, + ) -> Result, DispatchError> + where + Self: SwapEngine; + fn sim_swap( + netuid: NetUid, + order: O, + ) -> Result, DispatchError> + where + Self: SwapEngine; + + fn approx_fee_amount(netuid: NetUid, amount: T) -> T; + fn current_alpha_price(netuid: NetUid) -> U64F64; + fn max_price() -> C; + fn min_price() -> C; + fn adjust_protocol_liquidity( + netuid: NetUid, + tao_delta: TaoBalance, + alpha_delta: AlphaBalance, + ) -> (TaoBalance, AlphaBalance); + fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; + fn init_swap(netuid: NetUid, maybe_price: Option); + fn get_alpha_amount_for_tao(netuid: NetUid, tao_amount: TaoBalance) -> AlphaBalance; +} + +/// Combined swap + balance execution interface for limit orders. +/// +/// Wraps the complete buy/sell operation: AMM state update (via `SwapHandler`), +/// pool reserve accounting, and user balance changes (TAO free balance / +/// alpha staking). Implemented by `pallet_subtensor::Pallet` using +/// `stake_into_subnet` / `unstake_from_subnet`. +pub trait OrderSwapInterface { + /// Buy alpha with TAO: debit `tao_amount` from `coldkey`'s free balance, + /// credit resulting alpha as stake at `hotkey` on `netuid`. + /// + /// When `validate` is `true` the implementation enforces subnet + /// existence, hotkey registration, minimum stake amount, sufficient + /// coldkey balance, and sets the staking rate-limit flag for `(hotkey, + /// coldkey, netuid)` after a successful stake. Pass `false` for internal + /// pallet-intermediary swaps that must bypass these user-facing guards. + /// Buy alpha with TAO: debit `tao_amount` from `coldkey`'s free balance, + /// credit resulting alpha as stake at `hotkey` on `netuid`. + /// + /// **Implementations MUST be transactional** (wrap in + /// `frame_support::storage::with_transaction` or annotate with + /// `#[frame_support::transactional]`). The implementation debits the + /// caller's balance before the pool swap; if the swap fails the debit + /// must be rolled back to leave the caller's state unchanged. + fn buy_alpha( + coldkey: &AccountId, + hotkey: &AccountId, + netuid: NetUid, + tao_amount: TaoBalance, + limit_price: TaoBalance, + validate: bool, + ) -> Result; + + /// Sell alpha for TAO: remove `alpha_amount` from `coldkey`'s stake at + /// `hotkey` on `netuid`, credit resulting TAO to `coldkey`'s free balance. + /// + /// When `validate` is `true` the implementation enforces subnet + /// existence, hotkey registration, minimum stake amount, sufficient alpha + /// balance, and checks that the staking rate-limit flag is not set for + /// `(hotkey, coldkey, netuid)` (i.e. the account did not stake this + /// block). Pass `false` for internal pallet-intermediary swaps. + /// Sell alpha for TAO: remove `alpha_amount` from `coldkey`'s stake at + /// `hotkey` on `netuid`, credit resulting TAO to `coldkey`'s free balance. + /// + /// **Implementations MUST be transactional** (wrap in + /// `frame_support::storage::with_transaction` or annotate with + /// `#[frame_support::transactional]`). The implementation decrements the + /// caller's stake before the pool swap; if the swap fails the decrement + /// must be rolled back to leave the caller's state unchanged. + fn sell_alpha( + coldkey: &AccountId, + hotkey: &AccountId, + netuid: NetUid, + alpha_amount: AlphaBalance, + limit_price: TaoBalance, + validate: bool, + ) -> Result; + + /// Current spot price: TAO per alpha, same scale as + /// `SwapHandler::current_alpha_price`. + fn current_alpha_price(netuid: NetUid) -> U64F64; + + /// Transfer `amount` TAO from `from`'s free balance to `to`'s free balance. + /// + /// Used by the batch executor to collect TAO from buy-order signers into + /// the pallet intermediary account and to distribute TAO to sell-order + /// signers after internal matching. + fn transfer_tao(from: &AccountId, to: &AccountId, amount: TaoBalance) -> DispatchResult; + + /// Move `amount` staked alpha directly between two (coldkey, hotkey) pairs + /// on `netuid` **without going through the AMM pool**. + /// + /// This is a pure stake-accounting transfer used for internal order + /// matching in `execute_batched_orders`: it lets the pallet collect alpha + /// from sell-order signers into its intermediary account, and later + /// distribute alpha to buy-order signers, all without touching the pool. + /// + /// When `validate_sender` is `true`, the sender side is validated before + /// the transfer: subnet existence, subtoken enabled, minimum stake amount, + /// and the staking rate-limit flag for `(from_hotkey, from_coldkey, + /// netuid)` is checked — the transfer is rejected if `from_coldkey` + /// already staked this block. + /// + /// When `validate_receiver` is `true`, the staking rate-limit flag for + /// `(to_hotkey, to_coldkey, netuid)` is set after the transfer, marking + /// that `to_coldkey` has received stake this block. + /// + /// The two flags are intentionally separate so that each call site can + /// opt into only the half it needs: + /// - Collecting alpha from users into the pallet intermediary: + /// `validate_sender: true, validate_receiver: false` — validates the + /// user but does not rate-limit the intermediary account. + /// - Distributing alpha from the pallet intermediary to buyers: + /// `validate_sender: false, validate_receiver: true` — skips checking + /// the intermediary (which would fail) and rate-limits the buyer. + fn transfer_staked_alpha( + from_coldkey: &AccountId, + from_hotkey: &AccountId, + to_coldkey: &AccountId, + to_hotkey: &AccountId, + netuid: NetUid, + amount: AlphaBalance, + validate_sender: bool, + validate_receiver: bool, + ) -> DispatchResult; + + /// Set up a subnet for benchmark execution. + /// + /// Called once per benchmark before any orders are built. Implementations + /// should initialise the subnet (registers it, enables the subtoken, seeds + /// pool reserves) so that price queries and swaps succeed. + /// The default is a no-op; override in runtime implementations. + #[cfg(feature = "runtime-benchmarks")] + fn set_up_netuid_for_benchmark(_netuid: NetUid) {} + + /// Register `hotkey` as owned by `coldkey`. + /// + /// Called during `on_genesis` and `on_runtime_upgrade` to claim ownership of + /// the pallet's hotkey before any external actor can register it. Safe to call + /// multiple times — is a no-op if the hotkey account already exists. + fn register_pallet_hotkey(coldkey: &AccountId, hotkey: &AccountId) -> DispatchResult; + + /// Returns `true` if `coldkey` is the registered owner of `hotkey`. + fn pallet_hotkey_registered(coldkey: &AccountId, hotkey: &AccountId) -> bool; + + /// Set up accounts for benchmark execution. + /// + /// Called once per order before the benchmarked extrinsic runs. Implementations + /// should fund `coldkey` with sufficient TAO (and alpha for sell orders) and + /// register `hotkey` on the relevant subnet so that swap operations succeed. + /// The default is a no-op; override in runtime implementations. + #[cfg(feature = "runtime-benchmarks")] + fn set_up_acc_for_benchmark(_hotkey: &AccountId, _coldkey: &AccountId) {} +} + +pub trait DefaultPriceLimit +where + PaidIn: Token, + PaidOut: Token, +{ + fn default_price_limit() -> C; +} + +/// Externally used swap result (for RPC) +#[freeze_struct("6a03533fc53ccfb8")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct SwapResult +where + PaidIn: Token, + PaidOut: Token, +{ + pub amount_paid_in: PaidIn, + pub amount_paid_out: PaidOut, + pub fee_paid: PaidIn, + pub fee_to_block_author: PaidIn, +} + +impl SwapResult +where + PaidIn: Token, + PaidOut: Token, +{ + pub fn paid_in_reserve_delta(&self) -> i128 { + self.amount_paid_in.to_u64() as i128 + } + + pub fn paid_in_reserve_delta_i64(&self) -> i64 { + self.paid_in_reserve_delta() + .clamp(i64::MIN as i128, i64::MAX as i128) as i64 + } + + pub fn paid_out_reserve_delta(&self) -> i128 { + (self.amount_paid_out.to_u64() as i128).neg() + } + + pub fn paid_out_reserve_delta_i64(&self) -> i64 { + (self.amount_paid_out.to_u64() as i128) + .neg() + .clamp(i64::MIN as i128, i64::MAX as i128) as i64 + } +} diff --git a/pallets/swap-interface/src/order.rs b/primitives/swap-interface/src/order.rs similarity index 84% rename from pallets/swap-interface/src/order.rs rename to primitives/swap-interface/src/order.rs index b4075e9781..7b9970f123 100644 --- a/pallets/swap-interface/src/order.rs +++ b/primitives/swap-interface/src/order.rs @@ -11,7 +11,7 @@ pub trait Order: Clone { fn with_amount(amount: impl Into) -> Self; fn amount(&self) -> Self::PaidIn; - fn is_beyond_price_limit(&self, alpha_sqrt_price: U64F64, limit_sqrt_price: U64F64) -> bool; + fn is_beyond_price_limit(&self, current_price: U64F64, limit_price: U64F64) -> bool; } #[derive(Clone, Default)] @@ -45,8 +45,8 @@ where self.amount } - fn is_beyond_price_limit(&self, alpha_sqrt_price: U64F64, limit_sqrt_price: U64F64) -> bool { - alpha_sqrt_price < limit_sqrt_price + fn is_beyond_price_limit(&self, current_price: U64F64, limit_price: U64F64) -> bool { + current_price < limit_price } } @@ -81,7 +81,7 @@ where self.amount } - fn is_beyond_price_limit(&self, alpha_sqrt_price: U64F64, limit_sqrt_price: U64F64) -> bool { - alpha_sqrt_price > limit_sqrt_price + fn is_beyond_price_limit(&self, current_price: U64F64, limit_price: U64F64) -> bool { + current_price > limit_price } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 48269f5eb5..946e797f21 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -61,6 +61,7 @@ sp-authority-discovery.workspace = true subtensor-runtime-common.workspace = true subtensor-precompiles.workspace = true sp-weights.workspace = true +sp-io.workspace = true # Temporary sudo pallet-sudo.workspace = true @@ -88,9 +89,6 @@ pallet-transaction-payment-rpc-runtime-api.workspace = true frame-benchmarking = { workspace = true, optional = true } frame-system-benchmarking = { workspace = true, optional = true } -# Identity registry pallet for registering project info -pallet-registry.workspace = true - # Metadata commitment pallet pallet-commitments.workspace = true @@ -151,6 +149,9 @@ ark-serialize = { workspace = true, features = ["derive"] } # Crowdloan pallet-crowdloan.workspace = true +# Limit Orders +pallet-limit-orders.workspace = true + # Mev Shield pallet-shield.workspace = true stp-shield.workspace = true @@ -159,8 +160,8 @@ ethereum.workspace = true [dev-dependencies] frame-metadata.workspace = true -sp-io.workspace = true sp-tracing.workspace = true +sp-keyring.workspace = true precompile-utils = { workspace = true, features = ["testing"] } [build-dependencies] @@ -214,12 +215,12 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", - "pallet-registry/std", "pallet-admin-utils/std", "subtensor-custom-rpc-runtime-api/std", "subtensor-transaction-fee/std", "serde_json/std", "sp-io/std", + "sp-keyring/std", "sp-tracing/std", "log/std", "safe-math/std", @@ -227,6 +228,7 @@ std = [ "sp-genesis-builder/std", "subtensor-precompiles/std", "subtensor-runtime-common/std", + "pallet-limit-orders/std", "pallet-crowdloan/std", "pallet-babe/std", "pallet-session/std", @@ -296,7 +298,6 @@ runtime-benchmarks = [ "pallet-safe-mode/runtime-benchmarks", "pallet-subtensor/runtime-benchmarks", "pallet-subtensor-proxy/runtime-benchmarks", - "pallet-registry/runtime-benchmarks", "pallet-commitments/runtime-benchmarks", "pallet-admin-utils/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", @@ -330,7 +331,9 @@ runtime-benchmarks = [ "pallet-shield/runtime-benchmarks", "subtensor-runtime-common/runtime-benchmarks", - "subtensor-chain-extensions/runtime-benchmarks" + "subtensor-chain-extensions/runtime-benchmarks", + "pallet-limit-orders/runtime-benchmarks", + "subtensor-swap-interface/runtime-benchmarks", ] try-runtime = [ "frame-try-runtime/try-runtime", @@ -354,7 +357,6 @@ try-runtime = [ "sp-runtime/try-runtime", "pallet-admin-utils/try-runtime", "pallet-commitments/try-runtime", - "pallet-registry/try-runtime", "pallet-crowdloan/try-runtime", "pallet-babe/try-runtime", "pallet-session/try-runtime", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f20e21a0bb..a39c473880 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -18,6 +18,7 @@ pub mod transaction_payment_wrapper; extern crate alloc; +use alloc::collections::BTreeMap; use codec::{Compact, Decode, Encode}; use ethereum::AuthorizationList; use frame_support::{ @@ -30,7 +31,6 @@ use frame_support::{ use frame_system::{EnsureRoot, EnsureRootWithSuccess, EnsureSigned}; use pallet_commitments::{CanCommit, OnMetadataCommitment}; use pallet_grandpa::{AuthorityId as GrandpaId, fg_primitives}; -use pallet_registry::CanRegisterIdentity; pub use pallet_shield; use pallet_subtensor::rpc_info::{ delegate_info::DelegateInfo, @@ -38,7 +38,7 @@ use pallet_subtensor::rpc_info::{ metagraph::{Metagraph, SelectiveMetagraph}, neuron_info::{NeuronInfo, NeuronInfoLite}, show_subnet::SubnetState, - stake_info::StakeInfo, + stake_info::{StakeAvailability, StakeInfo}, subnet_info::{ SubnetHyperparams, SubnetHyperparamsV2, SubnetHyperparamsV3, SubnetInfo, SubnetInfov2, }, @@ -57,13 +57,11 @@ use sp_core::{ H160, H256, OpaqueMetadata, U256, crypto::{ByteArray, KeyTypeId}, }; -use sp_runtime::Cow; -use sp_runtime::generic::Era; use sp_runtime::{ - AccountId32, ApplyExtrinsicResult, ConsensusEngineId, Percent, generic, impl_opaque_keys, + AccountId32, ApplyExtrinsicResult, ConsensusEngineId, Cow, Percent, generic, impl_opaque_keys, traits::{ - AccountIdLookup, BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, One, - PostDispatchInfoOf, UniqueSaturatedInto, Verify, + AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, DispatchInfoOf, + Dispatchable, One, PostDispatchInfoOf, UniqueSaturatedInto, Verify, }, transaction_validity::{ TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, @@ -75,7 +73,7 @@ use sp_std::prelude::*; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use stp_shield::ShieldedTransaction; -use substrate_fixed::types::{U64F64, U96F32}; +use substrate_fixed::types::U64F64; use subtensor_precompiles::Precompiles; use subtensor_runtime_common::{AlphaBalance, AuthorshipInfo, TaoBalance, time::*, *}; use subtensor_swap_interface::{Order, SwapHandler}; @@ -185,47 +183,6 @@ impl frame_system::offchain::CreateBare> for Runtime } } -impl frame_system::offchain::CreateSignedTransaction> for Runtime { - fn create_signed_transaction< - S: frame_system::offchain::AppCrypto, - >( - call: RuntimeCall, - public: Self::Public, - account: Self::AccountId, - nonce: Self::Nonce, - ) -> Option { - use sp_runtime::traits::StaticLookup; - - let address = ::Lookup::unlookup(account.clone()); - let extra: TxExtension = ( - ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - check_mortality::CheckMortality::::from(Era::Immortal), - check_nonce::CheckNonce::::from(nonce).into(), - frame_system::CheckWeight::::new(), - ), - ( - ChargeTransactionPaymentWrapper::new(TaoBalance::new(0)), - SudoTransactionExtension::::new(), - pallet_shield::CheckShieldedTxValidity::::new(), - pallet_subtensor::SubtensorTransactionExtension::::new(), - pallet_drand::drand_priority::DrandPriority::::new(), - ), - frame_metadata_hash_extension::CheckMetadataHash::::new(true), - ); - - let raw_payload = SignedPayload::new(call.clone(), extra.clone()).ok()?; - let signature = raw_payload.using_encoded(|payload| S::sign(payload, public))?; - - Some(UncheckedExtrinsic::new_signed( - call, address, signature, extra, - )) - } -} - // Subtensor module pub use pallet_scheduler; pub use pallet_subtensor; @@ -277,7 +234,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: 422, + spec_version: 423, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -390,7 +347,14 @@ impl frame_system::Config for Runtime { type PostInherents = (); type PostTransactions = (); type ExtensionsWeightInfo = frame_system::SubstrateExtensionsWeight; - type DispatchExtension = pallet_subtensor::CheckColdkeySwap; + type DispatchExtension = ( + pallet_subtensor::CheckColdkeySwap, + pallet_subtensor::CheckWeights, + pallet_subtensor::CheckRateLimits, + pallet_subtensor::CheckDelegateTake, + pallet_subtensor::CheckServingEndpoints, + pallet_subtensor::CheckEvmKeyAssociation, + ); } impl pallet_insecure_randomness_collective_flip::Config for Runtime {} @@ -915,43 +879,6 @@ impl pallet_preimage::Config for Runtime { >; } -pub struct AllowIdentityReg; - -impl CanRegisterIdentity for AllowIdentityReg { - #[cfg(not(feature = "runtime-benchmarks"))] - fn can_register(address: &AccountId, identified: &AccountId) -> bool { - if address != identified { - SubtensorModule::coldkey_owns_hotkey(address, identified) - && SubtensorModule::is_hotkey_registered_on_network(NetUid::ROOT, identified) - } else { - SubtensorModule::is_subnet_owner(address) - } - } - - #[cfg(feature = "runtime-benchmarks")] - fn can_register(_: &AccountId, _: &AccountId) -> bool { - true - } -} - -// Configure registry pallet. -parameter_types! { - pub const MaxAdditionalFields: u32 = 1; - pub const InitialDeposit: Balance = TaoBalance::new(100_000_000); // 0.1 TAO - pub const FieldDeposit: Balance = TaoBalance::new(100_000_000); // 0.1 TAO -} - -impl pallet_registry::Config for Runtime { - type RuntimeHoldReason = RuntimeHoldReason; - type Currency = Balances; - type CanRegister = AllowIdentityReg; - type WeightInfo = pallet_registry::weights::SubstrateWeight; - - type MaxAdditionalFields = MaxAdditionalFields; - type InitialDeposit = InitialDeposit; - type FieldDeposit = FieldDeposit; -} - parameter_types! { pub const MaxCommitFieldsInner: u32 = 3; pub const CommitmentInitialDeposit: Balance = TaoBalance::ZERO; // Free @@ -1076,6 +1003,12 @@ parameter_types! { pub const SubtensorInitialMaxBurn: TaoBalance = TaoBalance::new(100_000_000_000); // 100 tao 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 SubtensorMinTempo: u16 = pallet_subtensor::MIN_TEMPO; + pub const SubtensorMaxTempo: u16 = pallet_subtensor::MAX_TEMPO; + pub const SubtensorMinActivityCutoffFactorMilli: u32 = + pallet_subtensor::MIN_ACTIVITY_CUTOFF_FACTOR_MILLI; + pub const SubtensorMaxActivityCutoffFactorMilli: u32 = + pallet_subtensor::MAX_ACTIVITY_CUTOFF_FACTOR_MILLI; pub const SubtensorInitialTxRateLimit: u64 = 1000; pub const SubtensorInitialTxDelegateTakeRateLimit: u64 = 216000; // 30 days at 12 seconds per block pub const SubtensorInitialTxChildKeyTakeRateLimit: u64 = INITIAL_CHILDKEY_TAKE_RATELIMIT; @@ -1106,6 +1039,7 @@ parameter_types! { pub const EvmKeyAssociateRateLimit: u64 = EVM_KEY_ASSOCIATE_RATELIMIT; pub const SubtensorPalletId: PalletId = PalletId(*b"subtensr"); pub const BurnAccountId: PalletId = PalletId(*b"burntnsr"); + pub const SubtensorMaxEpochsPerBlock: u8 = prod_or_fast!(2, 32); } impl pallet_subtensor::Config for Runtime { @@ -1150,6 +1084,10 @@ impl pallet_subtensor::Config for Runtime { type InitialMinStake = SubtensorInitialMinStake; type MinBurnUpperBound = MinBurnUpperBound; type MaxBurnLowerBound = MaxBurnLowerBound; + type MinTempo = SubtensorMinTempo; + type MaxTempo = SubtensorMaxTempo; + type MinActivityCutoffFactorMilli = SubtensorMinActivityCutoffFactorMilli; + type MaxActivityCutoffFactorMilli = SubtensorMaxActivityCutoffFactorMilli; type InitialTxRateLimit = SubtensorInitialTxRateLimit; type InitialTxDelegateTakeRateLimit = SubtensorInitialTxDelegateTakeRateLimit; type InitialTxChildKeyTakeRateLimit = SubtensorInitialTxChildKeyTakeRateLimit; @@ -1185,13 +1123,13 @@ impl pallet_subtensor::Config for Runtime { type AuthorshipProvider = BlockAuthorFromAura; type SubtensorPalletId = SubtensorPalletId; type BurnAccountId = BurnAccountId; + type InitialMaxEpochsPerBlock = SubtensorMaxEpochsPerBlock; type WeightInfo = pallet_subtensor::weights::SubstrateWeight; } 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 = unsafe { NonZeroU64::new_unchecked(1_000_000) }; } @@ -1203,10 +1141,8 @@ impl pallet_subtensor_swap::Config for Runtime { type TaoReserve = pallet_subtensor::TaoBalanceReserve; type AlphaReserve = pallet_subtensor::AlphaBalanceReserve; type MaxFeeRate = SwapMaxFeeRate; - type MaxPositions = SwapMaxPositions; type MinimumLiquidity = SwapMinimumLiquidity; type MinimumReserve = SwapMinimumReserve; - // TODO: set measured weights when the pallet been benchmarked and the type is generated type WeightInfo = pallet_subtensor_swap::weights::SubstrateWeight; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = SwapBenchmarkHelper; @@ -1542,6 +1478,42 @@ impl pallet_crowdloan::Config for Runtime { type MaxContributors = MaxContributors; } +// Limit Orders +parameter_types! { + pub const LimitOrdersPalletId: PalletId = PalletId(*b"bt/limit"); + pub const LimitOrdersMaxOrdersPerBatch: u32 = 100; +} + +pub struct LimitOrdersPalletHotkey; +impl Get for LimitOrdersPalletHotkey { + fn get() -> AccountId { + PalletId(*b"bt/lmhky").into_account_truncating() + } +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct LimitOrdersUnixTime; + +#[cfg(feature = "runtime-benchmarks")] +impl frame_support::traits::UnixTime for LimitOrdersUnixTime { + fn now() -> core::time::Duration { + core::time::Duration::from_millis(pallet_timestamp::Pallet::::get()) + } +} + +impl pallet_limit_orders::Config for Runtime { + type SwapInterface = SubtensorModule; + #[cfg(feature = "runtime-benchmarks")] + type TimeProvider = LimitOrdersUnixTime; + #[cfg(not(feature = "runtime-benchmarks"))] + type TimeProvider = Timestamp; + type MaxOrdersPerBatch = LimitOrdersMaxOrdersPerBatch; + type PalletId = LimitOrdersPalletId; + type PalletHotkey = LimitOrdersPalletHotkey; + type WeightInfo = pallet_limit_orders::weights::SubstrateWeight; + type ChainId = ConfigurableChainId; +} + fn contracts_schedule() -> pallet_contracts::Schedule { pallet_contracts::Schedule { limits: pallet_contracts::Limits { @@ -1646,7 +1618,7 @@ construct_runtime!( Preimage: pallet_preimage = 14, Scheduler: pallet_scheduler = 15, Proxy: pallet_proxy = 16, - Registry: pallet_registry = 17, + // pallet_registry was 17 Commitments: pallet_commitments = 18, AdminUtils: pallet_admin_utils = 19, SafeMode: pallet_safe_mode = 20, @@ -1664,6 +1636,7 @@ construct_runtime!( Contracts: pallet_contracts = 29, MevShield: pallet_shield = 30, AlphaAssets: pallet_alpha_assets = 31, + LimitOrders: pallet_limit_orders = 32, } ); @@ -1702,6 +1675,7 @@ type Migrations = ( pallet_subtensor::migrations::migrate_init_total_issuance::initialise_total_issuance::Migration< Runtime, >, + migrations::PalletRegistryCleanupMigration, ); // Unchecked extrinsic type as expected by this runtime. @@ -1739,7 +1713,6 @@ mod benches { [pallet_balances, Balances] [pallet_timestamp, Timestamp] [pallet_sudo, Sudo] - [pallet_registry, Registry] [pallet_commitments, Commitments] [pallet_admin_utils, AdminUtils] [pallet_subtensor, SubtensorModule] @@ -1749,6 +1722,7 @@ mod benches { [pallet_shield, MevShield] [pallet_subtensor_proxy, Proxy] [pallet_subtensor_utility, Utility] + [pallet_limit_orders, LimitOrders] ); } @@ -2559,6 +2533,10 @@ impl_runtime_apis! { fn get_subnet_account_id(netuid: NetUid) -> Option { SubtensorModule::get_subnet_account_id(netuid) } + + fn get_next_epoch_start_block(netuid: NetUid) -> Option { + SubtensorModule::get_next_epoch_start_block(netuid) + } } impl subtensor_custom_rpc_runtime_api::StakeInfoRuntimeApi for Runtime { @@ -2574,6 +2552,10 @@ impl_runtime_apis! { SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account, coldkey_account, netuid ) } + fn get_stake_availability_for_coldkeys( coldkey_accounts: Vec, netuids: Option> ) -> BTreeMap> { + SubtensorModule::get_stake_availability_for_coldkeys( coldkey_accounts, netuids ) + } + fn get_stake_fee( origin: Option<(AccountId32, NetUid)>, origin_coldkey_account: AccountId32, destination: Option<(AccountId32, NetUid)>, destination_coldkey_account: AccountId32, amount: u64 ) -> u64 { SubtensorModule::get_stake_fee( origin, origin_coldkey_account, destination, destination_coldkey_account, amount ) } @@ -2669,7 +2651,7 @@ impl_runtime_apis! { impl pallet_subtensor_swap_runtime_api::SwapRuntimeApi for Runtime { fn current_alpha_price(netuid: NetUid) -> u64 { pallet_subtensor_swap::Pallet::::current_price(netuid.into()) - .saturating_mul(U96F32::from_num(1_000_000_000)) + .saturating_mul(U64F64::from_num(1_000_000_000)) .saturating_to_num() } @@ -2688,7 +2670,7 @@ impl_runtime_apis! { fn sim_swap_tao_for_alpha(netuid: NetUid, tao: TaoBalance) -> SimSwapResult { let price = pallet_subtensor_swap::Pallet::::current_price(netuid.into()); let tao_u64: u64 = tao.into(); - let no_slippage_alpha = U96F32::saturating_from_num(tao_u64).safe_div(price).saturating_to_num::(); + let no_slippage_alpha = U64F64::saturating_from_num(tao_u64).safe_div(price).saturating_to_num::(); let order = pallet_subtensor::GetAlphaForTao::::with_amount(tao); // fee_to_block_author is included in sr.fee_paid, so it is absent in this calculation pallet_subtensor_swap::Pallet::::sim_swap( @@ -2718,7 +2700,7 @@ impl_runtime_apis! { fn sim_swap_alpha_for_tao(netuid: NetUid, alpha: AlphaBalance) -> SimSwapResult { let price = pallet_subtensor_swap::Pallet::::current_price(netuid.into()); let alpha_u64: u64 = alpha.into(); - let no_slippage_tao = U96F32::saturating_from_num(alpha_u64).saturating_mul(price).saturating_to_num::(); + let no_slippage_tao = U64F64::saturating_from_num(alpha_u64).saturating_mul(price).saturating_to_num::(); let order = pallet_subtensor::GetTaoForAlpha::::with_amount(alpha); // fee_to_block_author is included in sr.fee_paid, so it is absent in this calculation pallet_subtensor_swap::Pallet::::sim_swap( diff --git a/runtime/src/migrations/mod.rs b/runtime/src/migrations/mod.rs index ecc48efcdb..d0fdf7f3da 100644 --- a/runtime/src/migrations/mod.rs +++ b/runtime/src/migrations/mod.rs @@ -1 +1,3 @@ -//! Export migrations from here. +mod pallet_registry_cleanup_migration; + +pub use pallet_registry_cleanup_migration::*; diff --git a/runtime/src/migrations/pallet_registry_cleanup_migration.rs b/runtime/src/migrations/pallet_registry_cleanup_migration.rs new file mode 100644 index 0000000000..f12cdbdc36 --- /dev/null +++ b/runtime/src/migrations/pallet_registry_cleanup_migration.rs @@ -0,0 +1,543 @@ +use crate::{Runtime, RuntimeHoldReason}; +use alloc::string::String; +#[cfg(feature = "try-runtime")] +use alloc::vec::Vec; +#[cfg(feature = "try-runtime")] +use codec::{Decode, Encode}; +use deprecated::RegistryHoldReason as OldRegistryHoldReason; +use deprecated::RuntimeHoldReason as OldRuntimeHoldReason; +use frame_support::{ + BoundedVec, + pallet_prelude::Zero, + storage::unhashed, + traits::{OnRuntimeUpgrade, StoredMap, tokens::IdAmount}, + weights::Weight, +}; +use sp_io::hashing::twox_128; +use sp_runtime::Saturating; +#[cfg(feature = "try-runtime")] +use subtensor_macros::freeze_struct; + +type DbWeightOf = ::DbWeight; +#[cfg(feature = "try-runtime")] +type AccountIdOf = ::AccountId; +type BalanceOf = ::Balance; +type AccountStoreOf = ::AccountStore; + +const MIGRATION_NAME: &[u8] = b"pallet_registry_cleanup_migration"; +const REGISTRY_PALLET_NAME: &[u8] = b"Registry"; +#[cfg(test)] +const REGISTRY_IDENTITY_OF_STORAGE_NAME: &[u8] = b"IdentityOf"; + +mod deprecated { + use super::BalanceOf; + use crate::Runtime; + use codec::Decode; + use frame_support::{ + BoundedVec, + traits::{ConstU32, tokens::IdAmount}, + }; + + #[cfg_attr(test, derive(codec::Encode))] + #[derive(Decode, Copy, Clone, Eq, PartialEq, Debug)] + pub(super) enum RegistryHoldReason { + #[codec(index = 0)] + RegistryIdentity, + } + + #[cfg_attr(test, derive(codec::Encode))] + #[derive(Decode, Copy, Clone, Eq, PartialEq, Debug)] + pub(super) enum RuntimeHoldReason { + #[codec(index = 14)] + Preimage(pallet_preimage::HoldReason), + #[codec(index = 17)] + Registry(RegistryHoldReason), + #[codec(index = 20)] + SafeMode(pallet_safe_mode::HoldReason), + #[codec(index = 29)] + Contracts(pallet_contracts::HoldReason), + } + + // Aggregated variant count across all pallets defining a + // composite HoldReason when the pallet was removed. + pub(super) const VARIANT_COUNT: u32 = 5; + + pub(super) type Holds = + BoundedVec>, ConstU32>; +} + +pub struct PalletRegistryCleanupMigration; + +impl OnRuntimeUpgrade for PalletRegistryCleanupMigration { + fn on_runtime_upgrade() -> Weight { + let migration_name = MIGRATION_NAME.to_vec(); + let mut weight = Weight::zero(); + + if pallet_subtensor::HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + pallet_balances::Holds::::translate::( + |account_id, old_holds| { + weight.saturating_accrue(DbWeightOf::::get().reads_writes(1, 1)); + let mut current_holds = BoundedVec::new(); + let mut unlocked_amount = BalanceOf::::zero(); + + // Translate old holds to new holds and keep track of cleaned up amount. + for hold in old_holds { + match map_reason(hold.id) { + Some(id) => { + if current_holds + .try_push(IdAmount { + id, + amount: hold.amount, + }) + .is_err() + { + log::error!( + "too many balance holds after migration for account {:?}", + account_id + ); + } + } + None => { + unlocked_amount = unlocked_amount.saturating_add(hold.amount); + } + } + } + + // Unlock the balance if there is any. + if !unlocked_amount.is_zero() { + weight.saturating_accrue(DbWeightOf::::get().reads_writes(1, 1)); + if let Err(error) = AccountStoreOf::::mutate(&account_id, |account| { + account.reserved = account.reserved.saturating_sub(unlocked_amount); + account.free = account.free.saturating_add(unlocked_amount); + }) { + log::error!( + "failed to unlock balance during holds migration: {:?}", + error + ); + } + } + + (!current_holds.is_empty()).then_some(current_holds) + }, + ); + + let registry_prefix = twox_128(REGISTRY_PALLET_NAME); + let result = unhashed::clear_prefix(®istry_prefix, Some(u32::MAX), None); + weight.saturating_accrue( + DbWeightOf::::get().reads_writes(result.loops as u64, result.unique as u64), + ); + log::info!( + "Removed {} entries from Registry pallet storage.", + result.unique + ); + + pallet_subtensor::HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(DbWeightOf::::get().writes(1)); + + log::info!( + "Migration '{}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let mut affected_accounts = Vec::new(); + + for account_id in pallet_balances::Holds::::iter_keys() { + let old_holds = decode_deprecated_holds(&account_id)?; + let mut unlocked_amount = BalanceOf::::zero(); + + for hold in old_holds { + if matches!(hold.id, OldRuntimeHoldReason::Registry(_)) { + unlocked_amount = unlocked_amount.saturating_add(hold.amount); + } + } + + if !unlocked_amount.is_zero() { + let account = AccountStoreOf::::get(&account_id); + affected_accounts.push(AffectedAccount { + account_id, + free: account.free, + reserved: account.reserved, + unlocked: unlocked_amount, + }); + } + } + + let state = PreUpgradeState { + total_issuance: pallet_balances::TotalIssuance::::get(), + affected_accounts, + }; + + Ok(state.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let state = PreUpgradeState::decode(&mut state.as_slice()) + .map_err(|_| "failed to decode registry cleanup pre-upgrade state")?; + + if !pallet_subtensor::HasMigrationRun::::get(MIGRATION_NAME.to_vec()) { + return Err("registry cleanup migration marker was not set".into()); + } + + if pallet_balances::TotalIssuance::::get() != state.total_issuance { + return Err("registry cleanup migration changed total issuance".into()); + } + + for affected_account in state.affected_accounts { + let account = AccountStoreOf::::get(&affected_account.account_id); + let expected_free = affected_account + .free + .saturating_add(affected_account.unlocked); + let expected_reserved = affected_account + .reserved + .saturating_sub(affected_account.unlocked); + + if account.free != expected_free { + return Err("registry cleanup migration did not unlock free balance".into()); + } + + if account.reserved != expected_reserved { + return Err("registry cleanup migration did not reduce reserved balance".into()); + } + } + + for account_id in pallet_balances::Holds::::iter_keys() { + pallet_balances::Holds::::try_get(&account_id) + .map_err(|_| "failed to decode migrated balances holds")?; + } + + let registry_prefix = twox_128(REGISTRY_PALLET_NAME); + if unhashed::contains_prefixed_key(®istry_prefix) { + return Err("registry pallet storage was not cleared".into()); + } + + Ok(()) + } +} + +#[cfg(test)] +fn registry_storage_prefix(storage_name: &[u8]) -> Vec { + let mut prefix = twox_128(REGISTRY_PALLET_NAME).to_vec(); + prefix.extend_from_slice(&twox_128(storage_name)); + prefix +} + +fn map_reason(reason: OldRuntimeHoldReason) -> Option { + match reason { + OldRuntimeHoldReason::Preimage(reason) => Some(RuntimeHoldReason::Preimage(reason)), + OldRuntimeHoldReason::SafeMode(reason) => Some(RuntimeHoldReason::SafeMode(reason)), + OldRuntimeHoldReason::Contracts(reason) => Some(RuntimeHoldReason::Contracts(reason)), + OldRuntimeHoldReason::Registry(OldRegistryHoldReason::RegistryIdentity) => None, + } +} + +#[cfg(feature = "try-runtime")] +#[derive(Encode, Decode)] +#[freeze_struct("d1c269899b95593c")] +struct PreUpgradeState { + total_issuance: BalanceOf, + affected_accounts: Vec, +} + +#[cfg(feature = "try-runtime")] +#[derive(Encode, Decode)] +#[freeze_struct("dd446b32ea403051")] +struct AffectedAccount { + account_id: AccountIdOf, + free: BalanceOf, + reserved: BalanceOf, + unlocked: BalanceOf, +} + +#[cfg(feature = "try-runtime")] +fn decode_deprecated_holds( + account_id: &AccountIdOf, +) -> Result { + let key = pallet_balances::Holds::::hashed_key_for(account_id); + unhashed::get::(&key) + .ok_or("failed to decode deprecated balances holds".into()) +} + +#[cfg(test)] +#[allow(clippy::expect_used)] +mod tests { + use super::*; + use alloc::vec; + use codec::Encode; + use frame_support::{ + assert_ok, + storage::unhashed, + traits::{Currency, ReservableCurrency}, + }; + use sp_runtime::{AccountId32, BuildStorage}; + + fn new_test_ext() -> sp_io::TestExternalities { + let mut ext: sp_io::TestExternalities = crate::RuntimeGenesisConfig::default() + .build_storage() + .expect("runtime genesis storage should build") + .into(); + ext.execute_with(|| crate::System::set_block_number(1)); + ext + } + + fn account(seed: u8) -> AccountId32 { + AccountId32::new([seed; 32]) + } + + fn balance(amount: u64) -> BalanceOf { + amount.into() + } + + fn old_hold( + id: OldRuntimeHoldReason, + amount: u64, + ) -> IdAmount> { + IdAmount { + id, + amount: balance(amount), + } + } + + fn old_holds( + holds: alloc::vec::Vec>>, + ) -> deprecated::Holds { + holds + .try_into() + .expect("test old holds should fit the deprecated bound") + } + + fn holds_key(account_id: &AccountId32) -> alloc::vec::Vec { + pallet_balances::Holds::::hashed_key_for(account_id) + } + + fn insert_old_holds(account_id: &AccountId32, holds: deprecated::Holds) { + unhashed::put_raw(&holds_key(account_id), &holds.encode()); + } + + fn registry_identity_prefix() -> alloc::vec::Vec { + registry_storage_prefix(REGISTRY_IDENTITY_OF_STORAGE_NAME) + } + + fn insert_old_registry_identity_storage(suffix: &[u8]) -> alloc::vec::Vec { + let mut key = registry_identity_prefix(); + key.extend_from_slice(suffix); + unhashed::put_raw(&key, &[1]); + key + } + + fn insert_old_registry_storage_version() -> alloc::vec::Vec { + let key = registry_storage_prefix(b":__STORAGE_VERSION__:"); + unhashed::put_raw(&key, &[1]); + key + } + + #[test] + fn drops_registry_holds_and_unlocks_their_balance() { + new_test_ext().execute_with(|| { + let account_id = account(1); + + assert!(!pallet_subtensor::HasMigrationRun::::get( + MIGRATION_NAME.to_vec() + )); + + let _ = crate::Balances::make_free_balance_be(&account_id, balance(10_000)); + assert_ok!(crate::Balances::reserve(&account_id, balance(225))); + + insert_old_holds( + &account_id, + old_holds(vec![ + old_hold( + OldRuntimeHoldReason::Registry(OldRegistryHoldReason::RegistryIdentity), + 125, + ), + old_hold( + OldRuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage), + 75, + ), + old_hold( + OldRuntimeHoldReason::SafeMode(pallet_safe_mode::HoldReason::EnterOrExtend), + 25, + ), + ]), + ); + + let registry_identity_key = insert_old_registry_identity_storage(b"account-1"); + let registry_storage_version_key = insert_old_registry_storage_version(); + assert!(unhashed::contains_prefixed_key(&twox_128( + REGISTRY_PALLET_NAME + ))); + + let issuance_before = crate::Balances::total_issuance(); + + let weight = PalletRegistryCleanupMigration::on_runtime_upgrade(); + + let account = crate::System::account(&account_id).data; + assert!(!weight.is_zero()); + assert_eq!(account.free, balance(9_900)); + assert_eq!(account.reserved, balance(100)); + assert_eq!(crate::Balances::total_issuance(), issuance_before); + + let current_holds = pallet_balances::Holds::::get(&account_id); + assert_eq!(current_holds.len(), 2); + assert!(current_holds.contains(&IdAmount { + id: RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage), + amount: balance(75), + })); + assert!(current_holds.contains(&IdAmount { + id: RuntimeHoldReason::SafeMode(pallet_safe_mode::HoldReason::EnterOrExtend), + amount: balance(25), + })); + + assert!(pallet_subtensor::HasMigrationRun::::get( + MIGRATION_NAME.to_vec() + )); + assert!(unhashed::get_raw(®istry_identity_key).is_none()); + assert!(unhashed::get_raw(®istry_storage_version_key).is_none()); + assert!(!unhashed::contains_prefixed_key(&twox_128( + REGISTRY_PALLET_NAME + ))); + + let second_weight = PalletRegistryCleanupMigration::on_runtime_upgrade(); + let account_after_second = crate::System::account(&account_id).data; + + assert!(second_weight.is_zero()); + assert_eq!(account_after_second.free, account.free); + assert_eq!(account_after_second.reserved, account.reserved); + assert_eq!(account_after_second.frozen, account.frozen); + assert_eq!( + pallet_balances::Holds::::get(&account_id), + current_holds + ); + }); + } + + #[test] + fn removes_holds_storage_when_only_registry_holds_remain() { + new_test_ext().execute_with(|| { + let account_id = account(2); + + let _ = crate::Balances::make_free_balance_be(&account_id, balance(10_000)); + assert_ok!(crate::Balances::reserve(&account_id, balance(125))); + + insert_old_holds( + &account_id, + old_holds(vec![old_hold( + OldRuntimeHoldReason::Registry(OldRegistryHoldReason::RegistryIdentity), + 125, + )]), + ); + + let storage_key = holds_key(&account_id); + let issuance_before = crate::Balances::total_issuance(); + + PalletRegistryCleanupMigration::on_runtime_upgrade(); + + let account = crate::System::account(&account_id).data; + assert_eq!(account.free, balance(10_000)); + assert_eq!(account.reserved, balance(0)); + assert_eq!(crate::Balances::total_issuance(), issuance_before); + assert!(pallet_balances::Holds::::get(&account_id).is_empty()); + assert!(unhashed::get_raw(&storage_key).is_none()); + }); + } + + #[test] + fn preserves_non_registry_holds_without_changing_balances() { + new_test_ext().execute_with(|| { + let account_id = account(3); + + let _ = crate::Balances::make_free_balance_be(&account_id, balance(10_000)); + assert_ok!(crate::Balances::reserve(&account_id, balance(100))); + + insert_old_holds( + &account_id, + old_holds(vec![ + old_hold( + OldRuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage), + 70, + ), + old_hold( + OldRuntimeHoldReason::Contracts( + pallet_contracts::HoldReason::StorageDepositReserve, + ), + 30, + ), + ]), + ); + + let issuance_before = crate::Balances::total_issuance(); + + PalletRegistryCleanupMigration::on_runtime_upgrade(); + + let account = crate::System::account(&account_id).data; + assert_eq!(account.free, balance(9_900)); + assert_eq!(account.reserved, balance(100)); + assert_eq!(crate::Balances::total_issuance(), issuance_before); + + let current_holds = pallet_balances::Holds::::get(&account_id); + assert_eq!(current_holds.len(), 2); + assert!(current_holds.contains(&IdAmount { + id: RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage), + amount: balance(70), + })); + assert!(current_holds.contains(&IdAmount { + id: RuntimeHoldReason::Contracts( + pallet_contracts::HoldReason::StorageDepositReserve, + ), + amount: balance(30), + })); + }); + } + + #[cfg(feature = "try-runtime")] + #[test] + fn try_runtime_checks_validate_cleanup() { + new_test_ext().execute_with(|| { + let account_id = account(4); + + let _ = crate::Balances::make_free_balance_be(&account_id, balance(10_000)); + assert_ok!(crate::Balances::reserve(&account_id, balance(150))); + + insert_old_holds( + &account_id, + old_holds(vec![ + old_hold( + OldRuntimeHoldReason::Registry(OldRegistryHoldReason::RegistryIdentity), + 100, + ), + old_hold( + OldRuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage), + 50, + ), + ]), + ); + + insert_old_registry_identity_storage(b"account-4"); + + let state = PalletRegistryCleanupMigration::pre_upgrade() + .expect("pre-upgrade check should decode old holds"); + + PalletRegistryCleanupMigration::on_runtime_upgrade(); + + PalletRegistryCleanupMigration::post_upgrade(state) + .expect("post-upgrade check should validate migrated holds"); + }); + } +} diff --git a/runtime/tests/limit_orders.rs b/runtime/tests/limit_orders.rs new file mode 100644 index 0000000000..f68191fa29 --- /dev/null +++ b/runtime/tests/limit_orders.rs @@ -0,0 +1,2667 @@ +#![allow( + clippy::unwrap_used, + clippy::arithmetic_side_effects, + clippy::too_many_arguments +)] + +use codec::Encode; +use frame_support::{ + BoundedVec, PalletId, assert_noop, assert_ok, + traits::{ConstU32, Hooks}, +}; +use node_subtensor_runtime::{ + BuildStorage, LimitOrders, Runtime, RuntimeEvent, RuntimeGenesisConfig, RuntimeOrigin, + SubtensorModule, System, pallet_subtensor, +}; +use pallet_limit_orders::{ + HasMigrationRun, LimitOrdersEnabled, Order, OrderStatus, OrderType, Orders, SignedOrder, + VersionedOrder, +}; +use pallet_subtensor::{SubnetAlphaIn, SubnetMechanism, SubnetTAO}; +use sp_core::{Get, H256, Pair}; +use sp_keyring::Sr25519Keyring; +use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{MultiSignature, Perbill}; +use subtensor_runtime_common::{AccountId, AlphaBalance, NetUid, TaoBalance, Token}; + +fn new_test_ext() -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig::default() + .build_storage() + .unwrap() + .into(); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +/// Initialise a subnet so that limit-order execution has a pool to interact with. +/// +/// We use the stable mechanism (mechanism_id = 0, the default), which swaps at a +/// fixed 1 TAO : 1 alpha rate without requiring pre-seeded AMM liquidity. +fn setup_subnet(netuid: NetUid) { + SubtensorModule::init_new_network(netuid, 0); + // Genesis forces netuid 1 to dynamic (mechanism_id = 1); override to stable + // (mechanism_id = 0) so that swaps are 1:1 with no AMM fees, matching the + // intent of every test that calls this helper. + pallet_subtensor::SubnetMechanism::::insert(netuid, 0u16); + pallet_subtensor::SubtokenEnabled::::insert(netuid, true); +} + +fn min_default_stake() -> TaoBalance { + pallet_subtensor::DefaultMinStake::::get() +} + +fn add_balance_to_coldkey_account(coldkey: &AccountId, tao: TaoBalance) { + let credit = SubtensorModule::mint_tao(tao); + let _ = SubtensorModule::spend_tao(coldkey, credit, tao); +} + +fn seed_subnet_tao(netuid: NetUid, amount: TaoBalance) { + let subnet_account = SubtensorModule::get_subnet_account_id(netuid).unwrap(); + add_balance_to_coldkey_account(&subnet_account, amount); +} + +fn fund_account(id: &AccountId) { + add_balance_to_coldkey_account(id, min_default_stake() * 10u64.into()); +} + +fn order_id(order: &VersionedOrder) -> H256 { + H256(sp_io::hashing::blake2_256(&order.encode())) +} + +fn make_order_batch( + orders: Vec>, +) -> BoundedVec, ::MaxOrdersPerBatch> +{ + orders.try_into().unwrap() +} + +fn setup_buyer_seller( + netuid: NetUid, + alice_id: &AccountId, + charlie_id: &AccountId, + bob_id: &AccountId, + dave_id: &AccountId, +) { + fund_account(alice_id); + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 10u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + dave_id, + bob_id, + netuid, + initial_alpha, + ); + seed_subnet_tao(netuid, TaoBalance::from(initial_alpha.to_u64())); + let _ = SubtensorModule::create_account_if_non_existent(alice_id, charlie_id); + let _ = SubtensorModule::create_account_if_non_existent(bob_id, dave_id); +} + +struct OrderParams { + order_type: OrderType, + amount: u64, + limit_price: u64, + expiry: u64, + fee_rate: Perbill, + fee_recipient: AccountId, + relayer: Option>>, + max_slippage: Option, + partial_fills_enabled: bool, +} + +/// Shared implementation: constructs and signs a `VersionedOrder::V1` from an +/// `OrderParams` and returns a `SignedOrder` with `partial_fill = None`. +/// All three public factory functions delegate here so that adding a new field +/// to `Order` requires updating only this function. +fn make_signed_order_inner( + keyring: Sr25519Keyring, + hotkey: AccountId, + netuid: NetUid, + params: OrderParams, +) -> SignedOrder { + let order = VersionedOrder::V1(Order { + signer: keyring.to_account_id(), + hotkey, + netuid, + order_type: params.order_type, + amount: params.amount, + limit_price: params.limit_price, + expiry: params.expiry, + fee_rate: params.fee_rate, + fee_recipient: params.fee_recipient, + relayer: params.relayer, + max_slippage: params.max_slippage, + partial_fills_enabled: params.partial_fills_enabled, + // chain_id 0 matches the default pallet_evm_chain_id genesis value in tests + chain_id: 0, + }); + let sig = keyring.pair().sign(&order.encode()); + SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + } +} + +fn make_signed_order( + keyring: Sr25519Keyring, + hotkey: AccountId, + netuid: NetUid, + order_type: OrderType, + amount: u64, + limit_price: u64, + expiry: u64, + fee_rate: Perbill, + fee_recipient: AccountId, +) -> SignedOrder { + make_signed_order_inner( + keyring, + hotkey, + netuid, + OrderParams { + order_type, + amount, + limit_price, + expiry, + fee_rate, + fee_recipient, + relayer: None, + max_slippage: None, + partial_fills_enabled: false, + }, + ) +} + +/// Set up a dynamic-mechanism (Uniswap v3-style) subnet with equal TAO and +/// alpha reserves, giving an initial pool price of exactly 1.0 TAO/alpha. +/// +/// The stable mechanism (mechanism_id = 0) ignores the `price_limit` parameter +/// entirely and always executes at 1:1, so slippage enforcement can only be +/// tested against a dynamic subnet. +fn setup_dynamic_subnet(netuid: NetUid) { + SubtensorModule::init_new_network(netuid, 0); + // Override the mechanism to 1 (dynamic / Uniswap v3). + SubnetMechanism::::insert(netuid, 1u16); + pallet_subtensor::SubtokenEnabled::::insert(netuid, true); + // Equal reserves → price = tao_reserve / alpha_reserve = 1.0 + SubnetTAO::::insert(netuid, TaoBalance::from(1_000_000_000_000_u64)); + SubnetAlphaIn::::insert(netuid, AlphaBalance::from(1_000_000_000_000_u64)); + seed_subnet_tao(netuid, TaoBalance::from(1_000_000_000_000_u64)); +} + +/// Build a signed order with an explicit `max_slippage` value. +fn make_signed_order_with_slippage_rt( + keyring: Sr25519Keyring, + hotkey: AccountId, + netuid: NetUid, + order_type: OrderType, + amount: u64, + limit_price: u64, + expiry: u64, + fee_rate: Perbill, + fee_recipient: AccountId, + max_slippage: Option, +) -> SignedOrder { + make_signed_order_inner( + keyring, + hotkey, + netuid, + OrderParams { + order_type, + amount, + limit_price, + expiry, + fee_rate, + fee_recipient, + relayer: None, + max_slippage, + partial_fills_enabled: false, + }, + ) +} + +/// Build a `SignedOrder` with `partial_fills_enabled = true` and the relayer set +/// to `relayer`. The `partial_fill` field on the envelope is supplied separately +/// by each test so that the *same* `VersionedOrder` payload (and therefore the +/// same order-id) can be re-used across multiple submissions. +fn make_partial_fill_order( + keyring: Sr25519Keyring, + hotkey: AccountId, + netuid: NetUid, + order_type: OrderType, + amount: u64, + limit_price: u64, + expiry: u64, + fee_recipient: AccountId, + relayer: AccountId, + partial_fill: Option, +) -> SignedOrder { + let mut signed = make_signed_order_inner( + keyring, + hotkey, + netuid, + OrderParams { + order_type, + amount, + limit_price, + expiry, + fee_rate: Perbill::zero(), + fee_recipient, + relayer: Some(BoundedVec::try_from(vec![relayer]).unwrap()), + max_slippage: None, + partial_fills_enabled: true, + }, + ); + signed.partial_fill = partial_fill; + signed +} + +// ───────────────────────────────────────────────────────────────────────────── + +/// Signing and cancelling an order writes the order id to storage as Cancelled +/// and emits OrderCancelled. No subnet or balance setup required. +#[test] +fn cancel_order_works() { + new_test_ext().execute_with(|| { + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let fee_recipient = Sr25519Keyring::Charlie.to_account_id(); + + let order = VersionedOrder::V1(Order { + signer: alice_id.clone(), + hotkey: bob_id, + netuid: NetUid::from(1u16), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: u64::MAX, + fee_rate: Perbill::zero(), + fee_recipient, + relayer: None, + max_slippage: None, + partial_fills_enabled: false, + // chain_id 0 matches the default pallet_evm_chain_id genesis value in tests + chain_id: 0, + }); + let id = order_id(&order); + + assert_ok!(LimitOrders::cancel_order( + RuntimeOrigin::signed(alice_id), + order, + )); + + assert_eq!(Orders::::get(id), Some(OrderStatus::Cancelled)); + }); +} + +/// An order signed with an Ed25519 key is rejected at validation time even +/// though the signature itself is cryptographically valid. The order must not +/// appear in the Orders storage map after the batch runs. +#[test] +fn execute_orders_ed25519_signature_rejected() { + new_test_ext().execute_with(|| { + let alice_id = Sr25519Keyring::Alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let fee_recipient = Sr25519Keyring::Charlie.to_account_id(); + + let order = VersionedOrder::V1(Order { + signer: alice_id.clone(), + hotkey: bob_id, + netuid: NetUid::from(1u16), + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: u64::MAX, + fee_rate: Perbill::zero(), + fee_recipient, + relayer: None, + max_slippage: None, + partial_fills_enabled: false, + // chain_id 0 matches the default pallet_evm_chain_id genesis value in tests + chain_id: 0, + }); + let id = order_id(&order); + + // Sign with ed25519 — valid signature, wrong scheme. + let ed_pair = sp_core::ed25519::Pair::from_legacy_string("//Alice", None); + let ed_sig = ed_pair.sign(&order.encode()); + let signed = SignedOrder { + order, + signature: MultiSignature::Ed25519(ed_sig), + partial_fill: None, + }; + + let orders = make_order_batch(vec![signed]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(alice_id), + orders, + false, + )); + + // Order was silently skipped — nothing written to storage. + assert!(Orders::::get(id).is_none()); + }); +} + +/// An order carrying a wrong chain_id is silently skipped by `execute_orders` +/// (the per-order error path) and must not appear in the Orders storage map. +#[test] +fn execute_orders_chain_id_mismatch_rejected() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + setup_subnet(netuid); + + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let fee_recipient = Sr25519Keyring::Charlie.to_account_id(); + fund_account(&alice_id); + + // Build an order with a chain_id that doesn't match the runtime (0). + let order = VersionedOrder::V1(Order { + signer: alice_id.clone(), + hotkey: bob_id, + netuid, + order_type: OrderType::LimitBuy, + amount: 1_000, + limit_price: u64::MAX, + expiry: u64::MAX, + fee_rate: Perbill::zero(), + fee_recipient, + relayer: None, + max_slippage: None, + partial_fills_enabled: false, + chain_id: 9999, // wrong chain — should be rejected + }); + let id = order_id(&order); + let sig = alice.pair().sign(&order.encode()); + let signed = SignedOrder { + order, + signature: MultiSignature::Sr25519(sig), + partial_fill: None, + }; + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(alice_id), + make_order_batch(vec![signed]), + false, + )); + + // Order was silently skipped — nothing written to storage. + assert!(Orders::::get(id).is_none()); + }); +} + +/// A LimitBuy order whose price condition is satisfied executes against the pool, +/// marks the order as Fulfilled, and credits staked alpha to the buyer. +#[test] +fn limit_buy_order_executes_and_stakes_alpha() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Fund Alice so buy_alpha can debit her balance. + fund_account(&alice_id); + + // Create the hot-key association. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // limit_price = u64::MAX → current_price (1.0) ≤ MAX → condition always met. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), // default min stake units of TAO to spend + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order must be marked as executed. + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + + // Alice must now have staked alpha delegated through Bob on this subnet. + // AMM pool output has slight slippage even with the stable mechanism; check within 1%. + let staked = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + let expected_alpha = min_default_stake().to_u64(); + assert!( + staked >= AlphaBalance::from(expected_alpha * 99 / 100) + && staked <= AlphaBalance::from(expected_alpha), + "alice should hold approximately min_default_stake alpha after a LimitBuy order executes (got {staked:?})" + ); + }); +} + +/// A TakeProfit order whose price condition is satisfied executes against the pool, +/// marks the order as Fulfilled, and burns the seller's staked alpha position. +#[test] +fn take_profit_order_executes_and_unstakes_alpha() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Create the hot-key association. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Seed Alice with staked alpha through Bob so she has something to sell. + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 10u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &bob_id, + &alice_id, + netuid, + initial_alpha, + ); + seed_subnet_tao(netuid, TaoBalance::from(initial_alpha.to_u64())); + + // limit_price = 0 → current_price (1.0) ≥ 0 → condition always met. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().into(), // sell min default alpha units + 0, // price floor — always satisfied + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order must be marked as executed. + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + + // Alice's staked alpha must have decreased by exactly min_default_stake after the sell. + // Stable mechanism 1:1, zero fee: initial_alpha = min_default_stake * 10, + // sold min_default_stake alpha, so remaining = min_default_stake * 9. + let remaining = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + assert_eq!( + remaining, + AlphaBalance::from(min_default_stake().to_u64() * 9u64), + "alice's staked alpha should be min_default_stake*9 after a TakeProfit order executes" + ); + }); +} + +/// A StopLoss order whose price condition is satisfied (price ≤ limit_price) executes +/// against the pool, marks the order as Fulfilled, decreases the seller's staked alpha, +/// and credits free TAO to the seller. +#[test] +fn stop_loss_order_executes_and_unstakes_alpha() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Create the hot-key association. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Seed Alice with staked alpha through Bob so she has something to sell. + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 10u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &bob_id, + &alice_id, + netuid, + initial_alpha, + ); + seed_subnet_tao(netuid, TaoBalance::from(initial_alpha.to_u64())); + + // limit_price = 1_000_000_000 (1.0 × 10⁹) → scaled_price (1_000_000_000) ≤ 1_000_000_000 + // → StopLoss condition always met. Stable mechanism ignores the AMM floor, so any + // value ≥ 1_000_000_000 works here. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::StopLoss, + min_default_stake().into(), // sell min_default_stake alpha units + 1_000_000_000, // price ceiling in ×10⁹ scale (1.0) — always met + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order must be marked as executed. + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + + // Alice's staked alpha must have decreased by exactly min_default_stake. + // Stable mechanism 1:1, zero fee: initial_alpha = min_default_stake * 10, + // sold min_default_stake alpha, so remaining = min_default_stake * 9. + let remaining = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + assert_eq!( + remaining, + AlphaBalance::from(min_default_stake().to_u64() * 9u64), + "alice's staked alpha should be min_default_stake*9 after a StopLoss order executes" + ); + + // Alice must have received TAO from the sale. Pool output has slight slippage; check within 1%. + let alice_tao = SubtensorModule::get_coldkey_balance(&alice_id); + let expected_tao = min_default_stake().to_u64(); + assert!( + alice_tao >= TaoBalance::from(expected_tao * 99 / 100) + && alice_tao <= TaoBalance::from(expected_tao), + "alice should receive approximately min_default_stake TAO after a StopLoss order executes (got {alice_tao:?})" + ); + }); +} + +// ── Batched execution ───────────────────────────────────────────────────────── + +/// Buy side (5 000 TAO) exceeds sell side (2 000 alpha ≈ 2 000 TAO at 1:1). +/// +/// Residual 3 000 TAO goes to the pool; buyers receive pool alpha + seller passthrough +/// alpha. Sellers receive the passthrough TAO that corresponds to their alpha. +/// +/// With the stable mechanism (1 TAO = 1 alpha): +/// • Alice (buyer 5 000 TAO) → 5 000 alpha staked to Dave +/// • Bob (seller 2 000 α) → 2 000 free TAO +#[test] +fn batched_buy_dominant_executes_correctly() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + let dave_id = Sr25519Keyring::Dave.to_account_id(); + + setup_subnet(netuid); + + setup_buyer_seller(netuid, &alice_id, &charlie_id, &bob_id, &dave_id); + + let buy = make_signed_order( + alice, + charlie_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().to_u64() * 2u64, + u64::MAX, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let sell = make_signed_order( + bob, + dave_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().into(), + 0, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![buy, sell]); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + )); + + // Alice spent TAO and must hold the resulting staked alpha. + // Buy-dominant: Alice buys min_default_stake*2 TAO, Bob sells min_default_stake alpha. + // total_sell_tao_equiv = min_default_stake (at 1:1). residual_buy = min_default_stake. + // pool returns min_default_stake alpha; plus Bob's passthrough = min_default_stake. + // Alice receives Bob's passthrough alpha + pool alpha for the residual TAO. + // Pool output has slight slippage; check within 1% of expected min_default_stake*2. + let alice_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &charlie_id, + &alice_id, + netuid, + ); + let expected_alice_alpha = min_default_stake().to_u64() * 2u64; + assert!( + alice_alpha >= AlphaBalance::from(expected_alice_alpha * 99 / 100) + && alice_alpha <= AlphaBalance::from(expected_alice_alpha), + "alice should hold approximately min_default_stake*2 alpha after buy-dominant batch (got {alice_alpha:?})" + ); + + // Bob sold alpha and must hold the resulting free TAO. + // In buy-dominant, total_tao = total_sell_tao_equiv = min_default_stake. + // Bob's gross_share = (min_default_stake * min_default_stake) / min_default_stake + // = min_default_stake (exact). Zero fee => net_share = min_default_stake. + let bob_tao = SubtensorModule::get_coldkey_balance(&bob_id); + assert_eq!( + bob_tao, + TaoBalance::from(min_default_stake().to_u64()), + "bob should hold exactly min_default_stake TAO after buy-dominant batch" + ); + }); +} + +/// Regression (real-storage rollback): the same fully-signed `LimitBuy` order +/// appearing twice in one batch must hard-fail with `DuplicateOrderInBatch` and +/// leave the signer's balances completely untouched. +/// +/// Balances are real substrate storage, so the +/// all-or-nothing rollback is faithfully observable: free TAO and staked alpha +/// must match their pre-call values exactly, and the order must never be +/// recorded in `Orders`. +#[test] +fn batched_full_fill_duplicate_rejected_and_rolled_back() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + let dave_id = Sr25519Keyring::Dave.to_account_id(); + + setup_subnet(netuid); + setup_buyer_seller(netuid, &alice_id, &charlie_id, &bob_id, &dave_id); + + // Open-relay (relayer: None) fully-signed LimitBuy from Alice, staking + // to her hotkey (charlie). + let order = make_signed_order( + alice, + charlie_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&order.order); + + // Snapshot the signer's real balances before the call. + let alice_tao_before = SubtensorModule::get_coldkey_balance(&alice_id); + let alice_alpha_before = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &charlie_id, + &alice_id, + netuid, + ); + + // The same order twice in one batch — must hard-fail the whole batch. + let orders = make_order_batch(vec![order.clone(), order]); + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + ), + pallet_limit_orders::Error::::DuplicateOrderInBatch + ); + + // Full rollback: balances unchanged and no order status recorded. + assert_eq!( + SubtensorModule::get_coldkey_balance(&alice_id), + alice_tao_before, + "signer's free TAO must be unchanged after a duplicate-order batch rollback" + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &charlie_id, + &alice_id, + netuid, + ), + alice_alpha_before, + "signer's staked alpha must be unchanged after a duplicate-order batch rollback" + ); + assert!( + Orders::::get(id).is_none(), + "no order status must be recorded when the batch is rolled back" + ); + }); +} + +/// Sell side (min_default_stake()*2 alpha ≈ min_default_stake()*2 TAO at 1:1) exceeds buy side (min_default_stake() TAO). +/// +/// Residual min_default_stake() alpha goes to the pool; sellers receive pool TAO + buyer +/// passthrough TAO. Buyers receive the passthrough alpha corresponding to their TAO. +/// +/// With the stable mechanism (1 TAO = 1 alpha): +/// • Alice (buyer min_default_stake() TAO) → alpha staked to Dave +/// • Bob (seller min_default_stake()*2 α) → min_default_stake()*2 free TAO +#[test] +fn batched_sell_dominant_executes_correctly() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + let dave_id = Sr25519Keyring::Dave.to_account_id(); + + setup_subnet(netuid); + + setup_buyer_seller(netuid, &alice_id, &charlie_id, &bob_id, &dave_id); + + let buy = make_signed_order( + alice, + charlie_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let sell = make_signed_order( + bob, + dave_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().to_u64() * 2u64, + 0, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![buy, sell]); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + )); + + // Alice spent TAO and must hold the resulting staked alpha. + // Sell-dominant: Alice buys min_default_stake TAO, Bob sells min_default_stake*2 alpha. + // total_buy_alpha_equiv = tao_to_alpha(min_default_stake, 1.0) = min_default_stake (exact). + // Alice's pro-rata share = (min_default_stake * min_default_stake) / min_default_stake + // = min_default_stake (exact, no floor rounding). + let alice_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &charlie_id, + &alice_id, + netuid, + ); + assert_eq!( + alice_alpha, + AlphaBalance::from(min_default_stake().to_u64()), + "alice should hold exactly min_default_stake alpha after sell-dominant batch" + ); + + // Bob receives Alice's passthrough TAO + pool TAO for the residual alpha. + // Pool output has slight slippage; check within 1% of expected min_default_stake*2. + let bob_tao = SubtensorModule::get_coldkey_balance(&bob_id); + let expected_bob_tao = min_default_stake().to_u64() * 2u64; + assert!( + bob_tao >= TaoBalance::from(expected_bob_tao * 99 / 100) + && bob_tao <= TaoBalance::from(expected_bob_tao), + "bob should hold approximately min_default_stake*2 TAO after sell-dominant batch (got {bob_tao:?})" + ); + }); +} + +#[test] +fn batched_fails_if_executing_below_minimum_on_sell() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + let dave_id = Sr25519Keyring::Dave.to_account_id(); + + setup_subnet(netuid); + + setup_buyer_seller(netuid, &alice_id, &charlie_id, &bob_id, &dave_id); + + let buy = make_signed_order( + alice, + charlie_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let sell = make_signed_order( + bob, + dave_id.clone(), + netuid, + OrderType::TakeProfit, + 1u64, + 0, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![buy, sell]); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + ), + pallet_subtensor::Error::::AmountTooLow + ); + }); +} + +#[test] +fn batched_fails_if_executing_without_hot_key_association() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + let dave_id = Sr25519Keyring::Dave.to_account_id(); + + setup_subnet(netuid); + + // Create the hot-key association. Alice is not associating to charlie + + // Alice has free TAO to spend on a buy order. + fund_account(&alice_id); + + // Seed Bob with staked alph so he has something to sell. + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 10u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &dave_id, + &bob_id, + netuid, + initial_alpha, + ); + seed_subnet_tao(netuid, TaoBalance::from(initial_alpha.to_u64())); + + let buy = make_signed_order( + alice, + charlie_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let sell = make_signed_order( + bob, + dave_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().to_u64() * 2u64, + 0, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![buy, sell]); + + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + ), + pallet_subtensor::Error::::HotKeyAccountNotExists + ); + }); +} + +/// `execute_batched_orders` fails when the target subnet does not exist. +/// The subnet is never initialised (no `setup_subnet`), so `buy_alpha` +/// returns `SubnetNotExists` during the pool-swap step. +#[test] +fn batched_fails_for_nonexistent_subnet() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(2u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + // Fund Alice so that `transfer_tao` succeeds; the subnet check happens + // later inside `buy_alpha`. + fund_account(&alice_id); + + let buy = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![buy]); + + assert_noop!( + LimitOrders::execute_batched_orders(RuntimeOrigin::signed(charlie_id), netuid, orders,), + pallet_subtensor::Error::::SubnetNotExists + ); + }); +} + +/// `execute_batched_orders` fails when the subnet exists but its subtoken is +/// not enabled. The order passes validation (price condition is met) and the +/// TAO transfer succeeds, but `buy_alpha` then returns `SubtokenDisabled`. +#[test] +fn batched_fails_if_subtoken_not_enabled() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + // Initialise the network but deliberately skip setting SubtokenEnabled. + SubtensorModule::init_new_network(netuid, 0); + + // Fund Alice so that the TAO transfer in `collect_assets` succeeds. + fund_account(&alice_id); + + let buy = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![buy]); + + assert_noop!( + LimitOrders::execute_batched_orders(RuntimeOrigin::signed(charlie_id), netuid, orders,), + pallet_subtensor::Error::::SubtokenDisabled + ); + }); +} + +/// An order whose `expiry` is in the past causes `execute_batched_orders` to +/// fail with `OrderExpired`. +#[test] +fn batched_fails_for_expired_order() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Advance the runtime timestamp so that `now_ms` exceeds the order's expiry. + // `pallet_timestamp::Now` stores milliseconds; set it to 100_000 ms. + pallet_timestamp::Now::::put(100_000u64); + + // Build an order that expired at 50_000 ms — already in the past. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + 50_000, // expiry in ms — before current timestamp of 100_000 + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![signed]); + + assert_noop!( + LimitOrders::execute_batched_orders(RuntimeOrigin::signed(charlie_id), netuid, orders,), + pallet_limit_orders::Error::::OrderExpired + ); + }); +} + +/// An order whose price condition is not met causes `execute_batched_orders` to +/// fail with `PriceConditionNotMet`. A `LimitBuy` with `limit_price = 0` +/// requires `current_price <= 0`; since the stable mechanism prices alpha at +/// 1.0 TAO the condition is never met. +#[test] +fn batched_fails_if_price_condition_not_met() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // limit_price = 0 requires current_price <= 0, but current_price ~= 1.0 → fails. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + 0, // price ceiling of 0 — never satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![signed]); + + assert_noop!( + LimitOrders::execute_batched_orders(RuntimeOrigin::signed(charlie_id), netuid, orders,), + pallet_limit_orders::Error::::PriceConditionNotMet + ); + }); +} + +/// `execute_batched_orders` fails immediately with `RootNetUidNotAllowed` when +/// called with `netuid = 0` (the root network). +#[test] +fn batched_fails_for_root_netuid() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(0u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + // Fund Alice so the call gets past any balance checks before hitting the root guard. + fund_account(&alice_id); + + let buy = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + + let orders = make_order_batch(vec![buy]); + + assert_noop!( + LimitOrders::execute_batched_orders(RuntimeOrigin::signed(charlie_id), netuid, orders,), + pallet_limit_orders::Error::::RootNetUidNotAllowed + ); + }); +} + +// ── execute_orders — silent-skip behaviour ──────────────────────────────────── + +/// `execute_orders` silently skips an expired order: the call returns `Ok` +/// and the order is NOT written to the `Orders` storage map. +#[test] +fn execute_orders_skips_expired_order() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Advance the runtime timestamp so that `now_ms` exceeds the order's expiry. + pallet_timestamp::Now::::put(100_000u64); + + // Build an order that expired at 50_000 ms — already in the past. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + 50_000, // expiry in ms — before current timestamp of 100_000 + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // The call must succeed even though the order is expired. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Expired order silently skipped — nothing written to storage. + assert!(Orders::::get(id).is_none()); + }); +} + +/// `execute_orders` processes a mixed batch: the valid order executes and is +/// stored as `Fulfilled`; the expired order is silently skipped and is NOT +/// written to storage. The call always returns `Ok`. +#[test] +fn execute_orders_valid_and_invalid_mixed() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Fund Alice so that her LimitBuy order can execute. + fund_account(&alice_id); + + // Create the hotkey association for Alice so buy_alpha succeeds. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Timestamp at 100_000 ms — Bob's order (expiry 50_000) will be expired. + pallet_timestamp::Now::::put(100_000u64); + + // Valid order: LimitBuy with price ceiling always satisfied and no expiry. + let valid = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + // Invalid order: already expired. + let expired = make_signed_order( + bob, + alice_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + 50_000, // expiry in ms — before current timestamp of 100_000 + Perbill::zero(), + charlie_id.clone(), + ); + let valid_id = order_id(&valid.order); + let expired_id = order_id(&expired.order); + + let orders = make_order_batch(vec![valid, expired]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Valid order executed — stored as Fulfilled. + assert_eq!( + Orders::::get(valid_id), + Some(OrderStatus::Fulfilled) + ); + // Expired order silently skipped — not written to storage. + assert!(Orders::::get(expired_id).is_none()); + }); +} + +// ── execute_orders — all-or-nothing (should_fail = true) ────────────────────── + +/// `execute_orders` with `should_fail = true` aborts the whole call as soon as +/// it hits a failing order. A single expired order makes the extrinsic return +/// `OrderExpired`, and nothing is written to the `Orders` storage map. +#[test] +fn execute_orders_should_fail_aborts_on_expired_order() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Advance the runtime timestamp so that `now_ms` exceeds the order's expiry. + pallet_timestamp::Now::::put(100_000u64); + + // Build an order that expired at 50_000 ms — already in the past. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + 50_000, // expiry in ms — before current timestamp of 100_000 + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // should_fail = true → the expired order surfaces its error to the caller + // and the whole call reverts (nothing written to storage). + assert_noop!( + LimitOrders::execute_orders(RuntimeOrigin::signed(charlie_id), orders, true), + pallet_limit_orders::Error::::OrderExpired + ); + + // Order was never stored — the call aborted. + assert!(Orders::::get(id).is_none()); + }); +} + +/// Contrast with `execute_orders_valid_and_invalid_mixed`: the SAME mixed batch +/// (a valid LimitBuy followed by an expired LimitBuy) submitted with +/// `should_fail = true` reverts the WHOLE batch. The valid order's stake and +/// balance effects are NOT applied — dispatchables are transactional. +#[test] +fn execute_orders_should_fail_reverts_valid_order_in_mixed_batch() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Fund Alice so that her LimitBuy order would execute (absent the abort). + fund_account(&alice_id); + + // Create the hotkey association for Alice so buy_alpha would succeed. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Snapshot Alice's balance and stake before submitting the batch. + let alice_balance_before = SubtensorModule::get_coldkey_balance(&alice_id); + let alice_stake_before = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + + // Timestamp at 100_000 ms — Bob's order (expiry 50_000) will be expired. + pallet_timestamp::Now::::put(100_000u64); + + // Valid order: LimitBuy with price ceiling always satisfied and no expiry. + let valid = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + // Invalid order: already expired. It follows the valid order in the batch, + // so the valid order is executed first and must be rolled back on abort. + let expired = make_signed_order( + bob, + alice_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, + 50_000, // expiry in ms — before current timestamp of 100_000 + Perbill::zero(), + charlie_id.clone(), + ); + let valid_id = order_id(&valid.order); + let expired_id = order_id(&expired.order); + + let orders = make_order_batch(vec![valid, expired]); + + // should_fail = true → the expired order aborts the whole call and reverts + // the already-executed valid order. + assert_noop!( + LimitOrders::execute_orders(RuntimeOrigin::signed(charlie_id), orders, true), + pallet_limit_orders::Error::::OrderExpired + ); + + // Neither order is stored — the entire batch was rolled back. + assert!( + Orders::::get(valid_id).is_none(), + "valid order must be rolled back, not stored, when should_fail aborts" + ); + assert!(Orders::::get(expired_id).is_none()); + + // The valid order's effects must NOT have been applied: Alice's TAO balance + // and her staked alpha are exactly what they were before the call. + assert_eq!( + SubtensorModule::get_coldkey_balance(&alice_id), + alice_balance_before, + "alice's TAO must be unchanged after an aborted all-or-nothing batch" + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid), + alice_stake_before, + "alice's staked alpha must be unchanged after an aborted all-or-nothing batch" + ); + }); +} + +/// `execute_orders` silently skips an order whose signer has no hotkey +/// association: the call returns `Ok` and the order is NOT written to the +/// `Orders` storage map. +#[test] +fn execute_orders_skips_order_with_unassociated_hotkey() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Fund Alice so that any balance check is not the reason for skipping. + fund_account(&alice_id); + + // Deliberately do NOT call create_account_if_non_existent — Alice has no + // hotkey association, so the order should be silently skipped. + + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // The call must succeed even though the hotkey association is missing. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order was silently skipped — nothing written to storage. + assert!(Orders::::get(id).is_none()); + }); +} + +/// `execute_orders` silently skips an order whose amount is below the minimum +/// stake threshold: the call returns `Ok` and the order is NOT written to the +/// `Orders` storage map. +#[test] +fn execute_orders_skips_order_below_minimum_stake() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Fund Alice so that any balance check is not the reason for skipping. + fund_account(&alice_id); + + // Create the hotkey association so that is not the reason for skipping. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // amount = 1 is well below min_default_stake(), triggering AmountTooLow. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + 1u64, + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // The call must succeed even though the amount is below the minimum. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order was silently skipped — nothing written to storage. + assert!(Orders::::get(id).is_none()); + }); +} + +/// `execute_orders` silently skips an order targeting a subnet that does not +/// exist: the call returns `Ok` and the order is NOT written to the `Orders` +/// storage map. +#[test] +fn execute_orders_skips_order_for_nonexistent_subnet() { + new_test_ext().execute_with(|| { + // netuid 2 is not initialised — no setup_subnet call. + let netuid = NetUid::from(2u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + // Fund Alice so that any balance check is not the reason for skipping. + fund_account(&alice_id); + + // Create the hotkey association so that is not the reason for skipping. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // The call must succeed even though the subnet does not exist. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order was silently skipped — nothing written to storage. + assert!(Orders::::get(id).is_none()); + }); +} + +// ── Fee-correctness tests ───────────────────────────────────────────────────── + +/// `execute_orders` (non-batched) correctly forwards the buy-order fee to the +/// designated fee recipient and charges Alice exactly `amount` TAO in total. +/// +/// Fee mechanics for a non-batched LimitBuy: +/// fee_tao = fee_rate * tao_in (computed from input BEFORE swap, exact integer arithmetic) +/// tao_after_fee = tao_in - fee_tao (goes to the pool) +/// fee transferred directly from signer to fee_recipient via transfer_tao +/// +/// We use amount = min_default_stake() * 2 so that tao_after_fee = 90% * 2 * min_default_stake() +/// = 1.8 * min_default_stake() > min_default_stake(), satisfying the minimum-stake validation +/// inside buy_alpha. With fee_rate = 10%: +/// fee_tao = 10% * (min_default_stake() * 2) = min_default_stake() / 5 (exact integer result) +/// Alice pays min_default_stake()*2 total and has min_default_stake()*8 remaining. +/// Charlie (fee recipient) receives exactly fee_tao. +#[test] +fn execute_orders_fee_forwarded_to_recipient() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Fund Alice with 10× min_default_stake so she can cover the order amount and a margin. + fund_account(&alice_id); + + // Create the hotkey association Alice → Bob. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Charlie starts with zero balance — verify before submitting. + assert_eq!( + SubtensorModule::get_coldkey_balance(&charlie_id), + TaoBalance::from(0u64), + "charlie should start with zero balance" + ); + + // Use 2× min_default_stake so tao_after_fee (90%) stays above the minimum-stake threshold. + let order_amount = min_default_stake().to_u64() * 2u64; + + // limit_price = u64::MAX → condition always met; fee_recipient = Charlie. + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + order_amount, + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::from_percent(10), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id.clone()), + orders, + false, + )); + + // Order must be marked as executed. + assert_eq!(Orders::::get(id), Some(OrderStatus::Fulfilled)); + + // Buy fee is computed from input: fee = 10% * order_amount. Exact integer arithmetic. + let expected_fee = Perbill::from_percent(10) * order_amount; + assert_eq!( + SubtensorModule::get_coldkey_balance(&charlie_id), + TaoBalance::from(expected_fee), + "charlie (fee recipient) should receive exactly the buy fee" + ); + + // Alice spent exactly order_amount TAO (fee is deducted from the order amount, + // not charged on top), so she has min_default_stake()*10 - order_amount remaining. + assert_eq!( + SubtensorModule::get_coldkey_balance(&alice_id), + min_default_stake() * 8u64.into(), + "alice should have min_default_stake()*8 TAO remaining after the order" + ); + + // Alice must have received staked alpha through Bob. The pool received + // tao_after_fee = order_amount - fee; check within 1% of that expected alpha. + let tao_after_fee = order_amount - expected_fee; + let staked = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + assert!( + staked >= AlphaBalance::from(tao_after_fee * 99 / 100) + && staked <= AlphaBalance::from(tao_after_fee), + "alice should hold approximately tao_after_fee alpha after the LimitBuy with fee (got {staked:?})" + ); + }); +} + +/// `execute_batched_orders` correctly forwards fees to a shared fee recipient (Eve) +/// when both a buy and a sell order designate the same recipient. +/// +/// Fee mechanics for batched orders: +/// Buy: fee = gross - net = fee_rate * gross (withheld from pool input, transferred from pallet). +/// Sell: fee = fee_rate * gross_share (withheld from TAO pool output, inherits slippage). +/// +/// The buy fee is exact; the sell fee is approximate (pool slippage). +#[test] +fn batched_fee_forwarded_to_recipient() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + let dave_id = Sr25519Keyring::Dave.to_account_id(); + let eve_id = Sr25519Keyring::Eve.to_account_id(); + + setup_subnet(netuid); + + setup_buyer_seller(netuid, &alice_id, &charlie_id, &bob_id, &dave_id); + + // Eve (shared fee recipient) starts with zero balance. + assert_eq!( + SubtensorModule::get_coldkey_balance(&eve_id), + TaoBalance::from(0u64), + "eve should start with zero balance" + ); + + let buy = make_signed_order( + alice, + charlie_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::from_percent(10), + eve_id.clone(), // fee goes to Eve + ); + let sell = make_signed_order( + bob, + dave_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().into(), + 0, // price floor — always satisfied + u64::MAX, // no expiry + Perbill::from_percent(10), + eve_id.clone(), // fee goes to Eve + ); + let buy_id = order_id(&buy.order); + let sell_id = order_id(&sell.order); + + let orders = make_order_batch(vec![buy, sell]); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + )); + + // Both orders must be fulfilled. + assert_eq!(Orders::::get(buy_id), Some(OrderStatus::Fulfilled)); + assert_eq!( + Orders::::get(sell_id), + Some(OrderStatus::Fulfilled) + ); + + // Buy fee is exact: fee = 10% * min_default_stake(). + let buy_fee = Perbill::from_percent(10) * min_default_stake().to_u64(); + + // Sell fee is approximate (pool slippage). Lower bound: 10% of 99% of amount. + let sell_fee_lower_bound = + Perbill::from_percent(10) * (min_default_stake().to_u64() * 99 / 100); + + // Eve must have received at least buy_fee + sell_fee_lower_bound, + // and at most buy_fee + 10% * amount (upper bound on sell fee with no slippage). + let sell_fee_upper_bound = Perbill::from_percent(10) * min_default_stake().to_u64(); + let eve_balance = SubtensorModule::get_coldkey_balance(&eve_id); + assert!( + eve_balance >= TaoBalance::from(buy_fee + sell_fee_lower_bound) + && eve_balance <= TaoBalance::from(buy_fee + sell_fee_upper_bound), + "eve should receive combined buy+sell fee within tolerance (got {eve_balance:?})" + ); + }); +} + +/// `execute_batched_orders` routes fees to the correct recipient when two orders +/// in the same batch designate different fee recipients (Charlie for the buy, +/// Dave for the sell). +/// +/// Verifies that: +/// - Charlie receives exactly the buy fee (no pool slippage on input). +/// - Dave receives approximately the sell fee (within 1%, due to pool slippage). +/// - Neither recipient received both fees. +#[test] +fn batched_multiple_fee_recipients_each_receive_correct_amount() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob = Sr25519Keyring::Bob; + let bob_id = bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + let dave_id = Sr25519Keyring::Dave.to_account_id(); + + setup_subnet(netuid); + + setup_buyer_seller(netuid, &alice_id, &charlie_id, &bob_id, &dave_id); + + // Charlie and Dave start with zero free balance (they are hotkeys; no initial funding). + assert_eq!( + SubtensorModule::get_coldkey_balance(&charlie_id), + TaoBalance::from(0u64), + "charlie should start with zero balance" + ); + assert_eq!( + SubtensorModule::get_coldkey_balance(&dave_id), + TaoBalance::from(0u64), + "dave should start with zero balance" + ); + + // Alice: LimitBuy, fee goes to Charlie. + let buy = make_signed_order( + alice, + charlie_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::from_percent(10), + charlie_id.clone(), // buy fee to Charlie + ); + // Bob: TakeProfit, fee goes to Dave. + let sell = make_signed_order( + bob, + dave_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().into(), + 0, // price floor — always satisfied + u64::MAX, // no expiry + Perbill::from_percent(10), + dave_id.clone(), // sell fee to Dave + ); + let buy_id = order_id(&buy.order); + let sell_id = order_id(&sell.order); + + let orders = make_order_batch(vec![buy, sell]); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + )); + + // Both orders must be fulfilled. + assert_eq!(Orders::::get(buy_id), Some(OrderStatus::Fulfilled)); + assert_eq!( + Orders::::get(sell_id), + Some(OrderStatus::Fulfilled) + ); + + // Charlie receives exactly the buy fee: 10% * min_default_stake(). + let expected_buy_fee = Perbill::from_percent(10) * min_default_stake().to_u64(); + assert_eq!( + SubtensorModule::get_coldkey_balance(&charlie_id), + TaoBalance::from(expected_buy_fee), + "charlie (buy fee recipient) should receive exactly the buy fee" + ); + + // Dave receives approximately the sell fee (pool slippage ≤ 1%). + // Expected sell fee ≈ 10% of min_default_stake (the seller's gross TAO share). + let expected_sell_fee = Perbill::from_percent(10) * min_default_stake().to_u64(); + let sell_fee_lower_bound = + Perbill::from_percent(10) * (min_default_stake().to_u64() * 99 / 100); + let dave_balance = SubtensorModule::get_coldkey_balance(&dave_id); + assert!( + dave_balance >= TaoBalance::from(sell_fee_lower_bound) + && dave_balance <= TaoBalance::from(expected_sell_fee), + "dave (sell fee recipient) should receive approximately the sell fee within 1% (got {dave_balance:?})" + ); + + // Verify fees are separate: neither recipient received both fees. + // Charlie's balance is exactly buy_fee (not buy_fee + sell_fee). + let charlie_balance = SubtensorModule::get_coldkey_balance(&charlie_id); + assert!( + charlie_balance <= TaoBalance::from(expected_buy_fee), + "charlie should not have received the sell fee (got {charlie_balance:?})" + ); + // Dave's balance is ≤ sell_fee (not sell_fee + buy_fee). + assert!( + dave_balance <= TaoBalance::from(expected_sell_fee), + "dave should not have received the buy fee (got {dave_balance:?})" + ); + }); +} + +// ── max_slippage enforcement against the real dynamic-mechanism AMM ─────────── + +/// A StopLoss order whose price condition is met (`current_price ≤ limit_price`) +/// but whose `max_slippage`-derived floor exceeds the pool's actual price is +/// silently skipped by `execute_orders`. +/// +/// Setup: +/// Dynamic subnet, equal reserves → pool price = 1.0 (raw ratio, i.e. 1 rao/alpha). +/// limit_price = 2_000_000_000 (2.0 × 10⁹) → StopLoss trigger: 1.0 ≤ 2.0 ✓ +/// max_slippage = 10% → effective AMM floor = 2_000_000_000 − 10% × 2_000_000_000 = 1_800_000_000. +/// Pool price = 1_000_000_000 (1.0 × 10⁹) < 1_800_000_000 → PriceLimitExceeded. +/// `execute_orders` catches the error and skips the order (no storage write). +/// Because `sell_alpha` is `#[transactional]`, the stake decrement is rolled back. +#[test] +fn execute_orders_stoploss_max_slippage_exceeds_pool_price_skipped() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_dynamic_subnet(netuid); + + // Alice needs staked alpha so the sell can debit her position. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 10u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &bob_id, + &alice_id, + netuid, + initial_alpha, + ); + + // limit_price = 2_000_000_000 (2.0 × 10⁹): StopLoss triggers when price ≤ 2.0; pool is at 1.0 → met. + // max_slippage = 10% → effective AMM floor = 1_800_000_000. + // Pool price = 1_000_000_000 < 1_800_000_000 → PriceLimitExceeded → order skipped. + let signed = make_signed_order_with_slippage_rt( + alice, + bob_id.clone(), + netuid, + OrderType::StopLoss, + min_default_stake().into(), + 2_000_000_000, // trigger at price 2.0 × 10⁹; pool is at 1.0 — condition met + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + Some(Perbill::from_percent(10)), + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // execute_orders is best-effort: the call succeeds even though the order + // is rejected by the AMM. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order must NOT have been written to storage — it was silently skipped. + assert!( + Orders::::get(id).is_none(), + "order should have been skipped, not stored" + ); + + // `try_execute_order` is #[transactional]: the stake decrement inside + // `unstake_from_subnet` is rolled back when the AMM rejects the swap, + // so alice's alpha is unchanged. + let remaining = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + assert_eq!( + remaining, initial_alpha, + "alice's staked alpha should be unchanged when the order is rolled back" + ); + }); +} + +/// Contrasting test: the same StopLoss order without `max_slippage` executes +/// successfully against the dynamic-mechanism pool. +/// +/// This confirms that the price condition alone is not the blocker and that +/// the previous test's skip is genuinely caused by the slippage floor. +#[test] +fn execute_orders_stoploss_no_slippage_executes_on_dynamic_subnet() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_dynamic_subnet(netuid); + + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 10u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &bob_id, + &alice_id, + netuid, + initial_alpha, + ); + + // Same limit_price — trigger still met. max_slippage = None → floor = 0 + // → AMM limit = 0 → no floor constraint → pool executes the sell. + // + // Sell 5× min_default_stake: the dynamic AMM deducts a small fee (~0.05%) + // from the alpha input before swapping, so the TAO output is slightly below + // the sell amount. The `validate_remove_stake` sim-swap check verifies that + // the TAO equivalent is ≥ DefaultMinStake — selling 5× ensures the fee cannot + // drag the output below that floor even on a lightly-loaded pool. + let sell_amount = min_default_stake().to_u64() * 5; + let signed = make_signed_order_with_slippage_rt( + alice, + bob_id.clone(), + netuid, + OrderType::StopLoss, + sell_amount, + 2_000_000_000, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + None, + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order must be marked as fulfilled. + assert_eq!( + Orders::::get(id), + Some(OrderStatus::Fulfilled), + "order should be fulfilled when no slippage floor is set" + ); + + // Alice's staked alpha must have decreased by the sold amount (5× min_default_stake). + let remaining = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + assert_eq!( + remaining, + AlphaBalance::from(min_default_stake().to_u64() * 5u64), + "alice's staked alpha should decrease by 5×min_default_stake after StopLoss executes" + ); + }); +} + +// ── Partial fill tests ──────────────────────────────────────────────────────── + +/// A LimitBuy order with `partial_fills_enabled` is partially filled on the +/// first `execute_orders` call, then fully filled (Fulfilled) on a second call +/// carrying the remaining amount. +/// +/// The signed payload (`VersionedOrder`) is identical in both submissions so +/// both calls share the same order-id. Only `SignedOrder::partial_fill` changes. +#[test] +fn execute_orders_partial_fill_then_complete() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + // Alice funds two fills: partial_amount + remaining_amount = order amount. + let order_amount = min_default_stake().to_u64() * 4u64; + let partial_amount = min_default_stake().to_u64() * 3u64; + let remaining_amount = order_amount - partial_amount; + + add_balance_to_coldkey_account(&alice_id, TaoBalance::from(order_amount * 2u64)); + + // Create the hotkey association Alice → Bob. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Build the base signed order — this exact payload is re-used for both submissions. + let first_signed = make_partial_fill_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + order_amount, + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + charlie_id.clone(), + charlie_id.clone(), // relayer = caller + Some(partial_amount), + ); + let id = order_id(&first_signed.order); + + // ── First submission: partial fill ──────────────────────────────────── + let orders = make_order_batch(vec![first_signed.clone()]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id.clone()), + orders, + false, + )); + + // After the first execution the order must be partially filled. + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(partial_amount)), + "order should be PartiallyFilled({partial_amount}) after first execution" + ); + + // ── Second submission: fill the remainder ───────────────────────────── + // Clone the order payload from the first signed order (same VersionedOrder, + // same order-id) but set partial_fill to the remaining amount. + let second_signed = SignedOrder { + order: first_signed.order.clone(), + signature: first_signed.signature.clone(), + partial_fill: Some(remaining_amount), + }; + + let orders2 = make_order_batch(vec![second_signed]); + + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id.clone()), + orders2, + false, + )); + + // After the second execution the order must be fulfilled. + assert_eq!( + Orders::::get(id), + Some(OrderStatus::Fulfilled), + "order should be Fulfilled after the remaining amount is filled" + ); + }); +} + +/// Same partial-fill-then-complete scenario exercised through +/// `execute_batched_orders`. +/// +/// The buy order is the only order in the batch both times, so the batch is +/// buy-dominant and routes all TAO through the pool. The signed payload is +/// identical between submissions; only `SignedOrder::partial_fill` changes. +#[test] +fn execute_batched_orders_partial_fill_then_complete() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + + let order_amount = min_default_stake().to_u64() * 4u64; + let partial_amount = min_default_stake().to_u64() * 3u64; + let remaining_amount = order_amount - partial_amount; + + add_balance_to_coldkey_account(&alice_id, TaoBalance::from(order_amount * 2u64)); + + // Create the hotkey association Alice → Bob. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Build the base signed order — identical payload reused in both batches. + let first_signed = make_partial_fill_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + order_amount, + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + charlie_id.clone(), + charlie_id.clone(), // relayer = caller + Some(partial_amount), + ); + let id = order_id(&first_signed.order); + + // ── First batch: partial fill ───────────────────────────────────────── + let orders = make_order_batch(vec![first_signed.clone()]); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders, + )); + + assert_eq!( + Orders::::get(id), + Some(OrderStatus::PartiallyFilled(partial_amount)), + "order should be PartiallyFilled({partial_amount}) after first batch" + ); + + // ── Second batch: fill the remainder ────────────────────────────────── + let second_signed = SignedOrder { + order: first_signed.order.clone(), + signature: first_signed.signature.clone(), + partial_fill: Some(remaining_amount), + }; + + let orders2 = make_order_batch(vec![second_signed]); + + assert_ok!(LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id.clone()), + netuid, + orders2, + )); + + assert_eq!( + Orders::::get(id), + Some(OrderStatus::Fulfilled), + "order should be Fulfilled after the remaining amount is filled in the second batch" + ); + }); +} + +// ── sim-swap partial-fill guard ─────────────────────────────────────────────── + +/// A LimitBuy order with 1 ppb max_slippage is silently skipped by +/// `execute_orders` because the sim-swap detects that the AMM would only +/// consume a microscopic fraction of the input before the price ceiling is +/// breached (partial fill). +/// +/// Setup: dynamic subnet, equal 1T TAO / 1T alpha reserves → pool price = 1.0. +/// limit_price = 1_000_000_000 (1.0 × 10⁹): LimitBuy triggers when price ≤ 1.0 — met. +/// max_slippage = 1 ppb → ceiling = 1_000_000_001, barely above pool price. +/// Sending any real TAO amount immediately pushes the price above the ceiling, +/// so sim.amount_paid_in + sim.fee_paid < input_amount → SlippageTooHigh. +/// `execute_orders` is best-effort: it catches the error and skips the order. +#[test] +fn execute_orders_buy_tight_slippage_partial_fill_skipped() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_dynamic_subnet(netuid); + + // Alice needs a hotkey association for the buy to validate. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + // Alice needs TAO to fund the buy. + fund_account(&alice_id); + + let initial_balance = SubtensorModule::get_coldkey_balance(&alice_id); + + // limit_price = 1_000_000_000 (= 1.0 × 10⁹): LimitBuy trigger (spot ≤ 1.0) met. + // max_slippage = 1 ppb → price ceiling = 1_000_000_001, just above pool price. + // Any real TAO amount pushes the price above the ceiling → partial fill detected. + let signed = make_signed_order_with_slippage_rt( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + 1_000_000_000, // price ceiling at exactly 1.0 × 10⁹ — trigger met + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + Some(Perbill::from_parts(1)), // 1 ppb — ceiling barely above spot + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // execute_orders is best-effort: the call succeeds even though the guard + // rejects the order due to partial fill. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order must NOT have been written to storage — it was silently skipped. + assert!( + Orders::::get(id).is_none(), + "order should have been skipped, not stored" + ); + + // No funds should have been debited from Alice — the rollback guard + // prevents any state change when partial fill is detected. + let final_balance = SubtensorModule::get_coldkey_balance(&alice_id); + assert_eq!( + final_balance, initial_balance, + "alice's TAO balance should be unchanged when the order is rolled back" + ); + }); +} + +/// Same setup as `execute_orders_buy_tight_slippage_partial_fill_skipped` but +/// submitted via `execute_batched_orders`. The batch hard-fails with +/// `SlippageTooHigh` because batched execution is not best-effort. +#[test] +fn execute_batched_orders_buy_tight_slippage_partial_fill_fails() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_dynamic_subnet(netuid); + + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + fund_account(&alice_id); + + // Identical order to the execute_orders variant above. + let signed = make_signed_order_with_slippage_rt( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + min_default_stake().into(), + 1_000_000_000, + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + Some(Perbill::from_parts(1)), + ); + + let orders = make_order_batch(vec![signed]); + + // Batched execution hard-fails: the partial-fill guard surfaces the error + // directly to the caller instead of silently skipping. + assert_noop!( + LimitOrders::execute_batched_orders(RuntimeOrigin::signed(charlie_id), netuid, orders,), + pallet_subtensor::Error::::SlippageTooHigh + ); + }); +} + +/// A TakeProfit order with 1 ppb max_slippage is silently skipped by +/// `execute_orders` because the sim-swap detects that selling any real alpha +/// amount immediately pushes the pool price below the 1 ppb floor. +/// +/// Setup: dynamic subnet, equal 1T TAO / 1T alpha reserves → pool price = 1.0. +/// limit_price = 1_000_000_000 (1.0 × 10⁹): TakeProfit triggers when price ≥ 1.0 — met. +/// max_slippage = 1 ppb → floor = 999_999_999, barely below pool price. +/// Selling any real alpha amount moves the price below the floor, +/// so sim.amount_paid_in + sim.fee_paid < input_amount → SlippageTooHigh. +/// `execute_orders` is best-effort: it catches the error and skips the order. +#[test] +fn execute_orders_sell_tight_slippage_partial_fill_skipped() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_dynamic_subnet(netuid); + + // Alice needs a hotkey association and staked alpha for the sell. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 10u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &bob_id, + &alice_id, + netuid, + initial_alpha, + ); + + // limit_price = 1_000_000_000 (= 1.0 × 10⁹): TakeProfit trigger (spot ≥ 1.0) met. + // max_slippage = 1 ppb → price floor = 999_999_999, just below pool price. + // Any real alpha sale pushes the price below the floor → partial fill detected. + let signed = make_signed_order_with_slippage_rt( + alice, + bob_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().into(), + 1_000_000_000, // price floor at exactly 1.0 × 10⁹ — trigger met + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + Some(Perbill::from_parts(1)), // 1 ppb — floor barely below spot + ); + let id = order_id(&signed.order); + + let orders = make_order_batch(vec![signed]); + + // execute_orders is best-effort: the call succeeds even though the guard + // rejects the order due to partial fill. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + orders, + false, + )); + + // Order must NOT have been written to storage — it was silently skipped. + assert!( + Orders::::get(id).is_none(), + "order should have been skipped, not stored" + ); + + // Alice's staked alpha must be unchanged — the rollback guard prevents + // any state change when partial fill is detected. + let remaining_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + assert_eq!( + remaining_alpha, initial_alpha, + "alice's staked alpha should be unchanged when the order is rolled back" + ); + }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// Migration integration tests +// ───────────────────────────────────────────────────────────────────────────── + +fn migration_key() -> BoundedVec> { + BoundedVec::truncate_from(b"migrate_register_pallet_hotkey".to_vec()) +} + +fn pallet_acct() -> AccountId { + PalletId(*b"bt/limit").into_account_truncating() +} + +fn pallet_hotkey() -> AccountId { + PalletId(*b"bt/lmhky").into_account_truncating() +} + +/// `on_runtime_upgrade` registers the pallet hotkey and marks the migration as run. +/// +/// Starting from the default genesis (which already registers the hotkey and +/// enables the pallet via `GenesisConfig::build`), the upgrade hook must: +/// - set `HasMigrationRun[migration_key]` to `true` +/// - leave `LimitOrdersEnabled` untouched (still `true`) +/// - leave the hotkey registration intact +#[test] +fn on_runtime_upgrade_marks_migration_run_without_touching_pallet_status() { + new_test_ext().execute_with(|| { + assert!(LimitOrdersEnabled::::get()); + assert!(!HasMigrationRun::::get(migration_key())); + assert!(SubtensorModule::coldkey_owns_hotkey( + &pallet_acct(), + &pallet_hotkey() + )); + + >::on_runtime_upgrade(); + + assert!( + HasMigrationRun::::get(migration_key()), + "migration must be marked as run" + ); + assert!( + LimitOrdersEnabled::::get(), + "upgrade must not change LimitOrdersEnabled" + ); + assert!(SubtensorModule::coldkey_owns_hotkey( + &pallet_acct(), + &pallet_hotkey() + )); + }); +} + +/// Running `on_runtime_upgrade` twice is a no-op on the second call. +#[test] +fn on_runtime_upgrade_is_idempotent() { + new_test_ext().execute_with(|| { + >::on_runtime_upgrade(); + assert!(HasMigrationRun::::get(migration_key())); + + // Second run must not change any state. + LimitOrdersEnabled::::set(false); + >::on_runtime_upgrade(); + + assert!( + !LimitOrdersEnabled::::get(), + "second upgrade must not touch LimitOrdersEnabled" + ); + }); +} + +// ── Conviction-lock protection ──────────────────────────────────────────────── + +/// A sell order whose alpha is fully conviction-locked is silently skipped by +/// `execute_orders` (best-effort path): the extrinsic returns `Ok`, the order +/// is never written to `Orders` storage, and the seller's staked alpha is +/// unchanged. +#[test] +fn individual_sell_order_skipped_when_alpha_is_conviction_locked() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Give alice staked alpha through bob. + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 3u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &bob_id, + &alice_id, + netuid, + initial_alpha, + ); + seed_subnet_tao(netuid, TaoBalance::from(initial_alpha.to_u64())); + + // Lock ALL of alice's alpha with conviction — nothing is available to sell. + assert_ok!(SubtensorModule::do_lock_stake( + &alice_id, + netuid, + &bob_id, + initial_alpha, + )); + + let sell_amount = min_default_stake().to_u64(); + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::TakeProfit, + sell_amount, + 0, // price floor — always satisfied + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + // Best-effort: the locked order is silently skipped, extrinsic still returns Ok. + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(charlie_id), + make_order_batch(vec![signed]), + false, + )); + + // Order must NOT be in storage — it was skipped, not fulfilled. + assert_eq!( + Orders::::get(id), + None, + "order should be skipped when alpha is conviction-locked" + ); + + // Alice's staked alpha must be completely unchanged. + let remaining = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + assert_eq!( + remaining, initial_alpha, + "conviction-locked alpha must not be moved by a skipped sell order" + ); + }); +} + +/// A batched sell order whose alpha is fully conviction-locked causes the +/// entire `execute_batched_orders` call to fail atomically with +/// `StakeUnavailable` — no state is committed. +#[test] +fn batched_sell_order_fails_when_alpha_is_conviction_locked() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + + setup_subnet(netuid); + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // Give alice staked alpha through bob. + let initial_alpha: AlphaBalance = (min_default_stake().to_u64() * 3u64).into(); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &bob_id, + &alice_id, + netuid, + initial_alpha, + ); + seed_subnet_tao(netuid, TaoBalance::from(initial_alpha.to_u64())); + + // Lock ALL of alice's alpha with conviction — nothing is available to sell. + assert_ok!(SubtensorModule::do_lock_stake( + &alice_id, + netuid, + &bob_id, + initial_alpha, + )); + + let sell = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::TakeProfit, + min_default_stake().to_u64(), + 0, // price floor — always satisfied + u64::MAX, + Perbill::zero(), + charlie_id.clone(), + ); + + // Atomic path: the lock violation must revert the entire batch. + assert_noop!( + LimitOrders::execute_batched_orders( + RuntimeOrigin::signed(charlie_id), + netuid, + make_order_batch(vec![sell]), + ), + pallet_subtensor::Error::::StakeUnavailable + ); + }); +} + +/// Regression test for `#[transactional]` on `try_execute_order`. +/// +/// Invariant: a single buy order is **atomic** — either the TAO→alpha swap AND +/// the fee transfer both commit (and the order is recorded), or neither does. +/// +/// ## Trigger (buy path) +/// `buy_alpha` only checks the signer can remove `tao_after_fee`, NOT the full +/// `tao_in`. So if the signer's free TAO sits in the window +/// `[tao_after_fee, tao_in)`, `buy_alpha` succeeds but the subsequent +/// `forward_fee` of `fee_tao` fails for insufficient funds. In best-effort mode +/// (`should_fail = false`) the caller catches that `Err`, emits `OrderSkipped`, +/// and returns `Ok(())`. Without `#[transactional]` the orphaned `buy_alpha` +/// swap would be committed by the outer storage layer; with it, the whole order +/// rolls back. +/// +/// ## Arithmetic (ED = 500, min_default_stake = 2_000_000) +/// - `amount = tao_in = min_default_stake * 10 = 20_000_000` +/// - `fee_rate = 10%` → `fee_tao = 2_000_000`, `tao_after_fee = 18_000_000` +/// (≥ min_default_stake, so it clears the `AmountTooLow` check). +/// - Fund the signer with `B = 19_000_000`, which sits strictly inside the +/// vulnerable window `[18_000_000, 20_000_000)`: +/// * `buy_alpha` passes: `tao_after_fee (18_000_000) ≤ B`, and +/// `stake_into_subnet` debits the full `18_000_000` because +/// `B - ED = 18_999_500 ≥ 18_000_000` (no ED clamp). Balance → 1_000_000. +/// * `forward_fee` of `fee_tao (2_000_000)` then fails: only 1_000_000 left. +#[test] +fn fee_failure_after_buy_rolls_back_swap() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1u16); + let alice = Sr25519Keyring::Alice; + let alice_id = alice.to_account_id(); + let bob_id = Sr25519Keyring::Bob.to_account_id(); + // Fee recipient distinct from the signer (Alice) and the relayer. + let charlie_id = Sr25519Keyring::Charlie.to_account_id(); + // Relayer that submits the batch — kept distinct so its balance is irrelevant. + let dave_id = Sr25519Keyring::Dave.to_account_id(); + + setup_subnet(netuid); + + // Create the hotkey association so buy_alpha's validation passes. + let _ = SubtensorModule::create_account_if_non_existent(&alice_id, &bob_id); + + // amount = tao_in = min_default_stake * 10; fee_rate = 10%. + // fee_tao = 2_000_000 + // tao_after_fee = 18_000_000 (≥ min_default_stake, clears AmountTooLow) + let tao_in = min_default_stake().to_u64() * 10u64; + + // Fund Alice's coldkey so her free TAO is inside the vulnerable window + // [tao_after_fee, tao_in) = [18_000_000, 20_000_000): 19_000_000. + // buy_alpha passes (18_000_000 ≤ 19_000_000, and 19_000_000 - ED clears the + // full debit), leaving 1_000_000 — which is < fee_tao (2_000_000), so + // forward_fee fails for insufficient funds. + let signer_balance = TaoBalance::from(19_000_000u64); + add_balance_to_coldkey_account(&alice_id, signer_balance); + + let signed = make_signed_order( + alice, + bob_id.clone(), + netuid, + OrderType::LimitBuy, + tao_in, + u64::MAX, // price ceiling — always satisfied + u64::MAX, // no expiry + Perbill::from_percent(10), + charlie_id.clone(), + ); + let id = order_id(&signed.order); + + // Snapshot the observable state before the call. + let alice_balance_before = SubtensorModule::get_coldkey_balance(&alice_id); + let alice_stake_before = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid); + let charlie_balance_before = SubtensorModule::get_coldkey_balance(&charlie_id); + + // Sanity: Alice really is funded to 19_000_000, inside the window. + assert_eq!(alice_balance_before, signer_balance); + + let orders = make_order_batch(vec![signed]); + + // Best-effort path: the per-order error is caught, OrderSkipped is emitted, + // and the extrinsic returns Ok(()). + assert_ok!(LimitOrders::execute_orders( + RuntimeOrigin::signed(dave_id), + orders, + false, + )); + + // ── Atomic rollback assertions ─────────────────────────────────────────── + + // 1. The signer's free TAO is unchanged: the buy_alpha debit was rolled back. + assert_eq!( + SubtensorModule::get_coldkey_balance(&alice_id), + alice_balance_before, + "signer's TAO must be unchanged: the orphaned buy_alpha swap must roll back when forward_fee fails" + ); + + // 2. No alpha was credited to the signer: the swap was rolled back. + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&bob_id, &alice_id, netuid), + alice_stake_before, + "signer's staked alpha must be unchanged: no alpha may be credited from a rolled-back buy" + ); + + // 3. The order was NOT recorded — it must remain replayable-free, i.e. absent. + assert!( + Orders::::get(id).is_none(), + "order must not be recorded when its execution failed and rolled back" + ); + + // 4. The fee recipient received nothing. + assert_eq!( + SubtensorModule::get_coldkey_balance(&charlie_id), + charlie_balance_before, + "fee recipient's balance must be unchanged: the fee transfer failed and rolled back" + ); + + // 5. An OrderSkipped event was emitted for this order id. + let skipped = System::events().into_iter().any(|record| { + matches!( + record.event, + RuntimeEvent::LimitOrders(pallet_limit_orders::Event::OrderSkipped { + order_id: skipped_id, + .. + }) if skipped_id == id + ) + }); + assert!( + skipped, + "an OrderSkipped event must be emitted for the failed order" + ); + }); +} diff --git a/runtime/tests/metadata.rs b/runtime/tests/metadata.rs index 3409098b41..fb73c58890 100644 --- a/runtime/tests/metadata.rs +++ b/runtime/tests/metadata.rs @@ -9,7 +9,6 @@ fn is_pallet_error(segments: &[String]) -> bool { "pallet_admin_utils", "pallet_subtensor_collective", "pallet_commitments", - "pallet_registry", "pallet_subtensor", ]; diff --git a/scripts/discover_pallets.sh b/scripts/discover_pallets.sh index 0b37239380..e42e6f7825 100755 --- a/scripts/discover_pallets.sh +++ b/scripts/discover_pallets.sh @@ -1,20 +1,53 @@ #!/usr/bin/env bash +set -euo pipefail + # Auto-discover benchmarked pallets. # # Finds all pallets under pallets/ that have both: -# - src/benchmarking.rs (or src/benchmarks.rs) -# - src/weights.rs +# - src/benchmarking.rs (or src/benchmarks.rs) +# - src/weights.rs +# +# Then filters that list to pallets actually registered in runtime/src/lib.rs +# define_benchmarks!(...). A pallet having benchmark files is not enough for: +# +# node-subtensor benchmark pallet --pallet= +# +# The pallet must also be present in the runtime benchmark registry. # # Outputs one line per pallet: "pallet_name pallets//src/weights.rs" # The pallet name is derived from the Cargo.toml `name` field with dashes -> underscores. ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +RUNTIME_FILE="$ROOT_DIR/runtime/src/lib.rs" + +RUNTIME_BENCHMARKS="$( + perl -0ne ' + if (/define_benchmarks!\s*\((.*?)\)\s*;/s) { + my $body = $1; + while ($body =~ /\[\s*([A-Za-z0-9_:]+)\s*,/g) { + my $name = $1; + $name =~ s/::.*$//; + print "$name\n"; + } + } + ' "$RUNTIME_FILE" | sort -u +)" for dir in "$ROOT_DIR"/pallets/*/; do - [ -f "$dir/src/weights.rs" ] || continue - [ -f "$dir/src/benchmarking.rs" ] || [ -f "$dir/src/benchmarks.rs" ] || continue + [ -f "$dir/src/weights.rs" ] || continue + [ -f "$dir/src/benchmarking.rs" ] || [ -f "$dir/src/benchmarks.rs" ] || continue + + name="$( + awk -F '"' '/^name[[:space:]]*=/ { print $2; exit }' "$dir/Cargo.toml" \ + | tr '-' '_' + )" + + [ -n "$name" ] || continue + + if ! printf '%s\n' "$RUNTIME_BENCHMARKS" | grep -qxF "$name"; then + continue + fi - name=$(grep '^name' "$dir/Cargo.toml" | head -1 | sed 's/.*= *"\(.*\)"/\1/' | tr '-' '_') - relpath="pallets/$(basename "$dir")/src/weights.rs" - echo "$name $relpath" -done + relpath="pallets/$(basename "$dir")/src/weights.rs" + echo "$name $relpath" +done \ No newline at end of file diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index e14617be24..f6fae12fbe 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -174,7 +174,6 @@ mod tests { Preimage : pallet_preimage = 14, Scheduler : pallet_scheduler = 15, Proxy : pallet_subtensor_proxy = 16, - Registry : pallet_registry = 17, Commitments : pallet_commitments = 18, AdminUtils : pallet_admin_utils = 19, SafeMode : pallet_safe_mode = 20 diff --git a/ts-tests/biome.jsonc b/ts-tests/biome.jsonc index 426aca155a..033d799010 100644 --- a/ts-tests/biome.jsonc +++ b/ts-tests/biome.jsonc @@ -3,7 +3,7 @@ "vcs": { "enabled": false, "clientKind": "git", - "useIgnoreFile": false, + "useIgnoreFile": false }, "files": { "ignoreUnknown": false, @@ -16,17 +16,17 @@ "**/.yarn/**", "test/tsconfig.json", "tmp", - "**/tmp/", + "**/tmp/" ] }, "formatter": { "enabled": true, "indentStyle": "space", "lineWidth": 120, - "indentWidth": 4, + "indentWidth": 4 }, "organizeImports": { - "enabled": true, + "enabled": true }, "linter": { "enabled": true, @@ -34,27 +34,27 @@ "recommended": true, "suspicious": { "noExplicitAny": "off", - "noShadowRestrictedNames": "off", + "noShadowRestrictedNames": "off" }, "correctness": { - "noUnusedImports": "error", - }, + "noUnusedImports": "error" + } }, - "ignore": [], + "ignore": [] }, "javascript": { "formatter": { "quoteStyle": "double", "semicolons": "always", - "trailingCommas": "es5", - }, + "trailingCommas": "es5" + } }, "json": { "formatter": { - "enabled": false, + "enabled": false }, "linter": { - "enabled": false, - }, - }, -} + "enabled": false + } + } +} \ No newline at end of file diff --git a/ts-tests/ink/bittensor.json b/ts-tests/ink/bittensor.json new file mode 100644 index 0000000000..1a543547bc --- /dev/null +++ b/ts-tests/ink/bittensor.json @@ -0,0 +1,2196 @@ +{ + "source": { + "hash": "0x69edf9f009ca08b785fad0aacb75a1d8ddad6d231848c8ca12b75f0bfc943b86", + "language": "ink! 5.1.1", + "compiler": "rustc 1.89.0", + "build_info": { + "build_mode": "Release", + "cargo_contract_version": "5.0.3", + "rust_toolchain": "stable-x86_64-unknown-linux-gnu", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } + }, + "contract": { + "name": "bittensor", + "version": "0.1.0", + "authors": [ + "[your_name] <[your_email]>" + ] + }, + "image": null, + "spec": { + "constructors": [ + { + "args": [], + "default": false, + "docs": [ + "Constructor" + ], + "label": "new", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 1 + }, + "selector": "0x9bae9d5e" + }, + { + "args": [], + "default": false, + "docs": [ + "Constructor" + ], + "label": "default", + "payable": false, + "returnType": { + "displayName": [ + "ink_primitives", + "ConstructorResult" + ], + "type": 1 + }, + "selector": "0xed4b9d1b" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": [ + "AccountId" + ], + "type": 11 + }, + "balance": { + "displayName": [ + "Balance" + ], + "type": 14 + }, + "blockNumber": { + "displayName": [ + "BlockNumber" + ], + "type": 23 + }, + "chainExtension": { + "displayName": [ + "ChainExtension" + ], + "type": 24 + }, + "hash": { + "displayName": [ + "Hash" + ], + "type": 22 + }, + "maxEventTopics": 4, + "staticBufferSize": 16384, + "timestamp": { + "displayName": [ + "Timestamp" + ], + "type": 14 + } + }, + "events": [], + "lang_error": { + "displayName": [ + "ink", + "LangError" + ], + "type": 3 + }, + "messages": [ + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "coldkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + } + ], + "default": false, + "docs": [], + "label": "get_stake_info_for_hotkey_coldkey_netuid", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 7 + }, + "selector": "0x5b73b8b9" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "add_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x3a656e31" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "remove_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x7758d434" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "unstake_all", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x3f525cc7" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "unstake_all_alpha", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xab74c422" + }, + { + "args": [ + { + "label": "origin_hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "destination_hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "move_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xa06b0c55" + }, + { + "args": [ + { + "label": "destination_coldkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "transfer_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x3528ef5e" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "swap_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x04f7ca30" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "allow_partial", + "type": { + "displayName": [ + "bool" + ], + "type": 15 + } + } + ], + "default": false, + "docs": [], + "label": "add_stake_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x30013b98" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "allow_partial", + "type": { + "displayName": [ + "bool" + ], + "type": 15 + } + } + ], + "default": false, + "docs": [], + "label": "remove_stake_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xc3ce39c8" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "allow_partial", + "type": { + "displayName": [ + "bool" + ], + "type": 15 + } + } + ], + "default": false, + "docs": [], + "label": "swap_stake_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x212ef7ad" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "Option" + ], + "type": 19 + } + } + ], + "default": false, + "docs": [], + "label": "remove_stake_full_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xa6d6ea64" + }, + { + "args": [ + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "set_coldkey_auto_stake_hotkey", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xe24f0d8a" + }, + { + "args": [ + { + "label": "delegate", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "add_proxy", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x528b6757" + }, + { + "args": [ + { + "label": "delegate", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "remove_proxy", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x129d4f75" + }, + { + "args": [ + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + } + ], + "default": false, + "docs": [], + "label": "get_alpha_price", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 20 + }, + "selector": "0x08adc2e2" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "recycle_alpha", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 20 + }, + "selector": "0xb82d0b9a" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "burn_alpha", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 20 + }, + "selector": "0x84ccc19d" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "add_stake_recycle", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 20 + }, + "selector": "0xb144245c" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "add_stake_burn", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 20 + }, + "selector": "0x13160cf7" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "caller_add_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xa5b8d094" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "caller_remove_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xf4ed2209" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "caller_unstake_all", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xa662dec6" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "caller_unstake_all_alpha", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x5a0863c1" + }, + { + "args": [ + { + "label": "origin_hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "destination_hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "caller_move_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x8a1a0ef3" + }, + { + "args": [ + { + "label": "destination_coldkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "caller_transfer_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x7ef3a28d" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + } + ], + "default": false, + "docs": [], + "label": "caller_swap_stake", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x4a07270e" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "allow_partial", + "type": { + "displayName": [ + "bool" + ], + "type": 15 + } + } + ], + "default": false, + "docs": [], + "label": "caller_add_stake_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xd1c93224" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "allow_partial", + "type": { + "displayName": [ + "bool" + ], + "type": 15 + } + } + ], + "default": false, + "docs": [], + "label": "caller_remove_stake_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xeb3c2a2c" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "origin_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "destination_netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "amount", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "u64" + ], + "type": 14 + } + }, + { + "label": "allow_partial", + "type": { + "displayName": [ + "bool" + ], + "type": 15 + } + } + ], + "default": false, + "docs": [], + "label": "caller_swap_stake_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xa5d65480" + }, + { + "args": [ + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + }, + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "limit_price", + "type": { + "displayName": [ + "Option" + ], + "type": 19 + } + } + ], + "default": false, + "docs": [], + "label": "caller_remove_stake_full_limit", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x113e3cc9" + }, + { + "args": [ + { + "label": "netuid", + "type": { + "displayName": [ + "u16" + ], + "type": 6 + } + }, + { + "label": "hotkey", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "caller_set_coldkey_auto_stake_hotkey", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x30fd705d" + }, + { + "args": [ + { + "label": "delegate", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "caller_add_proxy", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0xe6a20239" + }, + { + "args": [ + { + "label": "delegate", + "type": { + "displayName": [], + "type": 4 + } + } + ], + "default": false, + "docs": [], + "label": "caller_remove_proxy", + "mutates": false, + "payable": false, + "returnType": { + "displayName": [ + "ink", + "MessageResult" + ], + "type": 17 + }, + "selector": "0x59392a8e" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [], + "name": "Bittensor" + } + }, + "root_key": "0x00000000", + "ty": 0 + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "composite": {} + }, + "path": [ + "bittensor", + "bittensor", + "Bittensor" + ] + } + }, + { + "id": 1, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 2 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 2 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 2, + "type": { + "def": { + "tuple": [] + } + } + }, + { + "id": 3, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": [ + "ink_primitives", + "LangError" + ] + } + }, + { + "id": 4, + "type": { + "def": { + "array": { + "len": 32, + "type": 5 + } + } + } + }, + { + "id": 5, + "type": { + "def": { + "primitive": "u8" + } + } + }, + { + "id": 6, + "type": { + "def": { + "primitive": "u16" + } + } + }, + { + "id": 7, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 8 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 8 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 8, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 9 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 16 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 9 + }, + { + "name": "E", + "type": 16 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 9, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 10 + } + ], + "index": 1, + "name": "Some" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 10 + } + ], + "path": [ + "Option" + ] + } + }, + { + "id": 10, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "hotkey", + "type": 11, + "typeName": "AccountId" + }, + { + "name": "coldkey", + "type": 11, + "typeName": "AccountId" + }, + { + "name": "netuid", + "type": 12, + "typeName": "Compact" + }, + { + "name": "stake", + "type": 13, + "typeName": "Compact" + }, + { + "name": "locked", + "type": 13, + "typeName": "Compact" + }, + { + "name": "emission", + "type": 13, + "typeName": "Compact" + }, + { + "name": "tao_emission", + "type": 13, + "typeName": "Compact" + }, + { + "name": "drain", + "type": 13, + "typeName": "Compact" + }, + { + "name": "is_registered", + "type": 15, + "typeName": "bool" + } + ] + } + }, + "params": [ + { + "name": "AccountId", + "type": 11 + } + ], + "path": [ + "bittensor", + "StakeInfo" + ] + } + }, + { + "id": 11, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 4, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "AccountId" + ] + } + }, + { + "id": 12, + "type": { + "def": { + "compact": { + "type": 6 + } + } + } + }, + { + "id": 13, + "type": { + "def": { + "compact": { + "type": 14 + } + } + } + }, + { + "id": 14, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 15, + "type": { + "def": { + "primitive": "bool" + } + } + }, + { + "id": 16, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "ReadFailed" + }, + { + "index": 1, + "name": "WriteFailed" + } + ] + } + }, + "path": [ + "bittensor", + "ReadWriteErrorCode" + ] + } + }, + { + "id": 17, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 18 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 18 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 18, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 2 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 16 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 2 + }, + { + "name": "E", + "type": 16 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 19, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 14 + } + ], + "index": 1, + "name": "Some" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 14 + } + ], + "path": [ + "Option" + ] + } + }, + { + "id": 20, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 21 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 3 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 21 + }, + { + "name": "E", + "type": 3 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 21, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 14 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 16 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 14 + }, + { + "name": "E", + "type": 16 + } + ], + "path": [ + "Result" + ] + } + }, + { + "id": 22, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 4, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": [ + "ink_primitives", + "types", + "Hash" + ] + } + }, + { + "id": 23, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 24, + "type": { + "def": { + "variant": {} + }, + "path": [ + "bittensor", + "RuntimeReadWrite" + ] + } + } + ], + "version": 5 +} \ No newline at end of file diff --git a/ts-tests/ink/bittensor.wasm b/ts-tests/ink/bittensor.wasm new file mode 100644 index 0000000000..5d54ecf3d6 Binary files /dev/null and b/ts-tests/ink/bittensor.wasm differ diff --git a/ts-tests/moonwall.config.json b/ts-tests/moonwall.config.json index cc5667af9f..710ea06d02 100644 --- a/ts-tests/moonwall.config.json +++ b/ts-tests/moonwall.config.json @@ -11,7 +11,9 @@ "testFileDir": [ "suites/dev" ], - "runScripts": [], + "runScripts": [ + "generate-types.sh" + ], "multiThreads": true, "reporters": ["basic"], "foundation": { @@ -32,7 +34,9 @@ "--sealing=manual" ], "disableDefaultEthProviders": true, - "newRpcBehaviour": true + "newRpcBehaviour": true, + "maxStartupTimeout": 120000, + "connectTimeout": 30000 } ] } @@ -120,6 +124,39 @@ "endpoints": ["ws://127.0.0.1:9947"] } ] + }, { + "name": "zombienet_evm", + "timeout": 600000, + "testFileDir": ["suites/zombienet_evm"], + "runScripts": [ + "generate-types.sh", + "generate-ink-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"], + "descriptor": "subtensor" + }, + { + "name": "EVM", + "type": "ethers", + "endpoints": ["http://127.0.0.1:9947"], + "descriptor": "evm" + } + ] }, { "name": "zombienet_subnets", "timeout": 600000, diff --git a/ts-tests/package.json b/ts-tests/package.json index 75b5506cf8..d6143e3eeb 100644 --- a/ts-tests/package.json +++ b/ts-tests/package.json @@ -12,7 +12,8 @@ "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" + "generate-types": "polkadot-api add subtensor --wsUrl ws://localhost:9944 --skip-codegen && polkadot-api", + "generate-ink-types": "polkadot-api ink add ./ink/bittensor.json && polkadot-api" }, "keywords": [], "author": "", @@ -23,8 +24,9 @@ "dependencies": { "@inquirer/prompts": "7.3.1", "@noble/ciphers": "^2.1.1", - "polkadot-api": "1.19.2", + "@polkadot-api/ink-contracts": "^0.4.1", "@polkadot-api/merkleize-metadata": "^1.1.15", + "@polkadot-api/sdk-ink": "^0.5.1", "@polkadot-api/substrate-bindings": "^0.17.0", "@polkadot/api": "*", "@polkadot/keyring": "*", @@ -35,6 +37,7 @@ "@zombienet/orchestrator": "0.0.105", "ethereum-cryptography": "3.1.0", "mlkem": "^2.7.0", + "polkadot-api": "^1.22.0", "ps-node": "0.1.6" }, "devDependencies": { @@ -48,7 +51,7 @@ "@types/node": "*", "@types/ps-node": "0.1.3", "@types/yargs": "^17.0.33", - "@vitest/ui": "3.1.3", + "@vitest/ui": "3.2.4", "@zombienet/utils": "^0.0.28", "bottleneck": "2.19.5", "chalk": "^5.4.0", diff --git a/ts-tests/pnpm-lock.yaml b/ts-tests/pnpm-lock.yaml index d92a6b9cd6..1b0ac70502 100644 --- a/ts-tests/pnpm-lock.yaml +++ b/ts-tests/pnpm-lock.yaml @@ -4,43 +4,52 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + toml: 3.0.0 + importers: .: dependencies: '@inquirer/prompts': specifier: 7.3.1 - version: 7.3.1(@types/node@25.3.5) + version: 7.3.1(@types/node@25.9.2) '@noble/ciphers': specifier: ^2.1.1 - version: 2.1.1 + version: 2.2.0 + '@polkadot-api/ink-contracts': + specifier: ^0.4.1 + version: 0.4.6 '@polkadot-api/merkleize-metadata': specifier: ^1.1.15 - version: 1.1.29 + version: 1.2.3 + '@polkadot-api/sdk-ink': + specifier: ^0.5.1 + version: 0.5.1(@polkadot-api/ink-contracts@0.4.6)(polkadot-api@1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2))(rxjs@7.8.2)(typescript@5.8.3)(zod@3.25.76) '@polkadot-api/substrate-bindings': specifier: ^0.17.0 version: 0.17.0 '@polkadot/api': specifier: '*' - version: 16.5.4 + version: 16.5.6 '@polkadot/keyring': specifier: '*' - version: 14.0.1(@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1))(@polkadot/util@14.0.1) + version: 14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) '@polkadot/types': specifier: '*' - version: 16.5.4 + version: 16.5.6 '@polkadot/types-codec': specifier: '*' - version: 16.5.4 + version: 16.5.6 '@polkadot/util': specifier: '*' - version: 14.0.1 + version: 14.0.3 '@polkadot/util-crypto': specifier: '*' - version: 14.0.1(@polkadot/util@14.0.1) + version: 14.0.3(@polkadot/util@14.0.3) '@zombienet/orchestrator': specifier: 0.0.105 - version: 0.0.105(@polkadot/util@14.0.1)(@types/node@25.3.5)(chokidar@3.6.0) + version: 0.0.105(@polkadot/util@14.0.3)(@types/node@25.9.2)(chokidar@3.6.0)(supports-color@8.1.1) ethereum-cryptography: specifier: 3.1.0 version: 3.1.0 @@ -48,27 +57,27 @@ importers: 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) + specifier: ^1.22.0 + version: 1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(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)) + version: 1.2.3(debug@4.3.7(supports-color@8.1.1))(supports-color@8.1.1)(ts-node@10.9.2(@types/node@25.9.2)(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) + version: 5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api-derive@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/rpc-provider@16.5.6)(@polkadot/types-codec@16.5.6)(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@types/node@25.9.2)(chokidar@3.6.0)(debug@4.3.7(supports-color@8.1.1))(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(supports-color@8.1.1)(ts-node@10.9.2(@types/node@25.9.2)(typescript@5.8.3))(tsx@4.22.4)(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) + version: 5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api-derive@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/rpc-provider@16.5.6)(@polkadot/types-codec@16.5.6)(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@types/node@25.9.2)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(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))) + version: 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) '@types/debug': specifier: 4.1.12 version: 4.1.12 @@ -77,7 +86,7 @@ importers: version: 1.0.4 '@types/node': specifier: '*' - version: 25.3.5 + version: 25.9.2 '@types/ps-node': specifier: 0.1.3 version: 0.1.3 @@ -85,11 +94,11 @@ importers: specifier: ^17.0.33 version: 17.0.35 '@vitest/ui': - specifier: 3.1.3 - version: 3.1.3(vitest@3.2.4) + specifier: 3.2.4 + version: 3.2.4(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) + version: 0.0.28(@types/node@25.9.2)(chokidar@3.6.0)(supports-color@8.1.1)(typescript@5.8.3) bottleneck: specifier: 2.19.5 version: 2.19.5 @@ -110,13 +119,13 @@ importers: version: 10.32.1 solc: specifier: 0.8.21 - version: 0.8.21(debug@4.3.7) + version: 0.8.21(debug@4.3.7(supports-color@8.1.1)) toml: - specifier: ^3.0.0 + specifier: 3.0.0 version: 3.0.0 tsx: specifier: '*' - version: 4.21.0 + version: 4.22.4 typescript: specifier: 5.8.3 version: 5.8.3 @@ -125,7 +134,7 @@ importers: 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) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.9.2)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.22.4)(yaml@2.9.0) web3: specifier: 4.15.0 version: 4.15.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) @@ -138,7 +147,7 @@ importers: 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)) + version: file:.papi/descriptors(polkadot-api@1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2)) packages: @@ -146,31 +155,31 @@ packages: 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==} + '@acala-network/chopsticks-core@1.4.2': + resolution: {integrity: sha512-pFqTC21htLrv8xrmE5ue+PiNnyJOSAk4IWXdQjjT0FkM32lyfK7LrWvvjVDGRQZ/sAdrN7acZTDOJExidSXQQA==} 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==} + '@acala-network/chopsticks-db@1.4.2': + resolution: {integrity: sha512-TpoLP0nfUjf0Fx/3iV42ixJ8FbDYEaJUx+VMgTkRD6BN63/nPToKkAkCUURpyXHPP+jjgBkNgI5kAw5SuhSUWw==} 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-executor@1.4.2': + resolution: {integrity: sha512-hG+hjK9x1pIxCWL9CG5dSvsbz7gEsR4/ayW+P4ldGw0uFckQhzp+Gak57FYuka/PjQx09l4blv1BWDS/Lxq9fg==} '@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==} + '@acala-network/chopsticks@1.4.2': + resolution: {integrity: sha512-vPEZNEBsyKzXPvDy7YIoNEGdvJRVc7Wkseg+47MsXnKYjUmoBqxh4pVIRGRQhl8KrRkKy0Nk27hSzCCwlCXKfg==} engines: {node: '>=v22'} hasBin: true @@ -255,12 +264,12 @@ packages: resolution: {integrity: sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A==} engines: {node: '>= 10'} - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} '@balena/dockerignore@1.0.2': @@ -433,162 +442,479 @@ packages: '@effect/rpc': ^0.72.2 effect: ^3.19.10 - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + '@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.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.28.0': + resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + '@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.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.28.0': + resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + '@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.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.28.0': + resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + '@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.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.28.0': + resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + '@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.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.28.0': + resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.0': + resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + '@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.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.28.0': + resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.0': + resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.28.0': + resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.28.0': + resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + '@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.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.28.0': + resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + '@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.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.28.0': + resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + '@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.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.28.0': + resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + '@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.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.28.0': + resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.0': + resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + '@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.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.28.0': + resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + '@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.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.28.0': + resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + '@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.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.28.0': + resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.0': + resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.28.0': + resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.0': + resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.28.0': + resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.28.0': + resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + '@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.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.28.0': + resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.28.0': + resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.28.0': + resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@ethereumjs/rlp@10.1.2': + resolution: {integrity: sha512-T5Zt6C2pd02Wd88Q9A5/UX+He1Q2Y1LntHxz/038tfbUMiqby4fYSSTLEDx+TEfJqw1BsJSBY/TSu6goUzlk+w==} + engines: {node: '>=20'} + hasBin: true + '@ethereumjs/rlp@4.0.1': resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} engines: {node: '>=14'} @@ -602,8 +928,8 @@ packages: '@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==} + '@grpc/grpc-js@1.14.4': + resolution: {integrity: sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==} engines: {node: '>=12.10.0'} '@grpc/proto-loader@0.7.15': @@ -611,8 +937,8 @@ packages: engines: {node: '>=6'} hasBin: true - '@grpc/proto-loader@0.8.0': - resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + '@grpc/proto-loader@0.8.1': + resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==} engines: {node: '>=6'} hasBin: true @@ -620,9 +946,9 @@ packages: 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/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} '@inquirer/checkbox@4.3.2': resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} @@ -633,9 +959,9 @@ packages: '@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'} + '@inquirer/checkbox@5.2.1': + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -651,9 +977,9 @@ packages: '@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'} + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -669,9 +995,9 @@ packages: '@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'} + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -687,9 +1013,9 @@ packages: '@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'} + '@inquirer/editor@5.2.2': + resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -705,9 +1031,9 @@ packages: '@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'} + '@inquirer/expand@5.1.1': + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -723,9 +1049,9 @@ packages: '@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'} + '@inquirer/external-editor@3.0.3': + resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -736,9 +1062,9 @@ packages: 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/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} '@inquirer/input@4.3.1': resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} @@ -749,9 +1075,9 @@ packages: '@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'} + '@inquirer/input@5.1.2': + resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -767,9 +1093,9 @@ packages: '@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'} + '@inquirer/number@4.1.1': + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -785,9 +1111,9 @@ packages: '@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'} + '@inquirer/password@5.1.1': + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -803,9 +1129,9 @@ packages: '@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'} + '@inquirer/prompts@8.5.2': + resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -821,9 +1147,9 @@ packages: '@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'} + '@inquirer/rawlist@5.3.1': + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -839,9 +1165,9 @@ packages: '@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'} + '@inquirer/search@4.2.1': + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -857,9 +1183,9 @@ packages: '@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'} + '@inquirer/select@5.2.1': + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -875,9 +1201,9 @@ packages: '@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'} + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -896,6 +1222,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -953,33 +1283,33 @@ packages: '@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==} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': + resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} cpu: [arm64] os: [darwin] - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': + resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==} cpu: [x64] os: [darwin] - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': + resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==} cpu: [arm64] os: [linux] - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': + resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==} cpu: [arm] os: [linux] - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': + resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==} cpu: [x64] os: [linux] - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': + resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==} cpu: [x64] os: [win32] @@ -991,8 +1321,8 @@ packages: resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} - '@noble/ciphers@2.1.1': - resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} + '@noble/ciphers@2.2.0': + resolution: {integrity: sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==} engines: {node: '>= 20.19.0'} '@noble/curves@1.2.0': @@ -1032,8 +1362,8 @@ packages: 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==} + '@noble/hashes@2.2.0': + resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} engines: {node: '>= 20.19.0'} '@noble/secp256k1@1.7.2': @@ -1088,8 +1418,8 @@ packages: resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} engines: {node: '>= 20'} - '@octokit/request@10.0.8': - resolution: {integrity: sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==} + '@octokit/request@10.0.10': + resolution: {integrity: sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w==} engines: {node: '>= 20'} '@octokit/rest@22.0.1': @@ -1213,23 +1543,42 @@ packages: resolution: {integrity: sha512-eC/wUxjaN8miAmwSwJ/XIZ1zG+4leB2fs6h0fcZrbVI9SJXwuoWGTCMtErq+fbgRlDoK3cxEUO16JBKhLkCWXw==} hasBin: true + '@polkadot-api/cli@0.18.1': + resolution: {integrity: sha512-jPa8WSNPZWdy372sBAUnm0nU1XX5mLbmgkOOU39+zpYPSE12mYXyM3r7JuT5IHdAccEJr6qK2DplPFTeNSyq9A==} + hasBin: true + '@polkadot-api/codegen@0.19.1': resolution: {integrity: sha512-129a0vHChzKuvQDELMYPpmqZtA5VFlJ7vo5HZh47bo67qYi1veRgDrNQVGM8yaHzi7Coo481b/SDruZbbbgd3Q==} + '@polkadot-api/codegen@0.21.2': + resolution: {integrity: sha512-e1Of2TfB13YndPQ71WrtOIPfRrSlkG6wGprP8/VHC484kkt2JPDOY+io3NdPWkafDblDQ47aG0368sxT+4RSZA==} + + '@polkadot-api/common-sdk-utils@0.1.0': + resolution: {integrity: sha512-cgA9fh8dfBai9b46XaaQmj9vwzyHStQjc/xrAvQksgF6SqvZ0yAfxVqLvGrsz/Xi3dsAdKLg09PybC7MUAMv9w==} + peerDependencies: + polkadot-api: ^1.8.1 + rxjs: '>=7.8.1' + '@polkadot-api/descriptors@file:.papi/descriptors': resolution: {directory: .papi/descriptors, type: directory} peerDependencies: - polkadot-api: '>=1.11.2' + polkadot-api: '>=2.0.0' '@polkadot-api/ink-contracts@0.4.0': resolution: {integrity: sha512-e2u5KhuYoiM+PyHsvjkI0O1nmFuC0rLH64uBerMqwK7hWENdM/ej9OqKawIzp6NQuYSHF5P4U8NBT0mjP9Y1yQ==} + '@polkadot-api/ink-contracts@0.4.6': + resolution: {integrity: sha512-wpFPa8CnGnmq+cFYMzuTEDmtt3ElBM0UWgTz4RpmI9E7knZ1ctWBhO7amXxOWcILqIG6sqWIE95x0cfF1PRcQg==} + '@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-proxy@0.2.8': + resolution: {integrity: sha512-AC5KK4p2IamAQuqR0S3YaiiUDRB2r1pWNrdF0Mntm5XGYEmeiAILBmnFa7gyWwemhkTWPYrK5HCurlGfw2EsDA==} + '@polkadot-api/json-rpc-provider@0.0.1': resolution: {integrity: sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==} @@ -1239,11 +1588,19 @@ packages: '@polkadot-api/known-chains@0.9.11': resolution: {integrity: sha512-ZbKXjPNI56DieJrM3DwuzNkjgLIGLjmXt5280cYJksGfatJkS/fZXIsAz0gBvs3UDeghd4co5a/OEEPiI5X8YQ==} + '@polkadot-api/known-chains@0.9.18': + resolution: {integrity: sha512-zdU4FA01lXcpNXUiFgSmFKIwDKbTw15KT4U6Zlqo6FPUMZgncVEbbS4dSgVrf+TGw9SDOUjGlEdyTHAiOAG5Tw==} + '@polkadot-api/legacy-provider@0.3.2': resolution: {integrity: sha512-/aM4jKNED5ONhOg1WnUzfSM9cJ17FHpZvASWLUGNbC2Y6CZKmLQ9UFm9fZnIbpMmC01Mz3L5orE+IlCo6g54Ag==} peerDependencies: rxjs: '>=7.8.0' + '@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==} @@ -1253,23 +1610,37 @@ packages: '@polkadot-api/merkleize-metadata@1.1.29': resolution: {integrity: sha512-z8ivYDdr4xlh50MQ7hLaSVw4VM6EV7gGgd+v/ej09nue0W08NG77zf7pXWeRKgOXe3+hPOSQQRSZT2OlIYRfqA==} + '@polkadot-api/merkleize-metadata@1.2.3': + resolution: {integrity: sha512-WkPbz0p2XQ9c8yXagdnwCHEB70Gnm91okcsd6IXU393//3aPgkxKgb+/Efnz7C5/KQmg02P0zXo7q/n/W/yVCA==} + '@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.14.3': + resolution: {integrity: sha512-m7CACsiqHzgVEh5WBZGkTV8AQ3CBQKR1YpPQMnlsJfCr/IkgKU0UyWM6WxCmBiReLFVkOfXMtGlpN8+GxpHmww==} + '@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/metadata-compatibility@0.4.4': + resolution: {integrity: sha512-V4ye5d2ns32YC45Fdc/IF9Y7CgM8inzJbmHQ2DCPSNd6omTRLJd81gU9zU88QAqPAcH2gKGnS5UF+wLL2VagSQ==} + '@polkadot-api/observable-client@0.15.1': resolution: {integrity: sha512-iR0ALA2C1aMzXqxqZqksLuScaImXbSWyaVs9Ym9Jz9SCeh2FSP6yK43BLW+RZOfcS84POxuGAktTXFssYM6fkg==} peerDependencies: rxjs: '>=7.8.0' + '@polkadot-api/observable-client@0.17.3': + resolution: {integrity: sha512-SJhbMKBIzxNgUUy7ZWflYf/TX9soMqiR2WYyggA7U3DLhgdx4wzFjOSbxCk8RuX9Kf/AmJE4dfleu9HBSCZv6g==} + peerDependencies: + rxjs: '>=7.8.0' + '@polkadot-api/observable-client@0.3.2': resolution: {integrity: sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==} peerDependencies: @@ -1279,35 +1650,68 @@ packages: '@polkadot-api/pjs-signer@0.6.15': resolution: {integrity: sha512-JsrsuV5aa8Ghnkle+ZiR15xB/xqW9PFNsP3jFsG/n0DlfbKI+mSfrBZ6v3gkpccQIHtRnOA4yB1qRijjIEp2WQ==} + '@polkadot-api/pjs-signer@0.6.19': + resolution: {integrity: sha512-jTHKoanZg9ewupthOczWNb2pici+GK+TBQmp9MwhwGs/3uMD2144aA8VNNBEi8rMxOBZlvKYfGkgjiTEGbBwuQ==} + '@polkadot-api/polkadot-sdk-compat@2.3.3': resolution: {integrity: sha512-p30po+iv4trniSJ7UZiIt/rFInvtA9Tzg65EzuRkCaQAnh54a3MPp9w/q+x+SNLEcfzVLvf8LyPnMPOIpKuj5w==} + '@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/sdk-ink@0.5.1': + resolution: {integrity: sha512-9pRnghjigivvgq7375hzkoazstvPDbc0YB01Jzw1/MYKcX+YJn1p/H8SAQTWbKlz2ohFgi1nwU52a0bsmKqb/Q==} + peerDependencies: + '@polkadot-api/ink-contracts': '>=0.4.0' + polkadot-api: '>=1.19.0' + rxjs: '>=7.8.0' + + '@polkadot-api/signer@0.2.13': + resolution: {integrity: sha512-XBOtjFsRGETVm/aXeZnsvFcJ1qvtZhRtwUMmpCOBt9s8PWfILaQH/ecOegzda3utNIZGmXXaOoJ5w9Hc/6I3ww==} + '@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/signers-common@0.1.20': + resolution: {integrity: sha512-v1mrTdRjQOV17riZ8172OsOQ/RJbv1QsEpjwnvxzvdCnjuNpYwtYHZaE+cSdDBb4n1p73XIBMvB/uAK/QFC2JA==} + '@polkadot-api/sm-provider@0.1.11': resolution: {integrity: sha512-XSli7BF3Xpyh0sdu1MNRJ1qyT3Werd5Z+tQa4iXR+nzo5Kpvg67paG9A8z1K7vNF83pRw4rvnXE9G5HbC+tPvA==} peerDependencies: '@polkadot-api/smoldot': '>=0.3' + '@polkadot-api/sm-provider@0.1.16': + resolution: {integrity: sha512-3LEDU7nkgtDx1A6ATHLLm3+nFAY6cdkNA9tGltfDzW0efACrhhfDjNqJdI1qLNY0wDyT1aGdoWr5r+4CckRpXA==} + peerDependencies: + '@polkadot-api/smoldot': '>=0.3' + '@polkadot-api/smoldot@0.3.14': resolution: {integrity: sha512-eWqO0xFQaKzqY5mRYxYuZcj1IiaLcQP+J38UQyuJgEorm+9yHVEQ/XBWoM83P+Y8TwE5IWTICp1LCVeiFQTGPQ==} + '@polkadot-api/smoldot@0.3.15': + resolution: {integrity: sha512-YyV+ytP8FcmKEgLRV7uXepJ5Y6md/7u2F8HKxmkWytmnGXO1z+umg2pHbOxLGifD9V2NhkPY+awpzErtVIzqAA==} + '@polkadot-api/substrate-bindings@0.16.3': resolution: {integrity: sha512-KN/nghI3SM0t7WsULwLRB3s4DnWogGCi5TuvXB0yPkkiB5GJugMPuHTTUxDkWmjZ0vLUFlmkaZ/sfFf0tvo8xQ==} + '@polkadot-api/substrate-bindings@0.16.6': + resolution: {integrity: sha512-cATY7HWU5hWd09C1MUEddechq7JT7QAciKL2/N/1wv5rxGcAFyAD9ZtaKBXVI4Aui9RSeGh8KvHdgKFLoozMyQ==} + '@polkadot-api/substrate-bindings@0.17.0': resolution: {integrity: sha512-YdbkvG/27N5A94AiKE4soVjDy0Nw74Nn+KD29mUnFmIZvL3fsN/DTYkxvMDVsOuanFXyAIXmzDMoi7iky0fyIw==} + '@polkadot-api/substrate-bindings@0.20.3': + resolution: {integrity: sha512-9iqC71fx1ee9ld1NZV8PFime5vryi0kt1bKCSlvNgO6dqMc06sMZuZ8WPjOzWLCHiKHLuphdMs3rVBBaeCP3yg==} + '@polkadot-api/substrate-bindings@0.6.0': resolution: {integrity: sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==} @@ -1317,48 +1721,57 @@ packages: '@polkadot-api/substrate-client@0.4.7': resolution: {integrity: sha512-Mmx9VKincVqfVQmq89gzDk4DN3uKwf8CxoqYvq+EiPUZ1QmMUc7X4QMwG1MXIlYdnm5LSXzn+2Jn8ik8xMgL+w==} + '@polkadot-api/substrate-client@0.5.0': + resolution: {integrity: sha512-J+gyZONCak+n6NxADZWtldH+gatYORqEScMAgI9gGu43pHUe7/xNRCqnin0dgDIzmuL3m1ERglF8LR7YhB0nHQ==} + '@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/utils@0.4.0': + resolution: {integrity: sha512-9b/hwRM0UloLWV7SfpNaSD/4k8UQAHoaACAk7Xe+1MlfAm2JtnmPiB1GfGrfTyBlsrJVUIBCZpEmbmxVMaIqBA==} + '@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/ws-provider@0.7.5': + resolution: {integrity: sha512-2ZLEo0PAFeuOx2DUDkbex85HZMf9lgnmZ8oGB5+NaButIydkoqXy5SHYJNPc45GcZy2tvwzImMZInNMLa5GJhg==} + '@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==} + '@polkadot/api-augment@16.5.6': + resolution: {integrity: sha512-bunJF1c3nIuDtU6iwa+reTt9U47Y8iOC8Gw7PfANlZmLJmO/XVXnWc3JJLM+g9ESDn2raHJELeWBFVOXQrbtUw==} 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==} + '@polkadot/api-base@16.5.6': + resolution: {integrity: sha512-eBLIv86ZZY4t5OrobVoGC+QXbErOGlBpI2rJI5OMvTNPoVvtEoI++u+wwRScjkOZaUhXyQikd+0Uv71qr3xnsA==} 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==} + '@polkadot/api-derive@16.5.6': + resolution: {integrity: sha512-cHdvPvhYFch18uPTcuOZJ8VceOfercod2fi4xCnHJAmattzlgj9qCgnOoxdmBS9GZ403ZyRHOjBuUwZy/IsUWQ==} 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==} + '@polkadot/api@16.5.6': + resolution: {integrity: sha512-5h/X3pY8WpqGk4XTaiIUjKD6Pnk8k4bJ6EIwPKLP8/kfFWKSOenpN6ggZxANr+Qj+RgXrp4TxJVcuhXSiBh9Sg==} engines: {node: '>=18'} '@polkadot/keyring@13.5.9': @@ -1368,91 +1781,91 @@ packages: '@polkadot/util': 13.5.9 '@polkadot/util-crypto': 13.5.9 - '@polkadot/keyring@14.0.1': - resolution: {integrity: sha512-kHydQPCeTvJrMC9VQO8LPhAhTUxzxfNF1HEknhZDBPPsxP/XpkYsEy/Ln1QzJmQqD5VsgwzLDE6cExbJ2CT9CA==} + '@polkadot/keyring@14.0.3': + resolution: {integrity: sha512-ozp1dQwaHCjgX/fpTTORmHjxdUNQnyiTVJszpzUaUpvtH/IGZhSU/mSHXMqNETS/g57vQa7NatIDcWfyR9abyA==} engines: {node: '>=18'} peerDependencies: - '@polkadot/util': 14.0.1 - '@polkadot/util-crypto': 14.0.1 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3 '@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==} + '@polkadot/networks@14.0.3': + resolution: {integrity: sha512-/VqTLUDn+Wm8S2L/yaGFddo3oW4vRYav0Rg4pLg/semMZLaN8PJ6h927ucn9JyWdH82QfZfyiIPORt0ZF3isyw==} 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==} + '@polkadot/rpc-augment@16.5.6': + resolution: {integrity: sha512-vlrNvl2VtU09jZV/AvH7jBb/cNUO+dWu8Xj9pId5ctSUnZHm8o8wRk9ekyieKP57OUoKMd8+VScwMKd624SxTw==} 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==} + '@polkadot/rpc-core@16.5.6': + resolution: {integrity: sha512-l6od++WlvKH4mw5mtsIh2AhiBs3H+TtdOoUHVLCx/R9il7+gl+arltzZ8vBuffyh/O+uQ36lI8yUoD1g4gi1tA==} 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==} + '@polkadot/rpc-provider@16.5.6': + resolution: {integrity: sha512-46sHIjKYr4aSzBCfbyqtCwuP8MMJ3jOp0xx9eggOGbKyP8Z0j0Cp+1nNkZUYzehcdGjjrmCxCbQp17wc6cj4zA==} 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==} + '@polkadot/types-augment@16.5.6': + resolution: {integrity: sha512-QN5UrluUZCVgknUDW0gps/FRQ13Qgm24w53pCd2HgD0nmTtXDt9D4psjWwx5JkGTkUAvpzFWwN41bkxAeCiV6g==} 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==} + '@polkadot/types-codec@16.5.6': + resolution: {integrity: sha512-3tzUv1LZOL97IlQmko4dqbfRC0cg9IQ2QAHRVoDIWsXrVovp1V3kPdP0o6e3I8T2XB9IlbabK91v+ZiIxhGMZw==} 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==} + '@polkadot/types-create@16.5.6': + resolution: {integrity: sha512-g7g3hrjpz4KgqQqei9PU0JY9fsFHBmThWALZk5pWB32vyDyDcXZiyhH3agDhqfmzQiolTW2FuvcNJxgS634J1w==} 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==} + '@polkadot/types-known@16.5.6': + resolution: {integrity: sha512-c78NcVO3LIvi4xzxB39WewE+80I4jOYUtPBaB4AzSMespEwIr92VTeX3KzFWuutxDXLSPqeVfXhaAhBB0NssiQ==} 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==} + '@polkadot/types-support@16.5.6': + resolution: {integrity: sha512-Hqpa/hCvXZXUTUiJMAE55UXpzAeCVLaFlzzXQXLkne0vhmv3/JkWcBnX755a/b9+C4b3MKEz2i0tSKLsa3DldA==} 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==} + '@polkadot/types@16.5.6': + resolution: {integrity: sha512-X/sfMHJS4RkRhnsc4CQqzUy7BM/s2y71TrBFHPYAjs2q/rbZ/BwvBk70SrUiSa0+iRRn3RewbBZm+AB8CbkdKw==} engines: {node: '>=18'} '@polkadot/util-crypto@13.5.9': @@ -1461,18 +1874,18 @@ packages: peerDependencies: '@polkadot/util': 13.5.9 - '@polkadot/util-crypto@14.0.1': - resolution: {integrity: sha512-Cu7AKUzBTsUkbOtyuNzXcTpDjR9QW0fVR56o3gBmzfUCmvO1vlsuGzmmPzqpHymQQ3rrfqV78CPs62EGhw0R+A==} + '@polkadot/util-crypto@14.0.3': + resolution: {integrity: sha512-V00BI6XnZLCkrAmV8uN0eSB6fy48CkxdDZT29cgSMSwHPtY6oKUNgd1ST07PGCL5x8XflwjoA7CTlhdbp1Y9gw==} engines: {node: '>=18'} peerDependencies: - '@polkadot/util': 14.0.1 + '@polkadot/util': 14.0.3 '@polkadot/util@13.5.9': resolution: {integrity: sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw==} engines: {node: '>=18'} - '@polkadot/util@14.0.1': - resolution: {integrity: sha512-764HhxkPV3x5rM0/p6QdynC2dw26n+SaE+jisjx556ViCd4E28Ke4xSPef6C0Spy4aoXf2gt0PuLEcBvd6fVZg==} + '@polkadot/util@14.0.3': + resolution: {integrity: sha512-mg1NR7ixHlNiz2zbvdcdy1OXZmca2tVA4DpewGpY/qFkW/gq9HdDrHLu7g0k90QnunDcFW4emb7NB60sGJQ0bw==} engines: {node: '>=18'} '@polkadot/wasm-bridge@7.5.4': @@ -1518,24 +1931,24 @@ packages: resolution: {integrity: sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g==} engines: {node: '>=18'} - '@polkadot/x-bigint@14.0.1': - resolution: {integrity: sha512-gfozjGnebr2rqURs31KtaWumbW4rRZpbiluhlmai6luCNrf5u8pB+oLA35kPEntrsLk9PnIG9OsC/n4hEtx4OQ==} + '@polkadot/x-bigint@14.0.3': + resolution: {integrity: sha512-U0al6BKgldFrEbmSObRAlzv9VDs5SMa/rbvZKvvkVec0sWTzYPWQZU1ZC/biXLYdjdKML89BeuCKmXZtCcGhUQ==} 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==} + '@polkadot/x-fetch@14.0.3': + resolution: {integrity: sha512-695c5aPBPtYcnn2zM+u0mXgyNHINlO0qGlGcJq3/0t5NVRZv5KZhk7NNm6antOay9uUjGG40F/r+LPzDT3QamA==} 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==} + '@polkadot/x-global@14.0.3': + resolution: {integrity: sha512-MzMEynJ7HMTy/plLmdyP8rv14RS/6s29HZodUG9aCOscBnEiEDxVEax/ztRJqxhhQuHeYdx0LYDwVbdQDTkqNw==} engines: {node: '>=18'} '@polkadot/x-randomvalues@13.5.9': @@ -1545,35 +1958,35 @@ packages: '@polkadot/util': 13.5.9 '@polkadot/wasm-util': '*' - '@polkadot/x-randomvalues@14.0.1': - resolution: {integrity: sha512-/XkQcvshzJLHITuPrN3zmQKuFIPdKWoaiHhhVLD6rQWV60lTXA3ajw3ocju8ZN7xRxnweMS9Ce0kMPYa0NhRMg==} + '@polkadot/x-randomvalues@14.0.3': + resolution: {integrity: sha512-qTPcrk0nIHL2tIu5e0cLj3puQvjCK7onehnqO2fvlmWeIlvDel66fwWs06Ipsib+CwLJdmE6WgNy+8Jv74r6YA==} engines: {node: '>=18'} peerDependencies: - '@polkadot/util': 14.0.1 + '@polkadot/util': 14.0.3 '@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==} + '@polkadot/x-textdecoder@14.0.3': + resolution: {integrity: sha512-4RJYDG00iUzQ7YAuS/yvkWRZlkjYU8PUNdJHRfqtJ+SjrSPB7LYYxFhLgw43TZUtHmIueNTsml2Ukv3xXTr2kA==} 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==} + '@polkadot/x-textencoder@14.0.3': + resolution: {integrity: sha512-9HH6o2L+r99wEfXhPb5g+Xwn7qouqD32PsMux7B0dFGR2KNqP4KwO19Hu+gdij6wsEhy7delhZwzHenrWwDfhQ==} 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==} + '@polkadot/x-ws@14.0.3': + resolution: {integrity: sha512-tOPdkMye3iuXnuFtdNg5+iSu7Cz9LRL8z5psMuZpUpThMYChGsS2pDFtNvXOKU8ohhO+frY9VdJ9VBg1WL9Iug==} engines: {node: '>=18'} '@protobufjs/aspromise@1.1.2': @@ -1582,20 +1995,20 @@ packages: '@protobufjs/base64@1.1.2': resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - '@protobufjs/codegen@2.0.4': - resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} - '@protobufjs/eventemitter@1.1.0': - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} - '@protobufjs/fetch@1.1.0': - resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - '@protobufjs/inquire@1.1.0': - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + '@protobufjs/inquire@1.1.2': + resolution: {integrity: sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==} '@protobufjs/path@1.1.2': resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} @@ -1603,144 +2016,144 @@ packages: '@protobufjs/pool@1.1.0': resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - '@protobufjs/utf8@1.1.0': - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@protobufjs/utf8@1.1.1': + resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@rollup/rollup-android-arm-eabi@4.61.1': + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@rollup/rollup-android-arm64@4.61.1': + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@rollup/rollup-darwin-arm64@4.61.1': + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@rollup/rollup-darwin-x64@4.61.1': + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + '@rollup/rollup-freebsd-arm64@4.61.1': + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@rollup/rollup-freebsd-x64@4.61.1': + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@rollup/rollup-linux-arm-musleabihf@4.61.1': + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@rollup/rollup-linux-arm64-gnu@4.61.1': + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@rollup/rollup-linux-arm64-musl@4.61.1': + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + '@rollup/rollup-linux-loong64-gnu@4.61.1': + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + '@rollup/rollup-linux-loong64-musl@4.61.1': + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@rollup/rollup-linux-ppc64-gnu@4.61.1': + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + '@rollup/rollup-linux-ppc64-musl@4.61.1': + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + '@rollup/rollup-linux-riscv64-gnu@4.61.1': + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + '@rollup/rollup-linux-riscv64-musl@4.61.1': + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@rollup/rollup-linux-s390x-gnu@4.61.1': + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@rollup/rollup-linux-x64-gnu@4.61.1': + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@rollup/rollup-linux-x64-musl@4.61.1': + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + '@rollup/rollup-openbsd-x64@4.61.1': + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@rollup/rollup-openharmony-arm64@4.61.1': + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@rollup/rollup-win32-arm64-msvc@4.61.1': + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + '@rollup/rollup-win32-ia32-msvc@4.61.1': + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + '@rollup/rollup-win32-x64-gnu@4.61.1': + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + '@rollup/rollup-win32-x64-msvc@4.61.1': + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} cpu: [x64] os: [win32] @@ -1755,8 +2168,8 @@ packages: '@scure/base@1.2.6': resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} - '@scure/base@2.0.0': - resolution: {integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==} + '@scure/base@2.2.0': + resolution: {integrity: sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg==} '@scure/bip32@1.4.0': resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} @@ -1838,8 +2251,8 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/json-bigint@1.0.4': resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} @@ -1853,11 +2266,11 @@ packages: '@types/node@22.7.5': resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} - '@types/node@24.12.0': - resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} + '@types/node@24.13.1': + resolution: {integrity: sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==} - '@types/node@25.3.5': - resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==} + '@types/node@25.9.2': + resolution: {integrity: sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1897,12 +2310,12 @@ packages: 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/pretty-format@3.2.6': + resolution: {integrity: sha512-lb7XXXzmm2h2ASzFnRvQpDo6onT1NmMJA3tkGTWiBFtRJ9lxGY3d3mm/Apt36gej2bkkOVLL/yTOtufDaFa/jA==} + '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} @@ -1912,22 +2325,22 @@ packages: '@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/ui@3.2.6': + resolution: {integrity: sha512-mATfG3zVdhobE9U1rIpvtYD3DGuSSxqZ3Aj/8ityGqKXy8YDJ9BoAjZmAz6dZ1IZ1xI5V+MerkCczvVa+3QK9Q==} + peerDependencies: + vitest: 3.2.6 '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@3.2.6': + resolution: {integrity: sha512-lI23nIs4bnT3T8NIoh+vFaz5s2/DdP0Jgt2jxwgWljvwn82cLJtyi/If+fjFyoLMGIOz0U/fKvWE0d4jsNQEfg==} + '@zombienet/orchestrator@0.0.105': resolution: {integrity: sha512-vw+Pt1N9oChdA+2WHgwygG4wpXaKnPJPIRbm3OWbhscCwHbWlmcVVZhZN3khC4+WMo+kvFt3XhzV6hZrZI5Bug==} engines: {node: '>=18'} @@ -1954,6 +2367,10 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + abbrev@4.0.0: + resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} + engines: {node: ^20.17.0 || >=22.9.0} + abitype@0.7.1: resolution: {integrity: sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==} peerDependencies: @@ -1974,6 +2391,17 @@ packages: zod: optional: true + abitype@1.2.4: + resolution: {integrity: sha512-dpKH+N27vRjarMVTFFkeY445VTKftzGWpL0FiT7xmVmzQRKazZexzC5uHG0f6XKsVLAuUlndnbGau6lRejClxg==} + 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'} @@ -2026,8 +2454,8 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - ansis@4.2.0: - resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} engines: {node: '>=14'} any-promise@1.3.0: @@ -2086,8 +2514,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.13.6: - resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} + axios@1.17.0: + resolution: {integrity: sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -2127,11 +2555,11 @@ packages: 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@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -2168,8 +2596,8 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} engines: {node: '>= 0.4'} call-bound@1.0.4: @@ -2230,6 +2658,10 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + class-is@1.1.0: resolution: {integrity: sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==} @@ -2350,6 +2782,10 @@ packages: console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + convert-to-spaces@2.0.1: resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2395,8 +2831,8 @@ packages: dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} @@ -2492,8 +2928,8 @@ packages: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} - docker-modem@5.0.6: - resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} + docker-modem@5.0.7: + resolution: {integrity: sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==} engines: {node: '>= 8.0'} dockerode@4.0.9: @@ -2515,8 +2951,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - effect@3.19.19: - resolution: {integrity: sha512-Yc8U/SVXo2dHnaP7zNBlAo83h/nzSJpi7vph6Hzyl4ulgMBIgPmz3UzOjb9sBgpFE00gC0iETR244sfXDNLHRg==} + effect@3.21.3: + resolution: {integrity: sha512-RqwU7WnJ6CqYhyjpOVJA5vh1Sgkn6eVECO6mnD0EjlbWcC2M3LJaPglXXr13Rdo/Y+B+wTEPzGRYFNL2xKxNeQ==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2562,22 +2998,32 @@ packages: 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==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} 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==} + es-toolkit@1.47.0: + resolution: {integrity: sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw==} es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.28.0: + resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} engines: {node: '>=18'} hasBin: true @@ -2633,15 +3079,15 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + 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-copy@4.0.3: + resolution: {integrity: sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw==} fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} @@ -2652,8 +3098,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-wrap-ansi@0.2.0: - resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -2668,8 +3114,8 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} - fflate@0.8.2: - resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} @@ -2696,11 +3142,11 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true - flatted@3.3.4: - resolution: {integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -2727,8 +3173,8 @@ packages: 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==} + fs-extra@11.3.5: + resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} engines: {node: '>=14.14'} fs-minipass@2.1.0: @@ -2762,8 +3208,8 @@ packages: 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==} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} get-func-name@2.0.2: @@ -2785,9 +3231,6 @@ packages: 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==} @@ -2845,8 +3288,8 @@ packages: has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} he@1.2.0: @@ -2860,6 +3303,10 @@ packages: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} + hosted-git-info@9.0.3: + resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} + engines: {node: ^20.17.0 || >=22.9.0} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -2950,13 +3397,13 @@ packages: react-devtools-core: optional: true - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} engines: {node: '>= 12'} - is-accessor-descriptor@1.0.1: - resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} - engines: {node: '>= 0.10'} + is-accessor-descriptor@1.0.2: + resolution: {integrity: sha512-AIbwAcazqP3R65dGvqk1V+a+vE5Fg1yu/ZKMOiBWSUIXXiwQkYmXQcVa2O0nh0tSDKDFKxG2mY7dB1Sr4hEP1g==} + engines: {node: '>= 0.4'} is-arguments@1.2.0: resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} @@ -2977,8 +3424,8 @@ packages: resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} engines: {node: '>= 0.4'} - is-descriptor@1.0.3: - resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} + is-descriptor@1.0.4: + resolution: {integrity: sha512-bv5z95W0dDtLfKwDfkTNxaRxmISBD3eQBKJeVxv2AQ7MjuUnDNG7cIQqvFtMOUYhsILWHhMayWdoGqNqYYYjww==} engines: {node: '>= 0.4'} is-extglob@2.1.1: @@ -3062,6 +3509,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@4.0.0: + resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} + engines: {node: '>=20'} + iso-random-stream@2.0.2: resolution: {integrity: sha512-yJvs+Nnelic1L2vH2JzWvvPQFA4r7kSTnpST/+LkAQjSz0hos2oqLD+qIVi9Qk38Hoe7mNDt3j0S27R58MVjLQ==} engines: {node: '>=10'} @@ -3092,8 +3543,8 @@ packages: js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true jsdom@23.2.0: @@ -3111,8 +3562,8 @@ packages: json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json-with-bigint@3.5.7: - resolution: {integrity: sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==} + json-with-bigint@3.5.8: + resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -3121,15 +3572,14 @@ packages: resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} engines: {node: '>=8.17.0'} hasBin: true - bundledDependencies: [] - jsondiffpatch@0.7.3: - resolution: {integrity: sha512-zd4dqFiXSYyant2WgSXAZ9+yYqilNVvragVNkNRn2IFZKgjyULNrKRznqN4Zon0MkLueCg+3QaPVCnDAVP20OQ==} + jsondiffpatch@0.7.6: + resolution: {integrity: sha512-zE9+AXFq+MkTolDor2Cw1nJzLC0aleqPkYf52Kb4Kn4mJcka/gFHpGI2JBVEJCfWOvBl0OoxZS+wuLdislQcqg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} @@ -3164,8 +3614,11 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -3190,8 +3643,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} engines: {node: 20 || >=22} lru-cache@6.0.0: @@ -3277,8 +3730,8 @@ packages: resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} engines: {node: '>=8'} - minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + minipass-flush@1.0.7: + resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} engines: {node: '>= 8'} minipass-pipeline@1.2.4: @@ -3305,6 +3758,10 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} @@ -3317,8 +3774,8 @@ packages: resolution: {integrity: sha512-I2bcB5d6jtkdan6MLGOxObpUbidqv0ej+PhbCGnXUqmcGYZ6X8F0qBpU6HE4mvYc81NSznBrVDp+Uc808Ba2RA==} engines: {node: '>=16.0.0'} - mlly@1.8.1: - resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==} + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} mocha@10.8.2: resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} @@ -3336,12 +3793,12 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msgpackr-extract@3.0.3: - resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + msgpackr-extract@3.0.4: + resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} hasBin: true - msgpackr@1.11.8: - resolution: {integrity: sha512-bC4UGzHhVvgDNS7kn9tV8fAucIYUBuGojcaLiz7v+P63Lmtm0Xeji8B/8tYKddALXxJLpwIeBmUN3u64C4YkRA==} + msgpackr@1.11.14: + resolution: {integrity: sha512-suPZQcjFtPGp0cksn70ICfLuxsO9F2/sRrbJzeNepojZ+OPwGzA0lNdLyU4SJUKAd5ZgvUWWPojzzdlVuOYcrQ==} multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} @@ -3360,11 +3817,11 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nan@2.25.0: - resolution: {integrity: sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==} + nan@2.27.0: + resolution: {integrity: sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -3408,13 +3865,17 @@ packages: resolution: {integrity: sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==} engines: {node: '>= 10.13'} - node-abi@3.87.0: - resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==} + node-abi@3.92.0: + resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} engines: {node: '>=10'} node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-addon-api@8.8.0: + resolution: {integrity: sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA==} + engines: {node: ^18 || ^20 || >= 21} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -3433,14 +3894,19 @@ packages: 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==} + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} engines: {node: '>= 6.13.0'} node-gyp-build-optional-packages@5.2.2: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true + node-gyp@12.4.0: + resolution: {integrity: sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + node-gyp@8.4.1: resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} engines: {node: '>= 10.12.0'} @@ -3451,10 +3917,19 @@ packages: engines: {node: '>=6'} hasBin: true + nopt@9.0.0: + resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} + engines: {node: ^20.17.0 || >=22.9.0} + 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} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3505,8 +3980,8 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - ora@9.3.0: - resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} + ora@9.4.0: + resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} engines: {node: '>=20'} os-tmpdir@1.0.2: @@ -3588,12 +4063,12 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pino-abstract-transport@2.0.0: @@ -3635,6 +4110,12 @@ packages: peerDependencies: rxjs: '>=7.8.0' + polkadot-api@1.23.3: + resolution: {integrity: sha512-wOWli6Cfk3bO1u/W8qmwriCIKxATkNea8Jyg1jj7GzAqafxy295BYPzYHy2mJZCQ0PAVFPR4/JvCXocTLBsp5A==} + hasBin: true + peerDependencies: + rxjs: '>=7.8.0' + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3657,8 +4138,8 @@ packages: yaml: optional: true - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} prebuild-install@7.1.3: @@ -3671,6 +4152,10 @@ packages: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} + proc-log@6.1.0: + resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} + engines: {node: ^20.17.0 || >=22.9.0} + process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} @@ -3693,16 +4178,17 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protobufjs@6.11.4: - resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} + protobufjs@6.11.6: + resolution: {integrity: sha512-k8BHqgPBOtrlougZZqF2uUk5Z7bN8f0wj+3e8M3hvtSv0NBAz4VBy5f6R5Nxq/l+i7mRFTgNZb2trxqTpHNY/A==} hasBin: true - protobufjs@7.5.4: - resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + protobufjs@7.6.2: + resolution: {integrity: sha512-N9EiLovGEQOJSPF26Ij7qUGvahfEnq0eeYZ02aigIedkmz1qZSwjnP9SBITHJuF/6MYbIW4HDN8zdYjsjqJKXQ==} engines: {node: '>=12.0.0'} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} ps-node@0.1.6: resolution: {integrity: sha512-w7QJhUTbu70hpDso0YXDRNKCPNuchV8UTUZsAv0m7Qj5g85oHOJfr9drA1EjvK4nQK/bG8P97W4L6PJ3IQLoOA==} @@ -3739,10 +4225,14 @@ packages: peerDependencies: react: ^19.2.0 - react@19.2.4: - resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} + 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'} @@ -3763,6 +4253,9 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + real-require@1.0.0: + resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} + reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -3781,9 +4274,6 @@ packages: 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} @@ -3809,8 +4299,8 @@ packages: 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==} + rollup@4.61.1: + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3857,8 +4347,8 @@ packages: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.2: + resolution: {integrity: sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==} engines: {node: '>=10'} hasBin: true @@ -3926,12 +4416,15 @@ packages: smoldot@2.0.39: resolution: {integrity: sha512-yFMSzI6nkqWFTNao99lBA/TguUFU+bR3A5UGTDd/QqqB12jqzvZnmW/No6l2rKmagt8Qx/KybMNowV/E28znhA==} + smoldot@2.0.40: + resolution: {integrity: sha512-h6XC/kKDLdZBBTI0X8y4ZxmaZ2KYVVB0+5isCQm6j26ljeNjHZUDOV+hf8VyoE23+jg00wrxNJ2IVcIAURxwtg==} + 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==} + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} solc@0.8.21: @@ -3954,6 +4447,11 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + 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==} @@ -3983,6 +4481,10 @@ packages: sqlite3@5.1.7: resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} + sqlite3@6.0.1: + resolution: {integrity: sha512-X0czUUMG2tmSqJpEQa3tCuZSHKIx8PwM53vLZzKp/o6Rpy25fiVfjdbnZ988M8+O3ZWR1ih0K255VumCb3MAnQ==} + engines: {node: '>=20.17.0'} + ssh2@1.17.0: resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} engines: {node: '>=10.16.0'} @@ -4001,8 +4503,8 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - stdin-discarder@0.3.1: - resolution: {integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==} + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} engines: {node: '>=18'} string-width@4.2.3: @@ -4017,8 +4519,8 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.0: - resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} string_decoder@1.3.0: @@ -4090,6 +4592,10 @@ packages: 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 + tar@7.5.16: + resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} + engines: {node: '>=18'} + terminal-size@4.0.1: resolution: {integrity: sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==} engines: {node: '>=18'} @@ -4101,11 +4607,11 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - thread-stream@3.1.0: - resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + thread-stream@3.2.0: + resolution: {integrity: sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==} - thread-stream@4.0.0: - resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} + thread-stream@4.2.0: + resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} engines: {node: '>=20'} through@2.3.8: @@ -4120,8 +4626,8 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinypool@1.1.1: @@ -4143,8 +4649,8 @@ packages: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} - tmp@0.2.5: - resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} engines: {node: '>=14.14'} to-buffer@1.2.2: @@ -4158,10 +4664,6 @@ packages: 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'} @@ -4173,6 +4675,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} @@ -4210,6 +4715,25 @@ packages: 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 + tsup@8.5.1: resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} engines: {node: '>=18'} @@ -4229,8 +4753,8 @@ packages: typescript: optional: true - tsx@4.21.0: - resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + tsx@4.22.4: + resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} engines: {node: '>=18.0.0'} hasBin: true @@ -4252,16 +4776,16 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - type-fest@5.4.4: - resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} + type-fest@5.7.0: + resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} 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==} + typeorm@0.3.30: + resolution: {integrity: sha512-8T35PzjefOdqc2ZR9mwLQj0pUGp6lQhMbK2EvVMwJVJWlaoHm0v/Q6dThNOZkFchD+0yMg8gwjKM28ePiLSXSQ==} engines: {node: '>=16.13.0'} hasBin: true peerDependencies: @@ -4325,8 +4849,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} uint8arrays@3.1.1: resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==} @@ -4334,14 +4858,18 @@ packages: 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==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + undici@6.26.0: + resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} + engines: {node: '>=18.17'} + + undici@7.27.2: + resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} engines: {node: '>=20.18.1'} unicorn-magic@0.1.0: @@ -4352,6 +4880,10 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + unique-filename@1.1.1: resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} @@ -4380,10 +4912,11 @@ packages: uuid@10.0.0: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true v8-compile-cache-lib@3.0.1: @@ -4413,8 +4946,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + vite@7.3.5: + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -4572,6 +5105,9 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -4592,8 +5128,11 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} which@2.0.2: @@ -4601,6 +5140,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@6.0.1: + resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -4676,8 +5220,8 @@ packages: utf-8-validate: optional: true - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -4702,11 +5246,20 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -4762,16 +5315,16 @@ 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/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/types-known': 16.5.6 '@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 + lodash: 4.18.1 + lru-cache: 11.5.1 pino: 9.14.0 pino-pretty: 13.1.3 rxjs: 7.8.2 @@ -4781,19 +5334,19 @@ snapshots: - supports-color - utf-8-validate - '@acala-network/chopsticks-core@1.2.7': + '@acala-network/chopsticks-core@1.4.2': 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) + '@acala-network/chopsticks-executor': 1.4.2 + '@polkadot/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/types-known': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) comlink: 4.4.2 eventemitter3: 5.0.4 - lodash: 4.17.23 - lru-cache: 11.2.6 + lodash: 4.18.1 + lru-cache: 11.5.1 pino: 9.14.0 pino-pretty: 13.1.3 rxjs: 7.8.2 @@ -4803,14 +5356,14 @@ snapshots: - 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))': + '@acala-network/chopsticks-db@1.2.3(supports-color@8.1.1)(ts-node@10.9.2(@types/node@25.9.2)(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)) + sqlite3: 5.1.7(supports-color@8.1.1) + typeorm: 0.3.30(sqlite3@5.1.7(supports-color@8.1.1))(ts-node@10.9.2(@types/node@25.9.2)(typescript@5.8.3)) transitivePeerDependencies: - '@google-cloud/spanner' - '@sap/hana-client' @@ -4833,20 +5386,19 @@ snapshots: - 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))': + '@acala-network/chopsticks-db@1.4.2(ts-node@10.9.2(@types/node@25.9.2)(typescript@5.8.3))': dependencies: - '@acala-network/chopsticks-core': 1.2.7 - '@polkadot/util': 14.0.1 + '@acala-network/chopsticks-core': 1.4.2 + '@polkadot/util': 14.0.3 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)) + sqlite3: 6.0.1 + typeorm: 0.3.30(sqlite3@6.0.1)(ts-node@10.9.2(@types/node@25.9.2)(typescript@5.8.3)) transitivePeerDependencies: - '@google-cloud/spanner' - '@sap/hana-client' - babel-plugin-macros - better-sqlite3 - - bluebird - bufferutil - ioredis - mongodb @@ -4868,30 +5420,30 @@ snapshots: '@polkadot/util': 13.5.9 '@polkadot/wasm-util': 7.5.4(@polkadot/util@13.5.9) - '@acala-network/chopsticks-executor@1.2.7': + '@acala-network/chopsticks-executor@1.4.2': dependencies: - '@polkadot/util': 14.0.1 - '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) - '@acala-network/chopsticks@1.2.3(debug@4.3.7)(ts-node@10.9.2(@types/node@25.3.5)(typescript@5.8.3))': + '@acala-network/chopsticks@1.2.3(debug@4.3.7(supports-color@8.1.1))(supports-color@8.1.1)(ts-node@10.9.2(@types/node@25.9.2)(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)) + '@acala-network/chopsticks-db': 1.2.3(supports-color@8.1.1)(ts-node@10.9.2(@types/node@25.9.2)(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/api': 16.5.6 + '@polkadot/api-augment': 16.5.6 + '@polkadot/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 '@polkadot/util': 13.5.9 '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) - axios: 1.13.6(debug@4.3.7) + axios: 1.17.0(debug@4.3.7(supports-color@8.1.1))(supports-color@8.1.1) comlink: 4.4.2 dotenv: 16.6.1 global-agent: 3.0.0 - js-yaml: 4.1.1 + js-yaml: 4.2.0 jsondiffpatch: 0.5.0 - lodash: 4.17.23 - ws: 8.19.0 + lodash: 4.18.1 + ws: 8.21.0 yargs: 18.0.0 zod: 3.25.76 transitivePeerDependencies: @@ -4917,26 +5469,26 @@ snapshots: - 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))': + '@acala-network/chopsticks@1.4.2(debug@4.3.7(supports-color@8.1.1))(ts-node@10.9.2(@types/node@25.9.2)(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)) + '@acala-network/chopsticks-core': 1.4.2 + '@acala-network/chopsticks-db': 1.4.2(ts-node@10.9.2(@types/node@25.9.2)(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) + '@polkadot/api': 16.5.6 + '@polkadot/api-augment': 16.5.6 + '@polkadot/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) + axios: 1.17.0(debug@4.3.7(supports-color@8.1.1))(supports-color@8.1.1) 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 + js-yaml: 4.2.0 + jsondiffpatch: 0.7.6 + lodash: 4.18.1 + ws: 8.21.0 yargs: 18.0.0 zod: 3.25.76 transitivePeerDependencies: @@ -4944,7 +5496,6 @@ snapshots: - '@sap/hana-client' - babel-plugin-macros - better-sqlite3 - - bluebird - bufferutil - debug - ioredis @@ -5026,13 +5577,13 @@ snapshots: '@ast-grep/napi-win32-ia32-msvc': 0.40.5 '@ast-grep/napi-win32-x64-msvc': 0.40.5 - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} '@balena/dockerignore@1.0.2': {} @@ -5104,155 +5655,313 @@ snapshots: '@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)': + '@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3)': 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 + '@effect/platform': 0.93.8(effect@3.21.3) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/workflow': 0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3) + effect: 3.21.3 kubernetes-types: 1.30.0 - '@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19)': + '@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3)': dependencies: - '@effect/platform': 0.93.8(effect@3.19.19) - effect: 3.19.19 - uuid: 11.1.0 + '@effect/platform': 0.93.8(effect@3.21.3) + effect: 3.21.3 + uuid: 11.1.1 - '@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/platform-node-shared@0.56.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3)': 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) + '@effect/cluster': 0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3) + '@effect/platform': 0.93.8(effect@3.21.3) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) '@parcel/watcher': 2.5.6 - effect: 3.19.19 + effect: 3.21.3 multipasta: 0.2.7 - ws: 8.19.0 + ws: 8.21.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)': + '@effect/platform-node@0.103.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3)': 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 + '@effect/cluster': 0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3) + '@effect/platform': 0.93.8(effect@3.21.3) + '@effect/platform-node-shared': 0.56.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + effect: 3.21.3 mime: 3.0.0 - undici: 7.22.0 - ws: 8.19.0 + undici: 7.27.2 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform@0.93.8(effect@3.19.19)': + '@effect/platform@0.93.8(effect@3.21.3)': dependencies: - effect: 3.19.19 + effect: 3.21.3 find-my-way-ts: 0.1.6 - msgpackr: 1.11.8 + msgpackr: 1.11.14 multipasta: 0.2.7 - '@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.19.19))(effect@3.19.19)': + '@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3)': dependencies: - '@effect/platform': 0.93.8(effect@3.19.19) - effect: 3.19.19 - msgpackr: 1.11.8 + '@effect/platform': 0.93.8(effect@3.21.3) + effect: 3.21.3 + msgpackr: 1.11.14 - '@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/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3)': 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/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/platform': 0.93.8(effect@3.21.3) + effect: 3.21.3 + uuid: 11.1.1 - '@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/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3)': 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 + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/platform': 0.93.8(effect@3.21.3) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + effect: 3.21.3 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/aix-ppc64@0.28.0': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.28.0': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-arm@0.28.0': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/android-x64@0.28.0': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.28.0': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.28.0': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.28.0': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.28.0': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.28.0': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-arm@0.28.0': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.28.0': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.28.0': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.28.0': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.28.0': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.28.0': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.28.0': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/linux-x64@0.28.0': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true - '@esbuild/aix-ppc64@0.27.3': + '@esbuild/netbsd-arm64@0.27.7': optional: true - '@esbuild/android-arm64@0.27.3': + '@esbuild/netbsd-arm64@0.28.0': optional: true - '@esbuild/android-arm@0.27.3': + '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/android-x64@0.27.3': + '@esbuild/netbsd-x64@0.27.7': optional: true - '@esbuild/darwin-arm64@0.27.3': + '@esbuild/netbsd-x64@0.28.0': optional: true - '@esbuild/darwin-x64@0.27.3': + '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.27.3': + '@esbuild/openbsd-arm64@0.27.7': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@esbuild/openbsd-arm64@0.28.0': optional: true - '@esbuild/linux-arm64@0.27.3': + '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/linux-arm@0.27.3': + '@esbuild/openbsd-x64@0.27.7': optional: true - '@esbuild/linux-ia32@0.27.3': + '@esbuild/openbsd-x64@0.28.0': optional: true - '@esbuild/linux-loong64@0.27.3': + '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@esbuild/openharmony-arm64@0.27.7': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/openharmony-arm64@0.28.0': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/sunos-x64@0.27.7': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/sunos-x64@0.28.0': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/win32-arm64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/win32-arm64@0.28.0': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/win32-ia32@0.27.7': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/win32-ia32@0.28.0': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/win32-x64@0.27.7': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/win32-x64@0.28.0': optional: true + '@ethereumjs/rlp@10.1.2': {} + '@ethereumjs/rlp@4.0.1': {} '@ethereumjs/rlp@5.0.2': {} @@ -5260,268 +5969,268 @@ snapshots: '@gar/promisify@1.1.3': optional: true - '@grpc/grpc-js@1.14.3': + '@grpc/grpc-js@1.14.4': dependencies: - '@grpc/proto-loader': 0.8.0 + '@grpc/proto-loader': 0.8.1 '@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 + protobufjs: 7.6.2 yargs: 17.7.2 - '@grpc/proto-loader@0.8.0': + '@grpc/proto-loader@0.8.1': dependencies: lodash.camelcase: 4.3.0 long: 5.3.2 - protobufjs: 7.5.4 + protobufjs: 7.6.2 yargs: 17.7.2 '@inquirer/ansi@1.0.2': {} - '@inquirer/ansi@2.0.3': {} + '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@25.3.5)': + '@inquirer/checkbox@4.3.2(@types/node@25.9.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.9.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/checkbox@5.1.0(@types/node@25.3.5)': + '@inquirer/checkbox@5.2.1(@types/node@25.9.2)': 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) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/confirm@5.1.21(@types/node@25.3.5)': + '@inquirer/confirm@5.1.21(@types/node@25.9.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.3.5) - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) + '@inquirer/type': 3.0.10(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/confirm@6.0.8(@types/node@25.3.5)': + '@inquirer/confirm@6.1.1(@types/node@25.9.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@25.3.5) - '@inquirer/type': 4.0.3(@types/node@25.3.5) + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/core@10.3.2(@types/node@25.3.5)': + '@inquirer/core@10.3.2(@types/node@25.9.2)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.9.2) 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 + '@types/node': 25.9.2 - '@inquirer/core@11.1.5(@types/node@25.3.5)': + '@inquirer/core@11.2.1(@types/node@25.9.2)': dependencies: - '@inquirer/ansi': 2.0.3 - '@inquirer/figures': 2.0.3 - '@inquirer/type': 4.0.3(@types/node@25.3.5) + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.2) cli-width: 4.1.0 - fast-wrap-ansi: 0.2.0 + fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/editor@4.2.23(@types/node@25.3.5)': + '@inquirer/editor@4.2.23(@types/node@25.9.2)': 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) + '@inquirer/core': 10.3.2(@types/node@25.9.2) + '@inquirer/external-editor': 1.0.3(@types/node@25.9.2) + '@inquirer/type': 3.0.10(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/editor@5.0.8(@types/node@25.3.5)': + '@inquirer/editor@5.2.2(@types/node@25.9.2)': 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) + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/external-editor': 3.0.3(@types/node@25.9.2) + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/expand@4.0.23(@types/node@25.3.5)': + '@inquirer/expand@4.0.23(@types/node@25.9.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.3.5) - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) + '@inquirer/type': 3.0.10(@types/node@25.9.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/expand@5.0.8(@types/node@25.3.5)': + '@inquirer/expand@5.1.1(@types/node@25.9.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@25.3.5) - '@inquirer/type': 4.0.3(@types/node@25.3.5) + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/external-editor@1.0.3(@types/node@25.3.5)': + '@inquirer/external-editor@1.0.3(@types/node@25.9.2)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/external-editor@2.0.3(@types/node@25.3.5)': + '@inquirer/external-editor@3.0.3(@types/node@25.9.2)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 '@inquirer/figures@1.0.15': {} - '@inquirer/figures@2.0.3': {} + '@inquirer/figures@2.0.7': {} - '@inquirer/input@4.3.1(@types/node@25.3.5)': + '@inquirer/input@4.3.1(@types/node@25.9.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.3.5) - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) + '@inquirer/type': 3.0.10(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/input@5.0.8(@types/node@25.3.5)': + '@inquirer/input@5.1.2(@types/node@25.9.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@25.3.5) - '@inquirer/type': 4.0.3(@types/node@25.3.5) + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/number@3.0.23(@types/node@25.3.5)': + '@inquirer/number@3.0.23(@types/node@25.9.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.3.5) - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) + '@inquirer/type': 3.0.10(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/number@4.0.8(@types/node@25.3.5)': + '@inquirer/number@4.1.1(@types/node@25.9.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@25.3.5) - '@inquirer/type': 4.0.3(@types/node@25.3.5) + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/password@4.0.23(@types/node@25.3.5)': + '@inquirer/password@4.0.23(@types/node@25.9.2)': 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) + '@inquirer/core': 10.3.2(@types/node@25.9.2) + '@inquirer/type': 3.0.10(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/password@5.0.8(@types/node@25.3.5)': + '@inquirer/password@5.1.1(@types/node@25.9.2)': 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) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/type': 4.0.7(@types/node@25.9.2) 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) + '@types/node': 25.9.2 + + '@inquirer/prompts@7.3.1(@types/node@25.9.2)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@25.9.2) + '@inquirer/confirm': 5.1.21(@types/node@25.9.2) + '@inquirer/editor': 4.2.23(@types/node@25.9.2) + '@inquirer/expand': 4.0.23(@types/node@25.9.2) + '@inquirer/input': 4.3.1(@types/node@25.9.2) + '@inquirer/number': 3.0.23(@types/node@25.9.2) + '@inquirer/password': 4.0.23(@types/node@25.9.2) + '@inquirer/rawlist': 4.1.11(@types/node@25.9.2) + '@inquirer/search': 3.2.2(@types/node@25.9.2) + '@inquirer/select': 4.4.2(@types/node@25.9.2) 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) + '@types/node': 25.9.2 + + '@inquirer/prompts@8.5.2(@types/node@25.9.2)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@25.9.2) + '@inquirer/confirm': 6.1.1(@types/node@25.9.2) + '@inquirer/editor': 5.2.2(@types/node@25.9.2) + '@inquirer/expand': 5.1.1(@types/node@25.9.2) + '@inquirer/input': 5.1.2(@types/node@25.9.2) + '@inquirer/number': 4.1.1(@types/node@25.9.2) + '@inquirer/password': 5.1.1(@types/node@25.9.2) + '@inquirer/rawlist': 5.3.1(@types/node@25.9.2) + '@inquirer/search': 4.2.1(@types/node@25.9.2) + '@inquirer/select': 5.2.1(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/rawlist@4.1.11(@types/node@25.3.5)': + '@inquirer/rawlist@4.1.11(@types/node@25.9.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.3.5) - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) + '@inquirer/type': 3.0.10(@types/node@25.9.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/rawlist@5.2.4(@types/node@25.3.5)': + '@inquirer/rawlist@5.3.1(@types/node@25.9.2)': dependencies: - '@inquirer/core': 11.1.5(@types/node@25.3.5) - '@inquirer/type': 4.0.3(@types/node@25.3.5) + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/search@3.2.2(@types/node@25.3.5)': + '@inquirer/search@3.2.2(@types/node@25.9.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.9.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/search@4.1.4(@types/node@25.3.5)': + '@inquirer/search@4.2.1(@types/node@25.9.2)': 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) + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/select@4.4.2(@types/node@25.3.5)': + '@inquirer/select@4.4.2(@types/node@25.9.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.3.5) + '@inquirer/core': 10.3.2(@types/node@25.9.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.3.5) + '@inquirer/type': 3.0.10(@types/node@25.9.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/select@5.1.0(@types/node@25.3.5)': + '@inquirer/select@5.2.1(@types/node@25.9.2)': 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) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.2) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.2) optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/type@3.0.10(@types/node@25.3.5)': + '@inquirer/type@3.0.10(@types/node@25.9.2)': optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 - '@inquirer/type@4.0.3(@types/node@25.3.5)': + '@inquirer/type@4.0.7(@types/node@25.9.2)': optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 '@isaacs/balanced-match@4.0.1': {} @@ -5538,6 +6247,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -5559,34 +6272,34 @@ snapshots: '@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)': + '@moonwall/cli@5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api-derive@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/rpc-provider@16.5.6)(@polkadot/types-codec@16.5.6)(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@types/node@25.9.2)(chokidar@3.6.0)(debug@4.3.7(supports-color@8.1.1))(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(supports-color@8.1.1)(ts-node@10.9.2(@types/node@25.9.2)(typescript@5.8.3))(tsx@4.22.4)(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)) + '@acala-network/chopsticks': 1.4.2(debug@4.3.7(supports-color@8.1.1))(ts-node@10.9.2(@types/node@25.9.2)(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) + '@effect/cluster': 0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3) + '@effect/experimental': 0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/platform': 0.93.8(effect@3.21.3) + '@effect/platform-node': 0.103.0(@effect/cluster@0.55.0(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/workflow@0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/sql@0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3) + '@effect/rpc': 0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/sql': 0.48.6(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3) + '@effect/workflow': 0.15.2(@effect/experimental@0.57.11(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(@effect/platform@0.93.8(effect@3.21.3))(@effect/rpc@0.72.2(@effect/platform@0.93.8(effect@3.21.3))(effect@3.21.3))(effect@3.21.3) + '@inquirer/prompts': 8.5.2(@types/node@25.9.2) + '@moonwall/types': 5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@vitest/ui@3.2.6)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76) + '@moonwall/util': 5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api-derive@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/rpc-provider@16.5.6)(@polkadot/types-codec@16.5.6)(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@types/node@25.9.2)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(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) + '@polkadot/api': 16.5.6 + '@polkadot/api-derive': 16.5.6 + '@polkadot/keyring': 14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) '@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) + '@vitest/ui': 3.2.6(vitest@3.2.4) + '@zombienet/orchestrator': 0.0.113(@polkadot/util@14.0.3)(@types/node@25.9.2)(chokidar@3.6.0) + '@zombienet/utils': 0.0.30(@types/node@25.9.2)(chokidar@3.6.0)(typescript@5.8.3) arkregex: 0.0.4 bottleneck: 2.19.5 cfonts: 3.3.1 @@ -5594,25 +6307,25 @@ snapshots: clear: 0.1.0 cli-progress: 3.12.0 colors: 1.4.0 - dockerode: 4.0.9 + dockerode: 4.0.9(supports-color@8.1.1) dotenv: 17.2.3 - effect: 3.19.19 + effect: 3.21.3 ethers: 6.16.0 - ink: 6.8.0(@types/react@19.2.7)(react@19.2.4) + ink: 6.8.0(@types/react@19.2.7)(react@19.2.7) 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 + polkadot-api: 1.19.2(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2) + react: 19.2.7 reflect-metadata: 0.2.2 - semver: 7.7.4 + semver: 7.8.2 tiny-invariant: 1.3.3 - tmp: 0.2.5 + tmp: 0.2.7 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) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.9.2)(@vitest/ui@3.2.6)(jsdom@23.2.0)(tsx@4.22.4)(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 + ws: 8.21.0 yaml: 2.8.2 yargs: 18.0.0 transitivePeerDependencies: @@ -5628,7 +6341,6 @@ snapshots: - '@vitest/browser' - babel-plugin-macros - better-sqlite3 - - bluebird - bufferutil - canvas - chokidar @@ -5667,21 +6379,21 @@ snapshots: - 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)': + '@moonwall/types@5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@vitest/ui@3.2.6)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(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) + '@polkadot/api': 16.5.6 + '@polkadot/api-base': 16.5.6 + '@polkadot/keyring': 14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/types': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) + '@types/node': 24.13.1 + '@zombienet/utils': 0.0.30(@types/node@24.13.1)(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) + polkadot-api: 1.19.2(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(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) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.13.1)(@vitest/ui@3.2.6)(jsdom@23.2.0)(tsx@4.22.4)(yaml@2.8.2) web3: 4.16.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) transitivePeerDependencies: - '@edge-runtime/vm' @@ -5714,19 +6426,19 @@ snapshots: - 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) + '@moonwall/util@5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api-derive@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/rpc-provider@16.5.6)(@polkadot/types-codec@16.5.6)(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@types/node@25.9.2)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76)': + dependencies: + '@inquirer/prompts': 8.5.2(@types/node@25.9.2) + '@moonwall/types': 5.18.3(@polkadot/api-base@16.5.6)(@polkadot/api@16.5.6)(@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3))(@polkadot/types@16.5.6)(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)(@types/debug@4.1.12)(@vitest/ui@3.2.6)(chokidar@3.6.0)(encoding@0.1.13)(jsdom@23.2.0)(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(typescript@5.8.3)(yaml@2.8.2)(zod@3.25.76) + '@polkadot/api': 16.5.6 + '@polkadot/api-derive': 16.5.6 + '@polkadot/keyring': 14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) + '@vitest/ui': 3.2.6(vitest@3.2.4) arkregex: 0.0.4 bottleneck: 2.19.5 chalk: 5.6.2 @@ -5737,12 +6449,12 @@ snapshots: pino: 10.3.1 pino-pretty: 13.1.3 rlp: 3.0.0 - semver: 7.7.4 + semver: 7.8.2 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) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.9.2)(@vitest/ui@3.2.6)(jsdom@23.2.0)(tsx@4.22.4)(yaml@2.8.2) web3: 4.16.0(encoding@0.1.13)(typescript@5.8.3)(zod@3.25.76) - ws: 8.19.0 + ws: 8.21.0 yargs: 18.0.0 transitivePeerDependencies: - '@edge-runtime/vm' @@ -5776,29 +6488,29 @@ snapshots: - yaml - zod - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': optional: true '@noble/ciphers@1.2.1': {} '@noble/ciphers@1.3.0': {} - '@noble/ciphers@2.1.1': {} + '@noble/ciphers@2.2.0': {} '@noble/curves@1.2.0': dependencies: @@ -5830,14 +6542,14 @@ snapshots: '@noble/hashes@1.8.0': {} - '@noble/hashes@2.0.1': {} + '@noble/hashes@2.2.0': {} '@noble/secp256k1@1.7.2': {} '@npmcli/fs@1.1.1': dependencies: '@gar/promisify': 1.1.3 - semver: 7.7.4 + semver: 7.8.2 optional: true '@npmcli/move-file@1.1.2': @@ -5852,7 +6564,7 @@ snapshots: dependencies: '@octokit/auth-token': 6.0.0 '@octokit/graphql': 9.0.3 - '@octokit/request': 10.0.8 + '@octokit/request': 10.0.10 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 before-after-hook: 4.0.0 @@ -5865,7 +6577,7 @@ snapshots: '@octokit/graphql@9.0.3': dependencies: - '@octokit/request': 10.0.8 + '@octokit/request': 10.0.10 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 @@ -5889,13 +6601,13 @@ snapshots: dependencies: '@octokit/types': 16.0.0 - '@octokit/request@10.0.8': + '@octokit/request@10.0.10': 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 + content-type: 2.0.0 + json-with-bigint: 3.5.8 universal-user-agent: 7.0.3 '@octokit/rest@22.0.1': @@ -5953,7 +6665,7 @@ snapshots: detect-libc: 2.1.2 is-glob: 4.0.3 node-addon-api: 7.1.1 - picomatch: 4.0.3 + picomatch: 4.0.4 optionalDependencies: '@parcel/watcher-android-arm64': 2.5.6 '@parcel/watcher-darwin-arm64': 2.5.6 @@ -5988,7 +6700,7 @@ snapshots: '@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)': + '@polkadot-api/cli@0.15.2(postcss@8.5.15)(tsx@4.22.4)(yaml@2.8.2)': dependencies: '@commander-js/extra-typings': 14.0.0(commander@14.0.3) '@polkadot-api/codegen': 0.19.1 @@ -6006,15 +6718,55 @@ snapshots: '@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 + '@types/node': 24.13.1 commander: 14.0.3 execa: 9.6.1 fs.promises.exists: 1.1.4 - ora: 9.3.0 + ora: 9.4.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) + tsup: 8.5.1(postcss@8.5.15)(tsx@4.22.4)(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/cli@0.18.1(postcss@8.5.15)(tsx@4.22.4)(yaml@2.8.2)': + 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.9.2 + commander: 14.0.3 + execa: 9.6.1 + fs.promises.exists: 1.1.4 + ora: 9.4.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.15)(tsx@4.22.4)(typescript@5.9.3)(yaml@2.8.2) typescript: 5.9.3 write-package: 7.2.0 transitivePeerDependencies: @@ -6036,9 +6788,22 @@ snapshots: '@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))': + '@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/common-sdk-utils@0.1.0(polkadot-api@1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2))(rxjs@7.8.2)': + dependencies: + polkadot-api: 1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2) + rxjs: 7.8.2 + + '@polkadot-api/descriptors@file:.papi/descriptors(polkadot-api@1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(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) + polkadot-api: 1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2) optional: true '@polkadot-api/ink-contracts@0.4.0': @@ -6047,11 +6812,19 @@ snapshots: '@polkadot-api/substrate-bindings': 0.16.3 '@polkadot-api/utils': 0.2.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.1.0': optional: true '@polkadot-api/json-rpc-provider-proxy@0.2.4': {} + '@polkadot-api/json-rpc-provider-proxy@0.2.8': {} + '@polkadot-api/json-rpc-provider@0.0.1': optional: true @@ -6059,6 +6832,8 @@ snapshots: '@polkadot-api/known-chains@0.9.11': {} + '@polkadot-api/known-chains@0.9.18': {} + '@polkadot-api/legacy-provider@0.3.2(rxjs@7.8.2)': dependencies: '@polkadot-api/json-rpc-provider': 0.0.4 @@ -6067,6 +6842,14 @@ snapshots: '@polkadot-api/utils': 0.2.0 rxjs: 7.8.2 + '@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 @@ -6083,6 +6866,12 @@ snapshots: '@polkadot-api/substrate-bindings': 0.17.0 '@polkadot-api/utils': 0.2.0 + '@polkadot-api/merkleize-metadata@1.2.3': + dependencies: + '@polkadot-api/metadata-builders': 0.14.3 + '@polkadot-api/substrate-bindings': 0.20.3 + '@polkadot-api/utils': 0.4.0 + '@polkadot-api/metadata-builders@0.13.5': dependencies: '@polkadot-api/substrate-bindings': 0.16.3 @@ -6093,6 +6882,11 @@ snapshots: '@polkadot-api/substrate-bindings': 0.17.0 '@polkadot-api/utils': 0.2.0 + '@polkadot-api/metadata-builders@0.14.3': + dependencies: + '@polkadot-api/substrate-bindings': 0.20.3 + '@polkadot-api/utils': 0.4.0 + '@polkadot-api/metadata-builders@0.3.2': dependencies: '@polkadot-api/substrate-bindings': 0.6.0 @@ -6104,6 +6898,11 @@ snapshots: '@polkadot-api/metadata-builders': 0.13.5 '@polkadot-api/substrate-bindings': 0.16.3 + '@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.15.1(rxjs@7.8.2)': dependencies: '@polkadot-api/metadata-builders': 0.13.5 @@ -6112,6 +6911,14 @@ snapshots: '@polkadot-api/utils': 0.2.0 rxjs: 7.8.2 + '@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/observable-client@0.3.2(@polkadot-api/substrate-client@0.1.4)(rxjs@7.8.2)': dependencies: '@polkadot-api/metadata-builders': 0.3.2 @@ -6129,19 +6936,56 @@ snapshots: '@polkadot-api/substrate-bindings': 0.16.3 '@polkadot-api/utils': 0.2.0 + '@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.3.3': dependencies: '@polkadot-api/json-rpc-provider': 0.0.4 + '@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/sdk-ink@0.5.1(@polkadot-api/ink-contracts@0.4.6)(polkadot-api@1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2))(rxjs@7.8.2)(typescript@5.8.3)(zod@3.25.76)': + dependencies: + '@ethereumjs/rlp': 10.1.2 + '@polkadot-api/common-sdk-utils': 0.1.0(polkadot-api@1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2))(rxjs@7.8.2) + '@polkadot-api/ink-contracts': 0.4.6 + '@polkadot-api/substrate-bindings': 0.16.6 + abitype: 1.2.4(typescript@5.8.3)(zod@3.25.76) + polkadot-api: 1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2) + rxjs: 7.8.2 + viem: 2.38.0(typescript@5.8.3)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@polkadot-api/signer@0.2.13': + dependencies: + '@noble/hashes': 2.2.0 + '@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/signer@0.2.9': dependencies: - '@noble/hashes': 2.0.1 + '@noble/hashes': 2.2.0 '@polkadot-api/merkleize-metadata': 1.1.25 '@polkadot-api/polkadot-signer': 0.1.6 '@polkadot-api/signers-common': 0.1.16 @@ -6155,32 +6999,67 @@ snapshots: '@polkadot-api/substrate-bindings': 0.16.3 '@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.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/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.14': dependencies: - '@types/node': 24.12.0 + '@types/node': 24.13.1 smoldot: 2.0.39 transitivePeerDependencies: - bufferutil - utf-8-validate + '@polkadot-api/smoldot@0.3.15': + dependencies: + '@types/node': 24.13.1 + smoldot: 2.0.40 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@polkadot-api/substrate-bindings@0.16.3': dependencies: - '@noble/hashes': 2.0.1 + '@noble/hashes': 2.2.0 + '@polkadot-api/utils': 0.2.0 + '@scure/base': 2.2.0 + scale-ts: 1.6.1 + + '@polkadot-api/substrate-bindings@0.16.6': + dependencies: + '@noble/hashes': 2.2.0 '@polkadot-api/utils': 0.2.0 - '@scure/base': 2.0.0 + '@scure/base': 2.2.0 scale-ts: 1.6.1 '@polkadot-api/substrate-bindings@0.17.0': dependencies: - '@noble/hashes': 2.0.1 + '@noble/hashes': 2.2.0 '@polkadot-api/utils': 0.2.0 - '@scure/base': 2.0.0 + '@scure/base': 2.2.0 + scale-ts: 1.6.1 + + '@polkadot-api/substrate-bindings@0.20.3': + dependencies: + '@noble/hashes': 2.2.0 + '@polkadot-api/utils': 0.4.0 + '@scure/base': 2.2.0 scale-ts: 1.6.1 '@polkadot-api/substrate-bindings@0.6.0': @@ -6203,11 +7082,19 @@ snapshots: '@polkadot-api/raw-client': 0.1.1 '@polkadot-api/utils': 0.2.0 + '@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.1.0': optional: true '@polkadot-api/utils@0.2.0': {} + '@polkadot-api/utils@0.4.0': {} + '@polkadot-api/wasm-executor@0.2.3': {} '@polkadot-api/ws-provider@0.6.2': @@ -6215,7 +7102,17 @@ snapshots: '@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 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@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.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -6234,14 +7131,14 @@ snapshots: - supports-color - utf-8-validate - '@polkadot/api-augment@16.5.4': + '@polkadot/api-augment@16.5.6': 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 + '@polkadot/api-base': 16.5.6 + '@polkadot/rpc-augment': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-augment': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/util': 14.0.3 tslib: 2.8.1 transitivePeerDependencies: - bufferutil @@ -6260,11 +7157,11 @@ snapshots: - supports-color - utf-8-validate - '@polkadot/api-base@16.5.4': + '@polkadot/api-base@16.5.6': dependencies: - '@polkadot/rpc-core': 16.5.4 - '@polkadot/types': 16.5.4 - '@polkadot/util': 14.0.1 + '@polkadot/rpc-core': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/util': 14.0.3 rxjs: 7.8.2 tslib: 2.8.1 transitivePeerDependencies: @@ -6289,16 +7186,16 @@ snapshots: - supports-color - utf-8-validate - '@polkadot/api-derive@16.5.4': + '@polkadot/api-derive@16.5.6': 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) + '@polkadot/api': 16.5.6 + '@polkadot/api-augment': 16.5.6 + '@polkadot/api-base': 16.5.6 + '@polkadot/rpc-core': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) rxjs: 7.8.2 tslib: 2.8.1 transitivePeerDependencies: @@ -6311,7 +7208,7 @@ snapshots: '@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/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@13.5.9))(@polkadot/util@13.5.9) '@polkadot/rpc-augment': 14.3.1 '@polkadot/rpc-core': 14.3.1 '@polkadot/rpc-provider': 14.3.1 @@ -6330,22 +7227,22 @@ snapshots: - 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) + '@polkadot/api@16.5.6': + dependencies: + '@polkadot/api-augment': 16.5.6 + '@polkadot/api-base': 16.5.6 + '@polkadot/api-derive': 16.5.6 + '@polkadot/keyring': 14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/rpc-augment': 16.5.6 + '@polkadot/rpc-core': 16.5.6 + '@polkadot/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-augment': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/types-create': 16.5.6 + '@polkadot/types-known': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) eventemitter3: 5.0.4 rxjs: 7.8.2 tslib: 2.8.1 @@ -6354,22 +7251,22 @@ snapshots: - 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)': + '@polkadot/keyring@13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@13.5.9))(@polkadot/util@13.5.9)': dependencies: '@polkadot/util': 13.5.9 - '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.1) + '@polkadot/util-crypto': 13.5.9(@polkadot/util@13.5.9) 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)': + '@polkadot/keyring@13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)': dependencies: - '@polkadot/util': 14.0.1 - '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.1) + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.3) 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)': + '@polkadot/keyring@14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3)': dependencies: - '@polkadot/util': 14.0.1 - '@polkadot/util-crypto': 14.0.1(@polkadot/util@14.0.1) + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) tslib: 2.8.1 '@polkadot/networks@13.5.9': @@ -6378,9 +7275,9 @@ snapshots: '@substrate/ss58-registry': 1.51.0 tslib: 2.8.1 - '@polkadot/networks@14.0.1': + '@polkadot/networks@14.0.3': dependencies: - '@polkadot/util': 14.0.1 + '@polkadot/util': 14.0.3 '@substrate/ss58-registry': 1.51.0 tslib: 2.8.1 @@ -6396,12 +7293,12 @@ snapshots: - supports-color - utf-8-validate - '@polkadot/rpc-augment@16.5.4': + '@polkadot/rpc-augment@16.5.6': dependencies: - '@polkadot/rpc-core': 16.5.4 - '@polkadot/types': 16.5.4 - '@polkadot/types-codec': 16.5.4 - '@polkadot/util': 14.0.1 + '@polkadot/rpc-core': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/util': 14.0.3 tslib: 2.8.1 transitivePeerDependencies: - bufferutil @@ -6421,12 +7318,12 @@ snapshots: - supports-color - utf-8-validate - '@polkadot/rpc-core@16.5.4': + '@polkadot/rpc-core@16.5.6': dependencies: - '@polkadot/rpc-augment': 16.5.4 - '@polkadot/rpc-provider': 16.5.4 - '@polkadot/types': 16.5.4 - '@polkadot/util': 14.0.1 + '@polkadot/rpc-augment': 16.5.6 + '@polkadot/rpc-provider': 16.5.6 + '@polkadot/types': 16.5.6 + '@polkadot/util': 14.0.3 rxjs: 7.8.2 tslib: 2.8.1 transitivePeerDependencies: @@ -6436,7 +7333,7 @@ snapshots: '@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/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@13.5.9))(@polkadot/util@13.5.9) '@polkadot/types': 14.3.1 '@polkadot/types-support': 14.3.1 '@polkadot/util': 13.5.9 @@ -6455,16 +7352,16 @@ snapshots: - supports-color - utf-8-validate - '@polkadot/rpc-provider@16.5.4': + '@polkadot/rpc-provider@16.5.6': 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 + '@polkadot/keyring': 14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/types': 16.5.6 + '@polkadot/types-support': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) + '@polkadot/x-fetch': 14.0.3 + '@polkadot/x-global': 14.0.3 + '@polkadot/x-ws': 14.0.3 eventemitter3: 5.0.4 mock-socket: 9.3.1 nock: 13.5.6 @@ -6483,11 +7380,11 @@ snapshots: '@polkadot/util': 13.5.9 tslib: 2.8.1 - '@polkadot/types-augment@16.5.4': + '@polkadot/types-augment@16.5.6': dependencies: - '@polkadot/types': 16.5.4 - '@polkadot/types-codec': 16.5.4 - '@polkadot/util': 14.0.1 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/util': 14.0.3 tslib: 2.8.1 '@polkadot/types-codec@14.3.1': @@ -6496,10 +7393,10 @@ snapshots: '@polkadot/x-bigint': 13.5.9 tslib: 2.8.1 - '@polkadot/types-codec@16.5.4': + '@polkadot/types-codec@16.5.6': dependencies: - '@polkadot/util': 14.0.1 - '@polkadot/x-bigint': 14.0.1 + '@polkadot/util': 14.0.3 + '@polkadot/x-bigint': 14.0.3 tslib: 2.8.1 '@polkadot/types-create@14.3.1': @@ -6508,10 +7405,10 @@ snapshots: '@polkadot/util': 13.5.9 tslib: 2.8.1 - '@polkadot/types-create@16.5.4': + '@polkadot/types-create@16.5.6': dependencies: - '@polkadot/types-codec': 16.5.4 - '@polkadot/util': 14.0.1 + '@polkadot/types-codec': 16.5.6 + '@polkadot/util': 14.0.3 tslib: 2.8.1 '@polkadot/types-known@14.3.1': @@ -6523,13 +7420,13 @@ snapshots: '@polkadot/util': 13.5.9 tslib: 2.8.1 - '@polkadot/types-known@16.5.4': + '@polkadot/types-known@16.5.6': 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 + '@polkadot/networks': 14.0.3 + '@polkadot/types': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/types-create': 16.5.6 + '@polkadot/util': 14.0.3 tslib: 2.8.1 '@polkadot/types-support@14.3.1': @@ -6537,14 +7434,14 @@ snapshots: '@polkadot/util': 13.5.9 tslib: 2.8.1 - '@polkadot/types-support@16.5.4': + '@polkadot/types-support@16.5.6': dependencies: - '@polkadot/util': 14.0.1 + '@polkadot/util': 14.0.3 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/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@13.5.9))(@polkadot/util@13.5.9) '@polkadot/types-augment': 14.3.1 '@polkadot/types-codec': 14.3.1 '@polkadot/types-create': 14.3.1 @@ -6553,14 +7450,14 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 - '@polkadot/types@16.5.4': + '@polkadot/types@16.5.6': 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) + '@polkadot/keyring': 14.0.3(@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/types-augment': 16.5.6 + '@polkadot/types-codec': 16.5.6 + '@polkadot/types-create': 16.5.6 + '@polkadot/util': 14.0.3 + '@polkadot/util-crypto': 14.0.3(@polkadot/util@14.0.3) rxjs: 7.8.2 tslib: 2.8.1 @@ -6570,36 +7467,36 @@ snapshots: '@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-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@13.5.9))) '@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)) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@13.5.9)) '@scure/base': 1.2.6 tslib: 2.8.1 - '@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.1)': + '@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.3)': 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/util': 14.0.3 + '@polkadot/wasm-crypto': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) '@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)) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) '@scure/base': 1.2.6 tslib: 2.8.1 - '@polkadot/util-crypto@14.0.1(@polkadot/util@14.0.1)': + '@polkadot/util-crypto@14.0.3(@polkadot/util@14.0.3)': 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)) + '@polkadot/networks': 14.0.3 + '@polkadot/util': 14.0.3 + '@polkadot/wasm-crypto': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-bigint': 14.0.3 + '@polkadot/x-randomvalues': 14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) '@scure/base': 1.2.6 '@scure/sr25519': 0.2.0 tslib: 2.8.1 @@ -6614,35 +7511,35 @@ snapshots: bn.js: 5.2.3 tslib: 2.8.1 - '@polkadot/util@14.0.1': + '@polkadot/util@14.0.3': 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 + '@polkadot/x-bigint': 14.0.3 + '@polkadot/x-global': 14.0.3 + '@polkadot/x-textdecoder': 14.0.3 + '@polkadot/x-textencoder': 14.0.3 '@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)))': + '@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@13.5.9)))': 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)) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@13.5.9)) 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)))': + '@polkadot/wasm-bridge@7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)))': 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)) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) 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)))': + '@polkadot/wasm-bridge@7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)))': 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)) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-randomvalues': 14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) tslib: 2.8.1 '@polkadot/wasm-crypto-asmjs@7.5.4(@polkadot/util@13.5.9)': @@ -6650,39 +7547,39 @@ snapshots: '@polkadot/util': 13.5.9 tslib: 2.8.1 - '@polkadot/wasm-crypto-asmjs@7.5.4(@polkadot/util@14.0.1)': + '@polkadot/wasm-crypto-asmjs@7.5.4(@polkadot/util@14.0.3)': dependencies: - '@polkadot/util': 14.0.1 + '@polkadot/util': 14.0.3 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)))': + '@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@13.5.9)))': 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-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@13.5.9))) '@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)) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@13.5.9)) 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)))': + '@polkadot/wasm-crypto-init@7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)))': 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)) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) 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)))': + '@polkadot/wasm-crypto-init@7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)))': 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)) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-randomvalues': 14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) tslib: 2.8.1 '@polkadot/wasm-crypto-wasm@7.5.4(@polkadot/util@13.5.9)': @@ -6691,43 +7588,43 @@ snapshots: '@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)': + '@polkadot/wasm-crypto-wasm@7.5.4(@polkadot/util@14.0.3)': dependencies: - '@polkadot/util': 14.0.1 - '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) 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)))': + '@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@13.5.9)))': 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-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@13.5.9))) '@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-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@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)) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@13.5.9)) 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)))': + '@polkadot/wasm-crypto@7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)))': 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)) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-crypto-init': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-randomvalues': 13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) 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)))': + '@polkadot/wasm-crypto@7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)))': 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)) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-bridge': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-crypto-asmjs': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-crypto-init': 7.5.4(@polkadot/util@14.0.3)(@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))) + '@polkadot/wasm-crypto-wasm': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-randomvalues': 14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)) tslib: 2.8.1 '@polkadot/wasm-util@7.5.4(@polkadot/util@13.5.9)': @@ -6735,9 +7632,9 @@ snapshots: '@polkadot/util': 13.5.9 tslib: 2.8.1 - '@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.1)': + '@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3)': dependencies: - '@polkadot/util': 14.0.1 + '@polkadot/util': 14.0.3 tslib: 2.8.1 '@polkadot/x-bigint@13.5.9': @@ -6745,9 +7642,9 @@ snapshots: '@polkadot/x-global': 13.5.9 tslib: 2.8.1 - '@polkadot/x-bigint@14.0.1': + '@polkadot/x-bigint@14.0.3': dependencies: - '@polkadot/x-global': 14.0.1 + '@polkadot/x-global': 14.0.3 tslib: 2.8.1 '@polkadot/x-fetch@13.5.9': @@ -6756,9 +7653,9 @@ snapshots: node-fetch: 3.3.2 tslib: 2.8.1 - '@polkadot/x-fetch@14.0.1': + '@polkadot/x-fetch@14.0.3': dependencies: - '@polkadot/x-global': 14.0.1 + '@polkadot/x-global': 14.0.3 node-fetch: 3.3.2 tslib: 2.8.1 @@ -6766,29 +7663,29 @@ snapshots: dependencies: tslib: 2.8.1 - '@polkadot/x-global@14.0.1': + '@polkadot/x-global@14.0.3': 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))': + '@polkadot/x-randomvalues@13.5.9(@polkadot/util@13.5.9)(@polkadot/wasm-util@7.5.4(@polkadot/util@13.5.9))': dependencies: '@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-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))': + '@polkadot/x-randomvalues@13.5.9(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))': dependencies: - '@polkadot/util': 14.0.1 - '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) + '@polkadot/util': 14.0.3 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) '@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))': + '@polkadot/x-randomvalues@14.0.3(@polkadot/util@14.0.3)(@polkadot/wasm-util@7.5.4(@polkadot/util@14.0.3))': dependencies: - '@polkadot/util': 14.0.1 - '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.1) - '@polkadot/x-global': 14.0.1 + '@polkadot/util': 14.0.3 + '@polkadot/wasm-util': 7.5.4(@polkadot/util@14.0.3) + '@polkadot/x-global': 14.0.3 tslib: 2.8.1 '@polkadot/x-textdecoder@13.5.9': @@ -6796,9 +7693,9 @@ snapshots: '@polkadot/x-global': 13.5.9 tslib: 2.8.1 - '@polkadot/x-textdecoder@14.0.1': + '@polkadot/x-textdecoder@14.0.3': dependencies: - '@polkadot/x-global': 14.0.1 + '@polkadot/x-global': 14.0.3 tslib: 2.8.1 '@polkadot/x-textencoder@13.5.9': @@ -6806,25 +7703,25 @@ snapshots: '@polkadot/x-global': 13.5.9 tslib: 2.8.1 - '@polkadot/x-textencoder@14.0.1': + '@polkadot/x-textencoder@14.0.3': dependencies: - '@polkadot/x-global': 14.0.1 + '@polkadot/x-global': 14.0.3 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 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@polkadot/x-ws@14.0.1': + '@polkadot/x-ws@14.0.3': dependencies: - '@polkadot/x-global': 14.0.1 + '@polkadot/x-global': 14.0.3 tslib: 2.8.1 - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -6833,98 +7730,97 @@ snapshots: '@protobufjs/base64@1.1.2': {} - '@protobufjs/codegen@2.0.4': {} + '@protobufjs/codegen@2.0.5': {} - '@protobufjs/eventemitter@1.1.0': {} + '@protobufjs/eventemitter@1.1.1': {} - '@protobufjs/fetch@1.1.0': + '@protobufjs/fetch@1.1.1': dependencies: '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 '@protobufjs/float@1.0.2': {} - '@protobufjs/inquire@1.1.0': {} + '@protobufjs/inquire@1.1.2': {} '@protobufjs/path@1.1.2': {} '@protobufjs/pool@1.1.0': {} - '@protobufjs/utf8@1.1.0': {} + '@protobufjs/utf8@1.1.1': {} - '@rollup/rollup-android-arm-eabi@4.59.0': + '@rollup/rollup-android-arm-eabi@4.61.1': optional: true - '@rollup/rollup-android-arm64@4.59.0': + '@rollup/rollup-android-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + '@rollup/rollup-darwin-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-x64@4.59.0': + '@rollup/rollup-darwin-x64@4.61.1': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + '@rollup/rollup-freebsd-arm64@4.61.1': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + '@rollup/rollup-freebsd-x64@4.61.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + '@rollup/rollup-linux-arm-musleabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + '@rollup/rollup-linux-arm64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + '@rollup/rollup-linux-arm64-musl@4.61.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@rollup/rollup-linux-loong64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@rollup/rollup-linux-loong64-musl@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@rollup/rollup-linux-ppc64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@rollup/rollup-linux-ppc64-musl@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@rollup/rollup-linux-riscv64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@rollup/rollup-linux-riscv64-musl@4.61.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@rollup/rollup-linux-s390x-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@rollup/rollup-linux-x64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@rollup/rollup-linux-x64-musl@4.61.1': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@rollup/rollup-openbsd-x64@4.61.1': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@rollup/rollup-openharmony-arm64@4.61.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@rollup/rollup-win32-arm64-msvc@4.61.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@rollup/rollup-win32-ia32-msvc@4.61.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@rollup/rollup-win32-x64-gnu@4.61.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@rollup/rollup-win32-x64-msvc@4.61.1': optional: true '@rx-state/core@0.1.4(rxjs@7.8.2)': @@ -6935,7 +7831,7 @@ snapshots: '@scure/base@1.2.6': {} - '@scure/base@2.0.0': {} + '@scure/base@2.2.0': {} '@scure/bip32@1.4.0': dependencies: @@ -7027,7 +7923,7 @@ snapshots: '@types/bn.js@5.2.0': dependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 '@types/chai@5.2.3': dependencies: @@ -7040,7 +7936,7 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} '@types/json-bigint@1.0.4': {} @@ -7052,13 +7948,13 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/node@24.12.0': + '@types/node@24.13.1': dependencies: - undici-types: 7.16.0 + undici-types: 7.18.2 - '@types/node@25.3.5': + '@types/node@25.9.2': dependencies: - undici-types: 7.18.2 + undici-types: 7.24.6 '@types/normalize-package-data@2.4.4': {} @@ -7072,11 +7968,11 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 '@types/ws@8.5.3': dependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 '@types/yargs-parser@21.0.3': {} @@ -7092,19 +7988,19 @@ snapshots: 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))': + '@vitest/mocker@3.2.4(vite@7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0))': 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) + vite: 7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0) - '@vitest/pretty-format@3.1.3': + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@3.2.4': + '@vitest/pretty-format@3.2.6': dependencies: tinyrainbow: 2.0.0 @@ -7124,51 +8020,51 @@ snapshots: dependencies: tinyspy: 4.0.4 - '@vitest/ui@3.1.3(vitest@3.2.4)': + '@vitest/ui@3.2.4(vitest@3.2.4)': dependencies: - '@vitest/utils': 3.1.3 - fflate: 0.8.2 - flatted: 3.3.4 + '@vitest/utils': 3.2.4 + fflate: 0.8.3 + flatted: 3.4.2 pathe: 2.0.3 sirv: 3.0.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 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: 3.2.4(@types/debug@4.1.12)(@types/node@25.9.2)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.22.4)(yaml@2.9.0) - '@vitest/ui@3.2.4(vitest@3.2.4)': + '@vitest/ui@3.2.6(vitest@3.2.4)': dependencies: - '@vitest/utils': 3.2.4 - fflate: 0.8.2 - flatted: 3.3.4 + '@vitest/utils': 3.2.6 + fflate: 0.8.3 + flatted: 3.4.2 pathe: 2.0.3 sirv: 3.0.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 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: 3.2.4(@types/debug@4.1.12)(@types/node@25.9.2)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.22.4)(yaml@2.9.0) - '@vitest/utils@3.1.3': + '@vitest/utils@3.2.4': dependencies: - '@vitest/pretty-format': 3.1.3 + '@vitest/pretty-format': 3.2.4 loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@3.2.4': + '@vitest/utils@3.2.6': dependencies: - '@vitest/pretty-format': 3.2.4 + '@vitest/pretty-format': 3.2.6 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)': + '@zombienet/orchestrator@0.0.105(@polkadot/util@14.0.3)(@types/node@25.9.2)(chokidar@3.6.0)(supports-color@8.1.1)': 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) + '@polkadot/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.3) + '@zombienet/utils': 0.0.28(@types/node@25.9.2)(chokidar@3.6.0)(supports-color@8.1.1)(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 + fs-extra: 11.3.5 jsdom: 23.2.0 json-bigint: 1.0.0 libp2p-crypto: 0.21.2 @@ -7178,7 +8074,7 @@ snapshots: peer-id: 0.16.0 tmp-promise: 3.0.3 typescript: 5.8.3 - yaml: 2.8.2 + yaml: 2.9.0 transitivePeerDependencies: - '@polkadot/util' - '@swc/core' @@ -7190,17 +8086,17 @@ snapshots: - supports-color - utf-8-validate - '@zombienet/orchestrator@0.0.113(@polkadot/util@14.0.1)(@types/node@25.3.5)(chokidar@3.6.0)': + '@zombienet/orchestrator@0.0.113(@polkadot/util@14.0.3)(@types/node@25.9.2)(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) + '@polkadot/keyring': 13.5.9(@polkadot/util-crypto@13.5.9(@polkadot/util@14.0.3))(@polkadot/util@14.0.3) + '@polkadot/util-crypto': 13.5.9(@polkadot/util@14.0.3) + '@zombienet/utils': 0.0.30(@types/node@25.9.2)(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 + fs-extra: 11.3.5 jsdom: 23.2.0 json-bigint: 1.0.0 libp2p-crypto: 0.21.2 @@ -7222,14 +8118,14 @@ snapshots: - supports-color - utf-8-validate - '@zombienet/utils@0.0.28(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3)': + '@zombienet/utils@0.0.28(@types/node@25.9.2)(chokidar@3.6.0)(supports-color@8.1.1)(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) + toml: 3.0.0 + ts-node: 10.9.2(@types/node@25.9.2)(typescript@5.8.3) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -7238,14 +8134,14 @@ snapshots: - supports-color - typescript - '@zombienet/utils@0.0.30(@types/node@24.12.0)(chokidar@3.6.0)(typescript@5.8.3)': + '@zombienet/utils@0.0.30(@types/node@24.13.1)(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) + toml: 3.0.0 + ts-node: 10.9.2(@types/node@24.13.1)(typescript@5.8.3) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -7254,14 +8150,14 @@ snapshots: - supports-color - typescript - '@zombienet/utils@0.0.30(@types/node@25.3.5)(chokidar@3.6.0)(typescript@5.8.3)': + '@zombienet/utils@0.0.30(@types/node@25.9.2)(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) + toml: 3.0.0 + ts-node: 10.9.2(@types/node@25.9.2)(typescript@5.8.3) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -7280,6 +8176,9 @@ snapshots: abbrev@1.1.1: optional: true + abbrev@4.0.0: + optional: true + abitype@0.7.1(typescript@5.8.3)(zod@3.25.76): dependencies: typescript: 5.8.3 @@ -7291,6 +8190,11 @@ snapshots: typescript: 5.8.3 zod: 3.25.76 + abitype@1.2.4(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 @@ -7299,12 +8203,11 @@ snapshots: aes-js@4.0.0-beta.5: {} - agent-base@6.0.2: + agent-base@6.0.2(supports-color@8.1.1): dependencies: debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color - optional: true agent-base@7.1.4: {} @@ -7335,14 +8238,14 @@ snapshots: ansi-styles@6.2.3: {} - ansis@4.2.0: {} + ansis@4.3.1: {} any-promise@1.3.0: {} anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.1 + picomatch: 2.3.2 app-root-path@3.1.0: {} @@ -7383,13 +8286,15 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios@1.13.6(debug@4.3.7): + axios@1.17.0(debug@4.3.7(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - follow-redirects: 1.15.11(debug@4.3.7) + follow-redirects: 1.16.0(debug@4.3.7(supports-color@8.1.1)) form-data: 4.0.5 - proxy-from-env: 1.1.0 + https-proxy-agent: 5.0.1(supports-color@8.1.1) + proxy-from-env: 2.1.0 transitivePeerDependencies: - debug + - supports-color balanced-match@1.0.2: {} @@ -7425,13 +8330,13 @@ snapshots: bottleneck@2.19.5: {} - brace-expansion@1.1.12: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 optional: true - brace-expansion@2.0.2: + brace-expansion@2.1.1: dependencies: balanced-match: 1.0.2 @@ -7454,9 +8359,14 @@ snapshots: buildcheck@0.0.7: optional: true - bundle-require@5.1.0(esbuild@0.27.3): + bundle-require@5.1.0(esbuild@0.25.12): dependencies: - esbuild: 0.27.3 + esbuild: 0.25.12 + load-tsconfig: 0.2.5 + + bundle-require@5.1.0(esbuild@0.27.7): + dependencies: + esbuild: 0.27.7 load-tsconfig: 0.2.5 cac@6.7.14: {} @@ -7472,7 +8382,7 @@ snapshots: lru-cache: 6.0.0 minipass: 3.3.6 minipass-collect: 1.0.2 - minipass-flush: 1.0.5 + minipass-flush: 1.0.7 minipass-pipeline: 1.2.4 mkdirp: 1.0.4 p-map: 4.0.0 @@ -7490,7 +8400,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: + call-bind@1.0.9: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 @@ -7567,6 +8477,8 @@ snapshots: chownr@2.0.0: {} + chownr@3.0.0: {} + class-is@1.1.0: {} clean-stack@2.2.0: @@ -7599,7 +8511,7 @@ snapshots: cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.0 + string-width: 8.2.1 cli-width@4.1.0: {} @@ -7671,12 +8583,14 @@ snapshots: console-control-strings@1.1.0: optional: true + content-type@2.0.0: {} + convert-to-spaces@2.0.1: {} cpu-features@0.0.10: dependencies: buildcheck: 0.0.7 - nan: 2.25.0 + nan: 2.27.0 optional: true crc-32@1.2.2: {} @@ -7716,7 +8630,7 @@ snapshots: dateformat@4.6.3: {} - dayjs@1.11.19: {} + dayjs@1.11.21: {} debug@4.3.7(supports-color@8.1.1): dependencies: @@ -7762,7 +8676,7 @@ snapshots: define-property@1.0.0: dependencies: - is-descriptor: 1.0.3 + is-descriptor: 1.0.4 delayed-stream@1.0.0: {} @@ -7781,7 +8695,7 @@ snapshots: diff@5.2.2: {} - docker-modem@5.0.6: + docker-modem@5.0.7(supports-color@8.1.1): dependencies: debug: 4.3.7(supports-color@8.1.1) readable-stream: 3.6.2 @@ -7790,13 +8704,13 @@ snapshots: transitivePeerDependencies: - supports-color - dockerode@4.0.9: + dockerode@4.0.9(supports-color@8.1.1): dependencies: '@balena/dockerignore': 1.0.2 - '@grpc/grpc-js': 1.14.3 + '@grpc/grpc-js': 1.14.4 '@grpc/proto-loader': 0.7.15 - docker-modem: 5.0.6 - protobufjs: 7.5.4 + docker-modem: 5.0.7(supports-color@8.1.1) + protobufjs: 7.6.2 tar-fs: 2.1.4 uuid: 10.0.0 transitivePeerDependencies: @@ -7814,7 +8728,7 @@ snapshots: eastasianwidth@0.2.0: {} - effect@3.19.19: + effect@3.21.3: dependencies: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 @@ -7852,7 +8766,7 @@ snapshots: es-module-lexer@1.7.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -7861,40 +8775,98 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 - es-toolkit@1.45.1: {} + es-toolkit@1.47.0: {} es6-error@4.1.1: {} - esbuild@0.27.3: + 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.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + esbuild@0.28.0: 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 + '@esbuild/aix-ppc64': 0.28.0 + '@esbuild/android-arm': 0.28.0 + '@esbuild/android-arm64': 0.28.0 + '@esbuild/android-x64': 0.28.0 + '@esbuild/darwin-arm64': 0.28.0 + '@esbuild/darwin-x64': 0.28.0 + '@esbuild/freebsd-arm64': 0.28.0 + '@esbuild/freebsd-x64': 0.28.0 + '@esbuild/linux-arm': 0.28.0 + '@esbuild/linux-arm64': 0.28.0 + '@esbuild/linux-ia32': 0.28.0 + '@esbuild/linux-loong64': 0.28.0 + '@esbuild/linux-mips64el': 0.28.0 + '@esbuild/linux-ppc64': 0.28.0 + '@esbuild/linux-riscv64': 0.28.0 + '@esbuild/linux-s390x': 0.28.0 + '@esbuild/linux-x64': 0.28.0 + '@esbuild/netbsd-arm64': 0.28.0 + '@esbuild/netbsd-x64': 0.28.0 + '@esbuild/openbsd-arm64': 0.28.0 + '@esbuild/openbsd-x64': 0.28.0 + '@esbuild/openharmony-arm64': 0.28.0 + '@esbuild/sunos-x64': 0.28.0 + '@esbuild/win32-arm64': 0.28.0 + '@esbuild/win32-ia32': 0.28.0 + '@esbuild/win32-x64': 0.28.0 escalade@3.2.0: {} @@ -7904,7 +8876,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 ethereum-cryptography@2.2.1: dependencies: @@ -7971,13 +8943,14 @@ snapshots: expect-type@1.3.0: {} + exponential-backoff@3.1.3: + optional: true + fast-check@3.23.2: dependencies: pure-rand: 6.1.0 - fast-content-type-parse@3.0.0: {} - - fast-copy@4.0.2: {} + fast-copy@4.0.3: {} fast-safe-stringify@2.1.1: {} @@ -7987,20 +8960,20 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-wrap-ansi@0.2.0: + fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 - fflate@0.8.2: {} + fflate@0.8.3: {} figures@6.1.0: dependencies: @@ -8022,14 +8995,14 @@ snapshots: fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.21 - mlly: 1.8.1 - rollup: 4.59.0 + mlly: 1.8.2 + rollup: 4.61.1 flat@5.0.2: {} - flatted@3.3.4: {} + flatted@3.4.2: {} - follow-redirects@1.15.11(debug@4.3.7): + follow-redirects@1.16.0(debug@4.3.7(supports-color@8.1.1)): optionalDependencies: debug: 4.3.7(supports-color@8.1.1) @@ -8047,7 +9020,7 @@ snapshots: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.4 mime-types: 2.1.35 formdata-polyfill@4.0.10: @@ -8056,10 +9029,10 @@ snapshots: fs-constants@1.0.0: {} - fs-extra@11.3.4: + fs-extra@11.3.5: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.2.0 + jsonfile: 6.2.1 universalify: 2.0.1 fs-minipass@2.1.0: @@ -8091,7 +9064,7 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.5.0: {} + get-east-asian-width@1.6.0: {} get-func-name@2.0.2: {} @@ -8100,18 +9073,18 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-stream@6.0.1: {} @@ -8120,10 +9093,6 @@ snapshots: '@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: @@ -8163,7 +9132,7 @@ snapshots: es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 - semver: 7.7.4 + semver: 7.8.2 serialize-error: 7.0.1 globalthis@1.0.4: @@ -8192,7 +9161,7 @@ snapshots: has-unicode@2.0.1: optional: true - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -8204,6 +9173,10 @@ snapshots: dependencies: lru-cache: 10.4.3 + hosted-git-info@9.0.3: + dependencies: + lru-cache: 11.5.1 + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -8211,10 +9184,10 @@ snapshots: http-cache-semantics@4.2.0: optional: true - http-proxy-agent@4.0.1: + http-proxy-agent@4.0.1(supports-color@8.1.1): dependencies: '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 + agent-base: 6.0.2(supports-color@8.1.1) debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -8227,13 +9200,12 @@ snapshots: transitivePeerDependencies: - supports-color - https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1(supports-color@8.1.1): dependencies: - agent-base: 6.0.2 + agent-base: 6.0.2(supports-color@8.1.1) debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color - optional: true https-proxy-agent@7.0.6: dependencies: @@ -8284,7 +9256,7 @@ snapshots: ini@1.3.8: {} - ink@6.8.0(@types/react@19.2.7)(react@19.2.4): + ink@6.8.0(@types/react@19.2.7)(react@19.2.7): dependencies: '@alcalzone/ansi-tokenize': 0.2.5 ansi-escapes: 7.3.0 @@ -8295,22 +9267,22 @@ snapshots: cli-cursor: 4.0.0 cli-truncate: 5.2.0 code-excerpt: 4.0.0 - es-toolkit: 1.45.1 + es-toolkit: 1.47.0 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) + react: 19.2.7 + react-reconciler: 0.33.0(react@19.2.7) scheduler: 0.27.0 signal-exit: 3.0.7 slice-ansi: 8.0.0 stack-utils: 2.0.6 - string-width: 8.2.0 + string-width: 8.2.1 terminal-size: 4.0.1 - type-fest: 5.4.4 + type-fest: 5.7.0 widest-line: 6.0.0 wrap-ansi: 9.0.2 - ws: 8.19.0 + ws: 8.21.0 yoga-layout: 3.2.1 optionalDependencies: '@types/react': 19.2.7 @@ -8318,12 +9290,12 @@ snapshots: - bufferutil - utf-8-validate - ip-address@10.1.0: + ip-address@10.2.0: optional: true - is-accessor-descriptor@1.0.1: + is-accessor-descriptor@1.0.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-arguments@1.2.0: dependencies: @@ -8340,11 +9312,11 @@ snapshots: is-data-descriptor@1.0.1: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 - is-descriptor@1.0.3: + is-descriptor@1.0.4: dependencies: - is-accessor-descriptor: 1.0.1 + is-accessor-descriptor: 1.0.2 is-data-descriptor: 1.0.1 is-extglob@2.1.1: {} @@ -8353,7 +9325,7 @@ snapshots: is-fullwidth-code-point@5.1.0: dependencies: - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 is-generator-function@1.1.2: dependencies: @@ -8391,7 +9363,7 @@ snapshots: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 is-stream@2.0.1: {} @@ -8399,7 +9371,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 is-unicode-supported@0.1.0: {} @@ -8409,14 +9381,17 @@ snapshots: isexe@2.0.0: {} + isexe@4.0.0: + optional: true + 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): + isomorphic-ws@5.0.0(ws@8.21.0): dependencies: - ws: 8.19.0 + ws: 8.21.0 isows@1.0.7(ws@8.18.3): dependencies: @@ -8436,7 +9411,7 @@ snapshots: js-tokens@9.0.1: {} - js-yaml@4.1.1: + js-yaml@4.2.0: dependencies: argparse: 2.0.1 @@ -8461,7 +9436,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.19.0 + ws: 8.21.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -8474,7 +9449,7 @@ snapshots: json-stringify-safe@5.0.1: {} - json-with-bigint@3.5.7: {} + json-with-bigint@3.5.8: {} jsonc-parser@3.3.1: {} @@ -8483,11 +9458,11 @@ snapshots: chalk: 3.0.0 diff-match-patch: 1.0.5 - jsondiffpatch@0.7.3: + jsondiffpatch@0.7.6: dependencies: '@dmsnell/diff-match-patch': 1.1.0 - jsonfile@6.2.0: + jsonfile@6.2.1: dependencies: universalify: 2.0.1 optionalDependencies: @@ -8508,8 +9483,8 @@ snapshots: err-code: 3.0.1 iso-random-stream: 2.0.2 multiformats: 9.9.0 - node-forge: 1.3.3 - protobufjs: 6.11.4 + node-forge: 1.4.0 + protobufjs: 6.11.6 uint8arrays: 3.1.1 lilconfig@3.1.3: {} @@ -8524,7 +9499,9 @@ snapshots: lodash.camelcase@4.3.0: {} - lodash@4.17.23: {} + lodash.sortby@4.7.0: {} + + lodash@4.18.1: {} log-symbols@4.1.0: dependencies: @@ -8548,7 +9525,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.6: {} + lru-cache@11.5.1: {} lru-cache@6.0.0: dependencies: @@ -8561,23 +9538,23 @@ snapshots: make-error@1.3.6: {} - make-fetch-happen@9.1.0: + make-fetch-happen@9.1.0(supports-color@8.1.1): 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 + http-proxy-agent: 4.0.1(supports-color@8.1.1) + https-proxy-agent: 5.0.1(supports-color@8.1.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-flush: 1.0.7 minipass-pipeline: 1.2.4 negotiator: 0.6.4 promise-retry: 2.0.1 - socks-proxy-agent: 6.2.1 + socks-proxy-agent: 6.2.1(supports-color@8.1.1) ssri: 8.0.1 transitivePeerDependencies: - bluebird @@ -8616,16 +9593,16 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.15 optional: true minimatch@5.1.9: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.1.1 minimatch@9.0.9: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.1.1 minimist@1.2.8: {} @@ -8643,7 +9620,7 @@ snapshots: encoding: 0.1.13 optional: true - minipass-flush@1.0.5: + minipass-flush@1.0.7: dependencies: minipass: 3.3.6 optional: true @@ -8671,18 +9648,22 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + mkdirp-classic@0.5.3: {} mkdirp@1.0.4: {} mlkem@2.7.0: {} - mlly@1.8.1: + mlly@1.8.2: dependencies: acorn: 8.16.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.3 + ufo: 1.6.4 mocha@10.8.2: dependencies: @@ -8695,7 +9676,7 @@ snapshots: find-up: 5.0.0 glob: 8.1.0 he: 1.2.0 - js-yaml: 4.1.1 + js-yaml: 4.2.0 log-symbols: 4.1.0 minimatch: 5.1.9 ms: 2.1.3 @@ -8713,21 +9694,21 @@ snapshots: ms@2.1.3: {} - msgpackr-extract@3.0.3: + msgpackr-extract@3.0.4: 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 + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 optional: true - msgpackr@1.11.8: + msgpackr@1.11.14: optionalDependencies: - msgpackr-extract: 3.0.3 + msgpackr-extract: 3.0.4 multiformats@9.9.0: {} @@ -8743,10 +9724,10 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nan@2.25.0: + nan@2.27.0: optional: true - nanoid@3.3.11: {} + nanoid@3.3.12: {} napi-build-utils@2.0.0: {} @@ -8780,12 +9761,14 @@ snapshots: transitivePeerDependencies: - supports-color - node-abi@3.87.0: + node-abi@3.92.0: dependencies: - semver: 7.7.4 + semver: 7.8.2 node-addon-api@7.1.1: {} + node-addon-api@8.8.0: {} + node-domexception@1.0.0: {} node-fetch@2.7.0(encoding@0.1.13): @@ -8800,23 +9783,37 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - node-forge@1.3.3: {} + node-forge@1.4.0: {} node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc: 2.1.2 optional: true - node-gyp@8.4.1: + node-gyp@12.4.0: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + graceful-fs: 4.2.11 + nopt: 9.0.0 + proc-log: 6.1.0 + semver: 7.8.2 + tar: 7.5.16 + tinyglobby: 0.2.17 + undici: 6.26.0 + which: 6.0.1 + optional: true + + node-gyp@8.4.1(supports-color@8.1.1): dependencies: env-paths: 2.2.1 glob: 7.2.3 graceful-fs: 4.2.11 - make-fetch-happen: 9.1.0 + make-fetch-happen: 9.1.0(supports-color@8.1.1) nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.7.4 + semver: 7.8.2 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -8829,10 +9826,21 @@ snapshots: abbrev: 1.1.1 optional: true + nopt@9.0.0: + dependencies: + abbrev: 4.0.0 + optional: true + normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.7.4 + semver: 7.8.2 + validate-npm-package-license: 3.0.4 + + normalize-package-data@8.0.0: + dependencies: + hosted-git-info: 9.0.3 + semver: 7.8.2 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -8880,7 +9888,7 @@ snapshots: dependencies: mimic-function: 5.0.1 - ora@9.3.0: + ora@9.4.0: dependencies: chalk: 5.6.2 cli-cursor: 5.0.0 @@ -8888,8 +9896,8 @@ snapshots: 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 + stdin-discarder: 0.3.2 + string-width: 8.2.1 os-tmpdir@1.0.2: {} @@ -8925,7 +9933,7 @@ snapshots: parse-json@8.3.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.29.7 index-to-position: 1.2.0 type-fest: 4.41.0 @@ -8962,14 +9970,14 @@ snapshots: class-is: 1.1.0 libp2p-crypto: 0.21.2 multiformats: 9.9.0 - protobufjs: 6.11.4 + protobufjs: 6.11.6 uint8arrays: 3.1.1 picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} - picomatch@4.0.3: {} + picomatch@4.0.4: {} pino-abstract-transport@2.0.0: dependencies: @@ -8983,7 +9991,7 @@ snapshots: dependencies: colorette: 2.0.20 dateformat: 4.6.3 - fast-copy: 4.0.2 + fast-copy: 4.0.3 fast-safe-stringify: 2.1.1 help-me: 5.0.0 joycon: 3.1.1 @@ -9009,7 +10017,7 @@ snapshots: real-require: 0.2.0 safe-stable-stringify: 2.5.0 sonic-boom: 4.2.1 - thread-stream: 4.0.0 + thread-stream: 4.2.0 pino@9.14.0: dependencies: @@ -9023,21 +10031,21 @@ snapshots: real-require: 0.2.0 safe-stable-stringify: 2.5.0 sonic-boom: 4.2.1 - thread-stream: 3.1.0 + thread-stream: 3.2.0 pirates@4.0.7: {} pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.8.1 + mlly: 1.8.2 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): + polkadot-api@1.19.2(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(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/cli': 0.15.2(postcss@8.5.15)(tsx@4.22.4)(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 @@ -9068,19 +10076,52 @@ snapshots: - utf-8-validate - yaml + polkadot-api@1.23.3(postcss@8.5.15)(rxjs@7.8.2)(tsx@4.22.4)(yaml@2.8.2): + dependencies: + '@polkadot-api/cli': 0.18.1(postcss@8.5.15)(tsx@4.22.4)(yaml@2.8.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/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 + 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): + postcss-load-config@6.0.1(postcss@8.5.15)(tsx@4.22.4)(yaml@2.8.2): dependencies: lilconfig: 3.1.3 optionalDependencies: - postcss: 8.5.8 - tsx: 4.21.0 + postcss: 8.5.15 + tsx: 4.22.4 yaml: 2.8.2 - postcss@8.5.8: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -9092,7 +10133,7 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.87.0 + node-abi: 3.92.0 pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 @@ -9103,6 +10144,9 @@ snapshots: dependencies: parse-ms: 4.0.0 + proc-log@6.1.0: + optional: true + process-warning@5.0.0: {} promise-inflight@1.0.1: @@ -9118,38 +10162,38 @@ snapshots: proto-list@1.2.4: {} - protobufjs@6.11.4: + protobufjs@6.11.6: 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/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 + '@protobufjs/inquire': 1.1.2 '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 + '@protobufjs/utf8': 1.1.1 '@types/long': 4.0.2 - '@types/node': 25.3.5 + '@types/node': 25.9.2 long: 4.0.0 - protobufjs@7.5.4: + protobufjs@7.6.2: 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/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 + '@protobufjs/inquire': 1.1.2 '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/node': 25.3.5 + '@protobufjs/utf8': 1.1.1 + '@types/node': 25.9.2 long: 5.3.2 - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} ps-node@0.1.6: dependencies: @@ -9183,12 +10227,20 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-reconciler@0.33.0(react@19.2.4): + react-reconciler@0.33.0(react@19.2.7): dependencies: - react: 19.2.4 + react: 19.2.7 scheduler: 0.27.0 - react@19.2.4: {} + react@19.2.7: {} + + 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.7.0 + unicorn-magic: 0.4.0 read-pkg@9.0.1: dependencies: @@ -9206,12 +10258,14 @@ snapshots: readdirp@3.6.0: dependencies: - picomatch: 2.3.1 + picomatch: 2.3.2 readdirp@4.1.2: {} real-require@0.2.0: {} + real-require@1.0.0: {} + reflect-metadata@0.2.2: {} require-directory@2.1.1: {} @@ -9222,8 +10276,6 @@ snapshots: resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} - restore-cursor@4.0.0: dependencies: onetime: 5.1.2 @@ -9253,35 +10305,35 @@ snapshots: semver-compare: 1.0.0 sprintf-js: 1.1.3 - rollup@4.59.0: + rollup@4.61.1: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 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 + '@rollup/rollup-android-arm-eabi': 4.61.1 + '@rollup/rollup-android-arm64': 4.61.1 + '@rollup/rollup-darwin-arm64': 4.61.1 + '@rollup/rollup-darwin-x64': 4.61.1 + '@rollup/rollup-freebsd-arm64': 4.61.1 + '@rollup/rollup-freebsd-x64': 4.61.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.1 + '@rollup/rollup-linux-arm-musleabihf': 4.61.1 + '@rollup/rollup-linux-arm64-gnu': 4.61.1 + '@rollup/rollup-linux-arm64-musl': 4.61.1 + '@rollup/rollup-linux-loong64-gnu': 4.61.1 + '@rollup/rollup-linux-loong64-musl': 4.61.1 + '@rollup/rollup-linux-ppc64-gnu': 4.61.1 + '@rollup/rollup-linux-ppc64-musl': 4.61.1 + '@rollup/rollup-linux-riscv64-gnu': 4.61.1 + '@rollup/rollup-linux-riscv64-musl': 4.61.1 + '@rollup/rollup-linux-s390x-gnu': 4.61.1 + '@rollup/rollup-linux-x64-gnu': 4.61.1 + '@rollup/rollup-linux-x64-musl': 4.61.1 + '@rollup/rollup-openbsd-x64': 4.61.1 + '@rollup/rollup-openharmony-arm64': 4.61.1 + '@rollup/rollup-win32-arm64-msvc': 4.61.1 + '@rollup/rollup-win32-ia32-msvc': 4.61.1 + '@rollup/rollup-win32-x64-gnu': 4.61.1 + '@rollup/rollup-win32-x64-msvc': 4.61.1 fsevents: 2.3.3 rrweb-cssom@0.6.0: {} @@ -9318,7 +10370,7 @@ snapshots: semver@5.7.2: {} - semver@7.7.4: {} + semver@7.8.2: {} serialize-error@7.0.1: dependencies: @@ -9384,7 +10436,7 @@ snapshots: smoldot@2.0.26: dependencies: - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -9392,31 +10444,38 @@ snapshots: smoldot@2.0.39: dependencies: - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate - socks-proxy-agent@6.2.1: + smoldot@2.0.40: + dependencies: + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + socks-proxy-agent@6.2.1(supports-color@8.1.1): dependencies: - agent-base: 6.0.2 + agent-base: 6.0.2(supports-color@8.1.1) debug: 4.3.7(supports-color@8.1.1) - socks: 2.8.7 + socks: 2.8.9 transitivePeerDependencies: - supports-color optional: true - socks@2.8.7: + socks@2.8.9: dependencies: - ip-address: 10.1.0 + ip-address: 10.2.0 smart-buffer: 4.2.0 optional: true - solc@0.8.21(debug@4.3.7): + solc@0.8.21(debug@4.3.7(supports-color@8.1.1)): dependencies: command-exists: 1.2.9 commander: 8.3.0 - follow-redirects: 1.15.11(debug@4.3.7) + follow-redirects: 1.16.0(debug@4.3.7(supports-color@8.1.1)) js-sha3: 0.8.0 memorystream: 0.3.1 semver: 5.7.2 @@ -9436,6 +10495,10 @@ snapshots: source-map@0.7.6: {} + 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 @@ -9458,25 +10521,34 @@ snapshots: sql-highlight@6.1.0: {} - sqlite3@5.1.7: + sqlite3@5.1.7(supports-color@8.1.1): 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 + node-gyp: 8.4.1(supports-color@8.1.1) transitivePeerDependencies: - bluebird - supports-color + sqlite3@6.0.1: + dependencies: + bindings: 1.5.0 + node-addon-api: 8.8.0 + prebuild-install: 7.1.3 + tar: 7.5.16 + optionalDependencies: + node-gyp: 12.4.0 + ssh2@1.17.0: dependencies: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 optionalDependencies: cpu-features: 0.0.10 - nan: 2.25.0 + nan: 2.27.0 ssri@8.0.1: dependencies: @@ -9491,7 +10563,7 @@ snapshots: std-env@3.10.0: {} - stdin-discarder@0.3.1: {} + stdin-discarder@0.3.2: {} string-width@4.2.3: dependencies: @@ -9508,12 +10580,12 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 - string-width@8.2.0: + string-width@8.2.1: dependencies: - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 string_decoder@1.3.0: @@ -9549,7 +10621,7 @@ snapshots: lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 supports-color@7.2.0: @@ -9592,6 +10664,14 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 + tar@7.5.16: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + terminal-size@4.0.1: {} thenify-all@1.6.0: @@ -9602,13 +10682,13 @@ snapshots: dependencies: any-promise: 1.3.0 - thread-stream@3.1.0: + thread-stream@3.2.0: dependencies: real-require: 0.2.0 - thread-stream@4.0.0: + thread-stream@4.2.0: dependencies: - real-require: 0.2.0 + real-require: 1.0.0 through@2.3.8: {} @@ -9618,10 +10698,10 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.15: + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinypool@1.1.1: {} @@ -9631,13 +10711,13 @@ snapshots: tmp-promise@3.0.3: dependencies: - tmp: 0.2.5 + tmp: 0.2.7 tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - tmp@0.2.5: {} + tmp@0.2.7: {} to-buffer@1.2.2: dependencies: @@ -9651,8 +10731,6 @@ snapshots: toml@3.0.0: {} - toml@https://codeload.github.com/pepoviola/toml-node/tar.gz/5e17114f1af5b5b70e4f2ec10cd007623c928988: {} - totalist@3.0.1: {} tough-cookie@4.1.4: @@ -9664,6 +10742,10 @@ snapshots: tr46@0.0.3: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + tr46@5.1.1: dependencies: punycode: 2.3.1 @@ -9672,14 +10754,14 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@24.12.0)(typescript@5.8.3): + ts-node@10.9.2(@types/node@24.13.1)(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 + '@types/node': 24.13.1 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -9690,14 +10772,14 @@ snapshots: 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): + ts-node@10.9.2(@types/node@25.9.2)(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 + '@types/node': 25.9.2 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -9716,27 +10798,55 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): + tsup@8.5.0(postcss@8.5.15)(tsx@4.22.4)(typescript@5.9.3)(yaml@2.8.2): + 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.15)(tsx@4.22.4)(yaml@2.8.2) + resolve-from: 5.0.0 + rollup: 4.61.1 + source-map: 0.8.0-beta.0 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.15 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + + tsup@8.5.1(postcss@8.5.15)(tsx@4.22.4)(typescript@5.9.3)(yaml@2.8.2): dependencies: - bundle-require: 5.1.0(esbuild@0.27.3) + bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 debug: 4.4.3 - esbuild: 0.27.3 + esbuild: 0.27.7 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) + postcss-load-config: 6.0.1(postcss@8.5.15)(tsx@4.22.4)(yaml@2.8.2) resolve-from: 5.0.0 - rollup: 4.59.0 + rollup: 4.61.1 source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.5.8 + postcss: 8.5.15 typescript: 5.9.3 transitivePeerDependencies: - jiti @@ -9744,10 +10854,9 @@ snapshots: - tsx - yaml - tsx@4.21.0: + tsx@4.22.4: dependencies: - esbuild: 0.27.3 - get-tsconfig: 4.13.6 + esbuild: 0.28.0 optionalDependencies: fsevents: 2.3.3 @@ -9763,7 +10872,7 @@ snapshots: type-fest@4.41.0: {} - type-fest@5.4.4: + type-fest@5.7.0: dependencies: tagged-tag: 1.0.0 @@ -9773,13 +10882,37 @@ snapshots: 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)): + typeorm@0.3.30(sqlite3@5.1.7(supports-color@8.1.1))(ts-node@10.9.2(@types/node@25.9.2)(typescript@5.8.3)): + dependencies: + '@sqltools/formatter': 1.2.5 + ansis: 4.3.1 + app-root-path: 3.1.0 + buffer: 6.0.3 + dayjs: 1.11.21 + 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.1 + yargs: 17.7.2 + optionalDependencies: + sqlite3: 5.1.7(supports-color@8.1.1) + ts-node: 10.9.2(@types/node@25.9.2)(typescript@5.8.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + typeorm@0.3.30(sqlite3@6.0.1)(ts-node@10.9.2(@types/node@25.9.2)(typescript@5.8.3)): dependencies: '@sqltools/formatter': 1.2.5 - ansis: 4.2.0 + ansis: 4.3.1 app-root-path: 3.1.0 buffer: 6.0.3 - dayjs: 1.11.19 + dayjs: 1.11.21 debug: 4.4.3 dedent: 1.7.2 dotenv: 16.6.1 @@ -9788,11 +10921,11 @@ snapshots: sha.js: 2.4.12 sql-highlight: 6.1.0 tslib: 2.8.1 - uuid: 11.1.0 + uuid: 11.1.1 yargs: 17.7.2 optionalDependencies: - sqlite3: 5.1.7 - ts-node: 10.9.2(@types/node@25.3.5)(typescript@5.8.3) + sqlite3: 6.0.1 + ts-node: 10.9.2(@types/node@25.9.2)(typescript@5.8.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -9801,7 +10934,7 @@ snapshots: typescript@5.9.3: {} - ufo@1.6.3: {} + ufo@1.6.4: {} uint8arrays@3.1.1: dependencies: @@ -9809,16 +10942,21 @@ snapshots: undici-types@6.19.8: {} - undici-types@7.16.0: {} - undici-types@7.18.2: {} - undici@7.22.0: {} + undici-types@7.24.6: {} + + undici@6.26.0: + optional: true + + undici@7.27.2: {} unicorn-magic@0.1.0: {} unicorn-magic@0.3.0: {} + unicorn-magic@0.4.0: {} + unique-filename@1.1.1: dependencies: unique-slug: 2.0.2 @@ -9848,11 +10986,11 @@ snapshots: is-arguments: 1.2.0 is-generator-function: 1.1.2 is-typed-array: 1.1.15 - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 uuid@10.0.0: {} - uuid@11.1.0: {} + uuid@11.1.1: {} v8-compile-cache-lib@3.0.1: {} @@ -9895,13 +11033,34 @@ snapshots: - utf-8-validate - zod - vite-node@3.2.4(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@24.13.1)(tsx@4.22.4)(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.5(@types/node@24.13.1)(tsx@4.22.4)(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.9.2)(tsx@4.22.4)(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) + vite: 7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -9916,13 +11075,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0): 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) + vite: 7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - jiti @@ -9937,40 +11096,54 @@ snapshots: - tsx - yaml - vite@7.3.1(@types/node@24.12.0)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.5(@types/node@24.13.1)(tsx@4.22.4)(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 + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.61.1 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 24.12.0 + '@types/node': 24.13.1 fsevents: 2.3.3 - tsx: 4.21.0 + tsx: 4.22.4 yaml: 2.8.2 - vite@7.3.1(@types/node@25.3.5)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.5(@types/node@25.9.2)(tsx@4.22.4)(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 + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.61.1 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.3.5 + '@types/node': 25.9.2 fsevents: 2.3.3 - tsx: 4.21.0 + tsx: 4.22.4 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): + vite@7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0): + dependencies: + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.61.1 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 25.9.2 + fsevents: 2.3.3 + tsx: 4.22.4 + yaml: 2.9.0 + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.13.1)(@vitest/ui@3.2.6)(jsdom@23.2.0)(tsx@4.22.4)(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/mocker': 3.2.4(vite@7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 3.2.6 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 @@ -9980,20 +11153,20 @@ snapshots: expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 3.10.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 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) + vite: 7.3.5(@types/node@24.13.1)(tsx@4.22.4)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@24.13.1)(tsx@4.22.4)(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) + '@types/node': 24.13.1 + '@vitest/ui': 3.2.6(vitest@3.2.4) jsdom: 23.2.0 transitivePeerDependencies: - jiti @@ -10009,12 +11182,12 @@ snapshots: - 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): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.9.2)(@vitest/ui@3.2.4)(jsdom@23.2.0)(tsx@4.22.4)(yaml@2.9.0): 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/mocker': 3.2.4(vite@7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 3.2.6 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 @@ -10024,20 +11197,20 @@ snapshots: expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 3.10.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 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) + vite: 7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0) + vite-node: 3.2.4(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0) 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) + '@types/node': 25.9.2 + '@vitest/ui': 3.2.4(vitest@3.2.4) jsdom: 23.2.0 transitivePeerDependencies: - jiti @@ -10053,12 +11226,12 @@ snapshots: - 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): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.9.2)(@vitest/ui@3.2.6)(jsdom@23.2.0)(tsx@4.22.4)(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/mocker': 3.2.4(vite@7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 3.2.6 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 @@ -10068,20 +11241,20 @@ snapshots: expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 3.10.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 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) + vite: 7.3.5(@types/node@25.9.2)(tsx@4.22.4)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.9.2)(tsx@4.22.4)(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) + '@types/node': 25.9.2 + '@vitest/ui': 3.2.6(vitest@3.2.4) jsdom: 23.2.0 transitivePeerDependencies: - jiti @@ -10252,11 +11425,11 @@ snapshots: web3-providers-ws@4.0.8: dependencies: '@types/ws': 8.5.3 - isomorphic-ws: 5.0.0(ws@8.19.0) + isomorphic-ws: 5.0.0(ws@8.21.0) web3-errors: 1.3.1 web3-types: 1.10.0 web3-utils: 4.3.3 - ws: 8.19.0 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -10356,6 +11529,8 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: @@ -10374,10 +11549,16 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-typed-array@1.1.20: + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 for-each: 0.3.5 get-proto: 1.0.1 @@ -10388,6 +11569,11 @@ snapshots: dependencies: isexe: 2.0.0 + which@6.0.1: + dependencies: + isexe: 4.0.0 + optional: true + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -10400,7 +11586,7 @@ snapshots: widest-line@6.0.0: dependencies: - string-width: 8.2.0 + string-width: 8.2.1 window-size@1.1.1: dependencies: @@ -10459,7 +11645,7 @@ snapshots: ws@8.18.3: {} - ws@8.19.0: {} + ws@8.21.0: {} xml-name-validator@5.0.0: {} @@ -10469,8 +11655,12 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + yaml@2.8.2: {} + yaml@2.9.0: {} + yargs-parser@20.2.9: {} yargs-parser@21.1.1: {} diff --git a/ts-tests/pnpm-workspace.yaml b/ts-tests/pnpm-workspace.yaml index 856299a3ed..be232d6643 100644 --- a/ts-tests/pnpm-workspace.yaml +++ b/ts-tests/pnpm-workspace.yaml @@ -1,6 +1,21 @@ packages: - "**" +overrides: + toml: 3.0.0 + +strictDepBuilds: false + +allowBuilds: + '@biomejs/biome': set this to true or false + '@parcel/watcher': set this to true or false + cpu-features: set this to true or false + esbuild: set this to true or false + msgpackr-extract: set this to true or false + protobufjs: set this to true or false + sqlite3: set this to true or false + ssh2: set this to true or false + onlyBuiltDependencies: - '@biomejs/biome' - '@chainsafe/blst' diff --git a/ts-tests/scripts/build-spec.sh b/ts-tests/scripts/build-spec.sh index 8ef4e40b96..9356b5fc5c 100755 --- a/ts-tests/scripts/build-spec.sh +++ b/ts-tests/scripts/build-spec.sh @@ -4,6 +4,8 @@ set -e cd $(dirname $0)/.. +# Clean vitest cache, so the tests order are the same on CI and locally +rm -rf node_modules/.vite/vitest 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-ink-types.sh b/ts-tests/scripts/generate-ink-types.sh new file mode 100755 index 0000000000..ede5fb5765 --- /dev/null +++ b/ts-tests/scripts/generate-ink-types.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +# Add ink contract metadata to polkadot-api descriptors. +# Requires the bittensor ink contract json file available in specs folder. +# +# Usage: +# ./scripts/generate-ink-types.sh +# +set -euo pipefail + +DESCRIPTORS_DIR="./.papi/contracts" +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 "==> Generating Ink contract types..." + pnpm generate-ink-types + + echo "==> Done generating Ink contract types." + exit 0 +else + echo "==> Types are up-to-date, nothing to do." +fi diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-batched-all-buys.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-all-buys.ts new file mode 100644 index 0000000000..1d2bf9b4a8 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-all-buys.ts @@ -0,0 +1,99 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { buildSignedOrder, FAR_FUTURE, filterEvents, registerLimitOrderTypes } from "../../../../utils/limit-orders.js"; + +// execute_batched_orders — all-buy batch. Own subnet, own file. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BATCH_BUY", + title: "execute_batched_orders — all-buy batch", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + }); + + it({ + id: "T01", + title: "all buyers receive alpha and GroupExecutionSummary is emitted", + test: async () => { + const aliceStakeBefore = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const bobStakeBefore = await devGetAlphaStake(polkadotJs, bobHotKey.address, bob.address, netuid); + + const orderAlice = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(50), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const orderBob = buildSignedOrder(polkadotJs, { + signer: bob, + hotkey: bobHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(50), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: bob.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeBatchedOrders(netuid, [orderAlice, orderBob]) + .signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(2); + expect(filterEvents(events, "GroupExecutionSummary").length).toBe(1); + + const aliceStakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(aliceStakeAfter).toBeGreaterThan(aliceStakeBefore); + + const bobStakeAfter = await devGetAlphaStake(polkadotJs, bobHotKey.address, bob.address, netuid); + expect(bobStakeAfter).toBeGreaterThan(bobStakeBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-batched-all-sells.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-all-sells.ts new file mode 100644 index 0000000000..9ce3fa0c2e --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-all-sells.ts @@ -0,0 +1,105 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devAddStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { buildSignedOrder, FAR_FUTURE, filterEvents, registerLimitOrderTypes } from "../../../../utils/limit-orders.js"; + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BATCH_SELL", + title: "execute_batched_orders — all-sell batch", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + + // Stake alpha for both sellers + await devAddStake(polkadotJs, context, alice, aliceHotKey.address, netuid, tao(200)); + await devAddStake(polkadotJs, context, bob, bobHotKey.address, netuid, tao(200)); + }); + + it({ + id: "T01", + title: "all sellers receive TAO and GroupExecutionSummary is emitted", + test: async () => { + const aliceTaoBefore = ( + (await polkadotJs.query.system.account(alice.address)) as any + ).data.free.toBigInt(); + const bobTaoBefore = ((await polkadotJs.query.system.account(bob.address)) as any).data.free.toBigInt(); + + const orderAlice = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "TakeProfit", + amount: tao(50), + limitPrice: 1_000_000_000n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const orderBob = buildSignedOrder(polkadotJs, { + signer: bob, + hotkey: bobHotKey.address, + netuid, + orderType: "TakeProfit", + amount: tao(50), + limitPrice: 1_000_000_000n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: bob.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeBatchedOrders(netuid, [orderAlice, orderBob]) + .signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(2); + expect(filterEvents(events, "GroupExecutionSummary").length).toBe(1); + + const aliceTaoAfter = ( + (await polkadotJs.query.system.account(alice.address)) as any + ).data.free.toBigInt(); + const bobTaoAfter = ((await polkadotJs.query.system.account(bob.address)) as any).data.free.toBigInt(); + + expect(aliceTaoAfter).toBeGreaterThan(aliceTaoBefore); + expect(bobTaoAfter).toBeGreaterThan(bobTaoBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-batched-fees.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-fees.ts new file mode 100644 index 0000000000..48be9461c4 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-fees.ts @@ -0,0 +1,168 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { generateKeyringPair, tao } from "../../../../utils"; +import { + devForceSetBalance, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + PERBILL_ONE_PERCENT, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Batched buy orders with fee recipients — own file, hits pool. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BATCH_FEES", + title: "execute_batched_orders — fee collection", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + }); + + it({ + id: "T01", + title: "unique fee recipients each receive their own fee", + test: async () => { + const feeRecipient1 = generateKeyringPair(); + const feeRecipient2 = generateKeyringPair(); + + const r1Before = ( + (await polkadotJs.query.system.account(feeRecipient1.address)) as any + ).data.free.toBigInt(); + const r2Before = ( + (await polkadotJs.query.system.account(feeRecipient2.address)) as any + ).data.free.toBigInt(); + + const orderAlice = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(100), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: PERBILL_ONE_PERCENT, + feeRecipient: feeRecipient1.address, + }); + + const orderBob = buildSignedOrder(polkadotJs, { + signer: bob, + hotkey: bobHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(100), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: PERBILL_ONE_PERCENT, + feeRecipient: feeRecipient2.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeBatchedOrders(netuid, [orderAlice, orderBob]) + .signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(2); + + const r1After = ( + (await polkadotJs.query.system.account(feeRecipient1.address)) as any + ).data.free.toBigInt(); + const r2After = ( + (await polkadotJs.query.system.account(feeRecipient2.address)) as any + ).data.free.toBigInt(); + + // Both recipients must have received some fee + expect(r1After).toBeGreaterThan(r1Before); + expect(r2After).toBeGreaterThan(r2Before); + }, + }); + + it({ + id: "T02", + title: "shared fee recipient receives aggregated fee", + test: async () => { + const sharedRecipient = generateKeyringPair(); + + const recipientBefore = ( + (await polkadotJs.query.system.account(sharedRecipient.address)) as any + ).data.free.toBigInt(); + + const orderAlice = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(100), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: PERBILL_ONE_PERCENT, + feeRecipient: sharedRecipient.address, + }); + + const orderBob = buildSignedOrder(polkadotJs, { + signer: bob, + hotkey: bobHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(100), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: PERBILL_ONE_PERCENT, + feeRecipient: sharedRecipient.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeBatchedOrders(netuid, [orderAlice, orderBob]) + .signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(2); + + const recipientAfter = ( + (await polkadotJs.query.system.account(sharedRecipient.address)) as any + ).data.free.toBigInt(); + + // Should have received fees from both orders in a single transfer + const expectedFee = tao(100) / 100n + tao(100) / 100n; // 1% * 2 + expect(recipientAfter - recipientBefore).toBe(expectedFee); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-batched-hardfail.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-hardfail.ts new file mode 100644 index 0000000000..f36f845efe --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-hardfail.ts @@ -0,0 +1,156 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { buildSignedOrder, FAR_FUTURE, registerLimitOrderTypes } from "../../../../utils/limit-orders.js"; + +// Hard-fail cases for execute_batched_orders — no pool interaction needed, +// all batches fail before reaching the swap step. Single subnet is fine. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BATCH_HARDFAIL", + title: "execute_batched_orders — hard-fail conditions", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + }); + + it({ + id: "T01", + title: "batch fails entirely when one order has an invalid signature", + test: async () => { + const valid = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const badSig = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(2), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + // Tamper after signing — signature now covers different bytes + const tampered = { + ...badSig, + order: { V1: { ...badSig.order.V1, amount: tao(999) } }, + }; + + const { + result: [attempt], + } = await context.createBlock([ + await polkadotJs.tx.limitOrders.executeBatchedOrders(netuid, [valid, tampered]).signAsync(alice), + ]); + + // The whole extrinsic should fail — hard-fail on invalid signature + expect(attempt.successful).toEqual(false); + expect(attempt.error.name).toEqual("InvalidSignature"); + }, + }); + + it({ + id: "T02", + title: "batch fails when one order targets a different netuid", + test: async () => { + const correct = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const wrongNetuid = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid: netuid + 1, // different subnet + orderType: "LimitBuy", + amount: tao(2), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const { + result: [attempt], + } = await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeBatchedOrders(netuid, [correct, wrongNetuid]) + .signAsync(alice), + ]); + + expect(attempt.successful).toEqual(false); + expect(attempt.error.name).toEqual("OrderNetUidMismatch"); + }, + }); + + it({ + id: "T03", + title: "root netuid (0) as batch parameter fails immediately", + test: async () => { + const order = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid: 0, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const { + result: [attempt], + } = await context.createBlock([ + await polkadotJs.tx.limitOrders.executeBatchedOrders(0, [order]).signAsync(alice), + ]); + + expect(attempt.successful).toEqual(false); + expect(attempt.error.name).toEqual("RootNetUidNotAllowed"); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-batched-mixed-buy-dominant.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-mixed-buy-dominant.ts new file mode 100644 index 0000000000..ed846b0b07 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-mixed-buy-dominant.ts @@ -0,0 +1,124 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devAddStake, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + computeNetAmount, + FAR_FUTURE, + filterEvents, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Buy-dominant mixed batch — net buy hits the pool. Own file. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BATCH_MIX_BUY", + title: "execute_batched_orders — buy-dominant mixed batch", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + + // Bob sells, needs alpha + await devAddStake(polkadotJs, context, bob, bobHotKey.address, netuid, tao(200)); + }); + + it({ + id: "T01", + title: "buy side dominates: both orders fulfilled, net buy hits pool", + test: async () => { + const aliceStakeBefore = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const bobTaoBefore = ((await polkadotJs.query.system.account(bob.address)) as any).data.free.toBigInt(); + + // Alice buys 200 TAO worth, Bob sells 10 alpha (~10 TAO equiv) + // → net buy ~190 TAO hits the pool + const buyOrder = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(200), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const sellOrder = buildSignedOrder(polkadotJs, { + signer: bob, + hotkey: bobHotKey.address, + netuid, + orderType: "TakeProfit", + amount: tao(10), + limitPrice: 1_000_000_000n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: bob.address, + }); + + // Read price before the swap — pallet uses pre-swap price for netting + const expectedNetAmount = await computeNetAmount(polkadotJs, netuid, tao(200), tao(10), "Buy"); + + await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeBatchedOrders(netuid, [buyOrder, sellOrder]) + .signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(2); + + const summary = filterEvents(events, "GroupExecutionSummary"); + expect(summary.length).toBe(1); + const summaryData = summary[0].event.data; + // net_side should be Buy (residual TAO sent to pool) + expect(summaryData[1].type).toBe("Buy"); + // net_amount matches buy_tao - alpha_to_tao(sell_alpha, price) + const netAmountDiff = summaryData[2].toBigInt() - expectedNetAmount; + expect(netAmountDiff < 0n ? -netAmountDiff : netAmountDiff).toBeLessThanOrEqual(10n); + // actual_out > 0 proves the pool returned alpha + expect(summaryData[3].toBigInt()).toBeGreaterThan(0n); + + const aliceStakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(aliceStakeAfter).toBeGreaterThan(aliceStakeBefore); + + const bobTaoAfter = ((await polkadotJs.query.system.account(bob.address)) as any).data.free.toBigInt(); + expect(bobTaoAfter).toBeGreaterThan(bobTaoBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-batched-mixed-sell-dominant.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-mixed-sell-dominant.ts new file mode 100644 index 0000000000..b4eb8b19d2 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-mixed-sell-dominant.ts @@ -0,0 +1,122 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devAddStake, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + computeNetAmount, + FAR_FUTURE, + filterEvents, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BATCH_MIX_SELL", + title: "execute_batched_orders — sell-dominant mixed batch", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + + // Bob sells a large amount, needs alpha + await devAddStake(polkadotJs, context, bob, bobHotKey.address, netuid, tao(500)); + }); + + it({ + id: "T01", + title: "sell side dominates: both orders fulfilled, net sell hits pool", + test: async () => { + const aliceStakeBefore = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const bobTaoBefore = ((await polkadotJs.query.system.account(bob.address)) as any).data.free.toBigInt(); + + // Alice buys 10 TAO, Bob sells 200 alpha (~200 TAO equiv) + // → net sell ~190 alpha hits the pool + const buyOrder = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(10), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const sellOrder = buildSignedOrder(polkadotJs, { + signer: bob, + hotkey: bobHotKey.address, + netuid, + orderType: "TakeProfit", + amount: tao(200), + limitPrice: 1_000_000_000n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: bob.address, + }); + + // Read price before the swap — pallet uses pre-swap price for netting + const expectedNetAmount = await computeNetAmount(polkadotJs, netuid, tao(10), tao(200), "Sell"); + + await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeBatchedOrders(netuid, [buyOrder, sellOrder]) + .signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(2); + + const summary = filterEvents(events, "GroupExecutionSummary"); + expect(summary.length).toBe(1); + const summaryData = summary[0].event.data; + // net_side should be Sell (residual alpha sent to pool) + expect(summaryData[1].type).toBe("Sell"); + // net_amount matches sell_alpha - tao_to_alpha(buy_tao, price) + const netAmountDiff = summaryData[2].toBigInt() - expectedNetAmount; + expect(netAmountDiff < 0n ? -netAmountDiff : netAmountDiff).toBeLessThanOrEqual(10n); + // actual_out > 0 proves the pool returned TAO + expect(summaryData[3].toBigInt()).toBeGreaterThan(0n); + + const aliceStakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(aliceStakeAfter).toBeGreaterThan(aliceStakeBefore); + + const bobTaoAfter = ((await polkadotJs.query.system.account(bob.address)) as any).data.free.toBigInt(); + expect(bobTaoAfter).toBeGreaterThan(bobTaoBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-batched-partial-fill.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-partial-fill.ts new file mode 100644 index 0000000000..6d1a4637e9 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-batched-partial-fill.ts @@ -0,0 +1,142 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + getOrderStatus, + getPartiallyFilledAmount, + orderId, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Tests for partial fill via execute_batched_orders. +// Same semantics as the execute_orders variant: the signed VersionedOrder +// payload is reused unchanged; only partial_fill on the envelope changes. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BATCH_PARTIAL_FILL", + title: "execute_batched_orders — partial fill", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + }); + + it({ + id: "T01", + title: "first batched partial fill sets status to PartiallyFilled", + test: async () => { + const orderAmount = tao(100); + const firstFill = Number(tao(50)); + + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: orderAmount, + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + relayer: [alice.address], + partialFillsEnabled: true, + }); + + const id = orderId(polkadotJs, signed.order); + + // Submit first partial fill (50 out of 100 TAO) via execute_batched_orders. + const firstEnvelope = { ...signed, partial_fill: firstFill }; + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeBatchedOrders(netuid, [firstEnvelope]).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + expect(filterEvents(events, "OrderSkipped").length).toBe(0); + + expect(await getOrderStatus(polkadotJs, id)).toBe("PartiallyFilled"); + const filled = await getPartiallyFilledAmount(polkadotJs, id); + expect(filled).toBe(BigInt(firstFill)); + + // Alpha stake should have increased from the partial buy. + const stakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(stakeAfter).toBeGreaterThan(0n); + }, + }); + + it({ + id: "T02", + title: "second batched partial fill completing the order sets status to Fulfilled", + test: async () => { + const orderAmount = tao(200); + const firstFill = Number(tao(100)); + const secondFill = Number(tao(100)); + + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: orderAmount, + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + relayer: [alice.address], + partialFillsEnabled: true, + }); + + const id = orderId(polkadotJs, signed.order); + + // First fill: 100 / 200. + const firstEnvelope = { ...signed, partial_fill: firstFill }; + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeBatchedOrders(netuid, [firstEnvelope]).signAsync(alice), + ]); + + expect(await getOrderStatus(polkadotJs, id)).toBe("PartiallyFilled"); + expect(await getPartiallyFilledAmount(polkadotJs, id)).toBe(BigInt(firstFill)); + + // Second fill: the remaining 100 — completes the order. + const secondEnvelope = { ...signed, partial_fill: secondFill }; + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeBatchedOrders(netuid, [secondEnvelope]).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + + expect(await getOrderStatus(polkadotJs, id)).toBe("Fulfilled"); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-cancel-order.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-cancel-order.ts new file mode 100644 index 0000000000..11c72eaf12 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-cancel-order.ts @@ -0,0 +1,145 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao } from "../../../../utils"; +import { devForceSetBalance, devExecuteOrders } from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + getOrderStatus, + orderId, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_CANCEL", + title: "cancel_order", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let bob: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + bob = context.keyring.bob; + + registerLimitOrderTypes(polkadotJs); + await devForceSetBalance(polkadotJs, context, alice.address, tao(1_000)); + }); + + it({ + id: "T01", + title: "signer can cancel their own order", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: alice.address, + netuid, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: BigInt(2_000_000_000), + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const tx = polkadotJs.tx.limitOrders.cancelOrder(signed.order); + await context.createBlock([await tx.signAsync(alice)]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderCancelled").length).toBe(1); + + const id = orderId(polkadotJs, signed.order); + expect(await getOrderStatus(polkadotJs, id)).toBe("Cancelled"); + }, + }); + + it({ + id: "T02", + title: "non-signer cannot cancel another account's order", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: alice.address, + netuid, + orderType: "LimitBuy", + amount: tao(2), + limitPrice: BigInt(2_000_000_000), + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + // Bob tries to cancel Alice's order + const tx = polkadotJs.tx.limitOrders.cancelOrder(signed.order); + const { + result: [attempt], + } = await context.createBlock([await tx.signAsync(bob)]); + + expect(attempt.successful).toEqual(false); + expect(attempt.error.name).toEqual("Unauthorized"); + }, + }); + + it({ + id: "T03", + title: "cancelling an already-cancelled order fails", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: alice.address, + netuid, + orderType: "LimitBuy", + amount: tao(3), + limitPrice: BigInt(2_000_000_000), + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const tx = polkadotJs.tx.limitOrders.cancelOrder(signed.order); + await context.createBlock([await tx.signAsync(alice)]); + + // Second cancel must fail + const tx2 = polkadotJs.tx.limitOrders.cancelOrder(signed.order); + await context.createBlock([await tx2.signAsync(alice)]); + + const events = await polkadotJs.query.system.events(); + const cancelled = filterEvents(events, "OrderCancelled"); + expect(cancelled.length).toBe(0); + }, + }); + + it({ + id: "T04", + title: "executing a cancelled order emits OrderSkipped", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: alice.address, + netuid, + orderType: "LimitBuy", + amount: tao(4), + limitPrice: BigInt(2_000_000_000), + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + // Cancel first + await context.createBlock([await polkadotJs.tx.limitOrders.cancelOrder(signed.order).signAsync(alice)]); + + // Now try to execute + await devExecuteOrders(polkadotJs, context, alice, [signed]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderSkipped").length).toBe(1); + expect(filterEvents(events, "OrderExecuted").length).toBe(0); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-fees.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-fees.ts new file mode 100644 index 0000000000..2945ecb535 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-fees.ts @@ -0,0 +1,124 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { generateKeyringPair, tao } from "../../../../utils"; +import { + devForceSetBalance, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, + devExecuteOrders, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + PERBILL_ONE_PERCENT, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Each test hits the pool so each gets its own file. +// This file covers fee collection for a buy order only. +// Sell-order fee is covered in 07. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_FEE_BUY", + title: "execute_orders — buy order fee collection", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let feeRecipient: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair(); + bob = context.keyring.bob; + feeRecipient = generateKeyringPair(); + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + // ENable subtoken + await devEnableSubtoken(polkadotJs, context, alice, netuid); + // associate hotkeys + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + }); + + it({ + id: "T01", + title: "fee recipient receives TAO for a buy order with 1% fee", + test: async () => { + const recipientBefore = ( + await polkadotJs.query.system.account(feeRecipient.address) + ).data.free.toBigInt(); + + const orderAmount = tao(100); + const expectedFee = orderAmount / 100n; // 1% + + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: orderAmount, + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: PERBILL_ONE_PERCENT, + feeRecipient: feeRecipient.address, + }); + + await devExecuteOrders(polkadotJs, context, alice, [signed]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + + const recipientAfter = ( + await polkadotJs.query.system.account(feeRecipient.address) + ).data.free.toBigInt(); + + expect(recipientAfter - recipientBefore).toBe(expectedFee); + }, + }); + + it({ + id: "T02", + title: "zero fee rate — fee recipient balance unchanged", + test: async () => { + const recipientBefore = ( + await polkadotJs.query.system.account(feeRecipient.address) + ).data.free.toBigInt(); + + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(10), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: feeRecipient.address, + }); + + await devExecuteOrders(polkadotJs, context, alice, [signed]); + + const recipientAfter = ( + await polkadotJs.query.system.account(feeRecipient.address) + ).data.free.toBigInt(); + + expect(recipientAfter).toBe(recipientBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-limit-buy.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-limit-buy.ts new file mode 100644 index 0000000000..c1d43601ae --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-limit-buy.ts @@ -0,0 +1,105 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, + devExecuteOrders, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + fetchChainId, + filterEvents, + getOrderStatus, + orderId, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// One subnet per file — this test submits a real buy order that hits the pool. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_BUY", + title: "execute_orders — LimitBuy execution", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + let chainId: bigint; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + chainId = await fetchChainId(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + // ENable subtoken + await devEnableSubtoken(polkadotJs, context, alice, netuid); + // associate hotkeys + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + }); + + it({ + id: "T01", + title: "LimitBuy executes when price condition is met", + test: async () => { + const stakeBefore = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const taoBalanceBefore = (await polkadotJs.query.system.account(alice.address)).data.free.toBigInt(); + + // TODO: why here far future? + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(100), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + chainId, + }); + + await devExecuteOrders(polkadotJs, context, alice, [signed]); + + const events = await polkadotJs.query.system.events(); + const executed = filterEvents(events, "OrderExecuted"); + expect(executed.length).toBe(1); + + // OrderId should be stored as Fulfilled + const id = orderId(polkadotJs, signed.order); + expect(await getOrderStatus(polkadotJs, id)).toBe("Fulfilled"); + + // Alpha stake should have increased + const stakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(stakeAfter).toBeGreaterThan(stakeBefore); + + // TAO balance should have decreased + const taoBalanceAfter = (await polkadotJs.query.system.account(alice.address)).data.free.toBigInt(); + expect(taoBalanceAfter).toBeLessThan(taoBalanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-partial-fill.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-partial-fill.ts new file mode 100644 index 0000000000..8e70dd358b --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-partial-fill.ts @@ -0,0 +1,146 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + getOrderStatus, + getPartiallyFilledAmount, + orderId, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Tests for partial fill via execute_orders. +// The relayer (alice) submits the same signed payload twice with different +// partial_fill values on the envelope. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_PARTIAL_FILL", + title: "execute_orders — partial fill", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + }); + + it({ + id: "T01", + title: "first partial fill sets status to PartiallyFilled", + test: async () => { + const orderAmount = tao(100); + const firstFill = Number(tao(60)); + + // Build a partial-fills-enabled order with alice as relayer. + // The signed VersionedOrder payload is the same for both fills. + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: orderAmount, + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + relayer: [alice.address], + partialFillsEnabled: true, + }); + + const id = orderId(polkadotJs, signed.order); + + // Submit first partial fill (60 out of 100 TAO). + const firstEnvelope = { ...signed, partial_fill: firstFill }; + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([firstEnvelope], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + expect(filterEvents(events, "OrderSkipped").length).toBe(0); + + expect(await getOrderStatus(polkadotJs, id)).toBe("PartiallyFilled"); + const filled = await getPartiallyFilledAmount(polkadotJs, id); + expect(filled).toBe(BigInt(firstFill)); + + // Alpha stake should have increased (partial buy occurred). + const stakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(stakeAfter).toBeGreaterThan(0n); + }, + }); + + it({ + id: "T02", + title: "second partial fill completing the order sets status to Fulfilled", + test: async () => { + const orderAmount = tao(200); + const firstFill = Number(tao(120)); + const secondFill = Number(tao(80)); + + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: orderAmount, + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + relayer: [alice.address], + partialFillsEnabled: true, + }); + + const id = orderId(polkadotJs, signed.order); + + // First fill: 120 / 200. + const firstEnvelope = { ...signed, partial_fill: firstFill }; + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([firstEnvelope], false).signAsync(alice), + ]); + + expect(await getOrderStatus(polkadotJs, id)).toBe("PartiallyFilled"); + expect(await getPartiallyFilledAmount(polkadotJs, id)).toBe(BigInt(firstFill)); + + // Second fill: the remaining 80 — completes the order. + // The signed VersionedOrder payload is identical; only partial_fill on the + // envelope changes, per the Rust design. + const secondEnvelope = { ...signed, partial_fill: secondFill }; + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([secondEnvelope], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + + expect(await getOrderStatus(polkadotJs, id)).toBe("Fulfilled"); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-sell-fees.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-sell-fees.ts new file mode 100644 index 0000000000..761af62de8 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-sell-fees.ts @@ -0,0 +1,95 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { generateKeyringPair, tao } from "../../../../utils"; +import { + devForceSetBalance, + devAddStake, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, + devExecuteOrders, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + PERBILL_ONE_PERCENT, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Sell order with fee — separate file, hits pool. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_FEE_SELL", + title: "execute_orders — sell order fee collection", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let feeRecipient: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair(); + bob = context.keyring.bob; + feeRecipient = generateKeyringPair(); + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + // ENable subtoken + await devEnableSubtoken(polkadotJs, context, alice, netuid); + // associate hotkeys + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + + // Give Alice some alpha stake to sell + await devAddStake(polkadotJs, context, alice, aliceHotKey.address, netuid, tao(1000)); + }); + + it({ + id: "T01", + title: "fee recipient receives TAO from sell order output with 1% fee", + test: async () => { + const recipientBefore = ( + await polkadotJs.query.system.account(feeRecipient.address) + ).data.free.toBigInt(); + + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "TakeProfit", + amount: tao(100), + limitPrice: 1_000_000_000n, // always met when price >= 1 TAO/alpha (×10⁹ scale) + expiry: FAR_FUTURE, + feeRate: PERBILL_ONE_PERCENT, + feeRecipient: feeRecipient.address, + }); + + await devExecuteOrders(polkadotJs, context, alice, [signed]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + + const recipientAfter = ( + await polkadotJs.query.system.account(feeRecipient.address) + ).data.free.toBigInt(); + + // Fee recipient must have received something > 0 + expect(recipientAfter).toBeGreaterThan(recipientBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-skip-conditions.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-skip-conditions.ts new file mode 100644 index 0000000000..0be5de5200 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-skip-conditions.ts @@ -0,0 +1,272 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + EXPIRED, + FAR_FUTURE, + filterEvents, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Tests in this file cover skip conditions: price-not-met, expired, bad-sig, +// root-netuid, already-processed. Pool price after devEnableSubtoken is ~1 TAO/alpha, +// so LimitBuy with limitPrice=1n is always skipped and TakeProfit with limitPrice=FAR_FUTURE too. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_SKIP", + title: "execute_orders — skip conditions", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + }); + + it({ + id: "T01", + title: "LimitBuy skipped when limit_price below current price", + test: async () => { + // limit_price = 0: current_price (1.0 TAO/alpha) > 0 → condition never met + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: 0n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([signed], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderSkipped").length).toBe(1); + expect(filterEvents(events, "OrderExecuted").length).toBe(0); + }, + }); + + it({ + id: "T02", + title: "TakeProfit skipped when price below limit_price", + test: async () => { + // limit_price = u64::MAX — price can never reach this + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "TakeProfit", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([signed], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderSkipped").length).toBe(1); + expect(filterEvents(events, "OrderExecuted").length).toBe(0); + }, + }); + + it({ + id: "T03", + title: "expired order is skipped", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: EXPIRED, + feeRate: 0, + feeRecipient: alice.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([signed], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderSkipped").length).toBe(1); + }, + }); + + it({ + id: "T04", + title: "order with invalid signature is skipped", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + // Tamper: change the amount inside the V1 inner order after signing. + // The signature now covers different bytes — validation must reject it. + const tampered = { + ...signed, + order: { V1: { ...signed.order.V1, amount: tao(999) } }, + }; + + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([tampered], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderSkipped").length).toBe(1); + }, + }); + + it({ + id: "T05", + title: "order targeting root netuid (0) is skipped", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid: 0, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([signed], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderSkipped").length).toBe(1); + }, + }); + + it({ + id: "T06", + title: "already-fulfilled order is skipped on second execution attempt", + test: async () => { + // Use a price condition that is always met (limitPrice = u64::MAX for buy) + // so the first call succeeds and fulfils the order. + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + // First execution — should succeed. + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([signed], false).signAsync(alice), + ]); + + // Second attempt — order already Fulfilled, must be skipped. + await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([signed], false).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderSkipped").length).toBe(1); + expect(filterEvents(events, "OrderExecuted").length).toBe(0); + }, + }); + + it({ + id: "T07", + title: "mixed batch: valid orders execute, invalid ones are skipped", + test: async () => { + const valid = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(4), // distinct from T06 to get a different OrderId + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const expired = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(2), + limitPrice: FAR_FUTURE, + expiry: EXPIRED, + feeRate: 0, + feeRecipient: alice.address, + }); + + const priceNotMet = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(3), + limitPrice: 0n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + await context.createBlock([ + await polkadotJs.tx.limitOrders + .executeOrders([valid, expired, priceNotMet], false) + .signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + expect(filterEvents(events, "OrderSkipped").length).toBe(2); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-stop-loss.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-stop-loss.ts new file mode 100644 index 0000000000..6f32bbb17b --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-stop-loss.ts @@ -0,0 +1,105 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devAddStake, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, + devExecuteOrders, +} from "../../../../utils/dev-helpers.js"; + +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + getOrderStatus, + orderId, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Separate file — StopLoss sell changes pool price. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_SL", + title: "execute_orders — StopLoss execution", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + // ENable subtoken + await devEnableSubtoken(polkadotJs, context, alice, netuid); + // associate hotkeys + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + + // Give Alice some alpha stake to sell + await devAddStake(polkadotJs, context, alice, aliceHotKey.address, netuid, tao(1000)); + }); + + it({ + id: "T01", + title: "StopLoss executes when price <= limit_price", + test: async () => { + const stakeBefore = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const taoBalanceBefore = (await polkadotJs.query.system.account(alice.address)).data.free.toBigInt(); + + // limit_price = 100_000_000_000 (100.0 TAO/alpha in ×10⁹ scale) — safely above the + // actual pool price on the freshly registered dynamic subnet after devAddStake(tao(1000)). + // max_slippage is unset (None) so the effective AMM floor is 0; the limit_price here + // only controls the StopLoss trigger condition, not the swap execution price. + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "StopLoss", + amount: tao(100), + limitPrice: 100_000_000_000n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + await devExecuteOrders(polkadotJs, context, alice, [signed]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + expect(filterEvents(events, "OrderSkipped").length).toBe(0); + + const id = orderId(polkadotJs, signed.order); + expect(await getOrderStatus(polkadotJs, id)).toBe("Fulfilled"); + + const stakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(stakeAfter).toBeLessThan(stakeBefore); + + const taoBalanceAfter = (await polkadotJs.query.system.account(alice.address)).data.free.toBigInt(); + expect(taoBalanceAfter).toBeGreaterThan(taoBalanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-take-profit.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-take-profit.ts new file mode 100644 index 0000000000..338bc075eb --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-execute-orders-take-profit.ts @@ -0,0 +1,104 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devAddStake, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, + devExecuteOrders, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + filterEvents, + getOrderStatus, + orderId, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; + +// Separate file because a TakeProfit sell changes pool price. + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_TP", + title: "execute_orders — TakeProfit execution", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let bob: KeyringPair; + let bobHotKey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + bob = context.keyring.bob; + bobHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devForceSetBalance(polkadotJs, context, bob.address, tao(10_000)); + + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + // ENable subtoken + await devEnableSubtoken(polkadotJs, context, alice, netuid); + // associate hotkeys + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + await devAssociateHotKey(polkadotJs, context, bob, bobHotKey.address); + + // Give Alice some alpha stake to sell + await devAddStake(polkadotJs, context, alice, aliceHotKey.address, netuid, tao(1000)); + }); + + it({ + id: "T01", + title: "TakeProfit executes when price >= limit_price", + test: async () => { + const stakeBefore = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const taoBalanceBefore = (await polkadotJs.query.system.account(alice.address)).data.free.toBigInt(); + + // limit_price = 1_000_000_000 (1.0 TAO/alpha in ×10⁹ scale) — current price after + // devAddStake(tao(1000)) is above 1.0 TAO/alpha, so this condition is always met + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "TakeProfit", + amount: tao(100), + limitPrice: 1_000_000_000n, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + await devExecuteOrders(polkadotJs, context, alice, [signed]); + + const events = await polkadotJs.query.system.events(); + expect(filterEvents(events, "OrderExecuted").length).toBe(1); + expect(filterEvents(events, "OrderSkipped").length).toBe(0); + + const id = orderId(polkadotJs, signed.order); + expect(await getOrderStatus(polkadotJs, id)).toBe("Fulfilled"); + + // Alpha stake should have decreased + const stakeAfter = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + expect(stakeAfter).toBeLessThan(stakeBefore); + + // TAO balance should have increased + const taoBalanceAfter = (await polkadotJs.query.system.account(alice.address)).data.free.toBigInt(); + expect(taoBalanceAfter).toBeGreaterThan(taoBalanceBefore); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-mevshield-execute-orders.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-mevshield-execute-orders.ts new file mode 100644 index 0000000000..daa06882f5 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-mevshield-execute-orders.ts @@ -0,0 +1,189 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { tao, generateKeyringPair } from "../../../../utils"; +import { + devForceSetBalance, + devGetAlphaStake, + devAssociateHotKey, + devEnableSubtoken, + devRegisterSubnet, + devSudoSetLockReductionInterval, +} from "../../../../utils/dev-helpers.js"; +import { + buildSignedOrder, + FAR_FUTURE, + fetchChainId, + getOrderStatus, + orderId, + registerLimitOrderTypes, +} from "../../../../utils/limit-orders.js"; +import { encryptTransaction } from "../../../../utils/shield_helpers.js"; +import { u8aToHex } from "@polkadot/util"; + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_MEVSHIELD", + title: "execute_orders via MEVShield submit_encrypted", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let netuid: number; + let chainId: bigint; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + + registerLimitOrderTypes(polkadotJs); + chainId = await fetchChainId(polkadotJs); + + // Create 3+ blocks so PendingKey is populated (needs 2 blocks for the + // AuthorKeys → NextKey → PendingKey pipeline to fill). The subsequent setup + // transactions each create additional blocks, so 2 here is sufficient. + await context.createBlock([]); + await context.createBlock([]); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + }); + + it({ + id: "T01", + title: "LimitBuy submitted via MEVShield submit_encrypted is decrypted and executed in the same block", + test: async () => { + // Use PendingKey — this is the key the current block's proposer checks against. + // NextKey is one rotation ahead; encrypting with it would require waiting an extra + // block for it to advance to PendingKey, which doesn't happen automatically in + // manual-seal mode. + const pendingKeyRaw = await polkadotJs.query.mevShield.pendingKey(); + if ((pendingKeyRaw as any).isNone) + throw new Error("MEVShield PendingKey not available — create more blocks first"); + const nextKeyBytes = (pendingKeyRaw as any).unwrap().toU8a(true); + + const signedOrder = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(100), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + relayer: null, + chainId, + }); + + // Get alice's current nonce so we can pre-sign the inner tx at nonce+1 + const aliceNonce = ( + (await polkadotJs.query.system.account(alice.address)) as any + ).nonce.toNumber() as number; + + // Sign the inner execute_orders tx at nonce+1, then get its raw bytes + const innerTx = await polkadotJs.tx.limitOrders + .executeOrders([signedOrder], false) + .signAsync(alice, { nonce: aliceNonce + 1 }); + const innerTxBytes = innerTx.toU8a(); + + // Encrypt the inner tx with the MEVShield NextKey + const ciphertext = await encryptTransaction(innerTxBytes, nextKeyBytes); + + // submit_encrypted requires a mortal era — immortal is rejected by CheckMortality. + // Anchor to the PARENT block, not the current best block. + // + // try_decode_shielded_tx is a runtime API call executed at parent_hash (block B's + // state). CheckMortality::implicit() looks up BlockHash[birth]. In block B's state, + // only blocks 0..B-1 are stored — BlockHash[B] is populated when block B+1 + // initializes. If we sign with { current: B }, birth = B and the lookup fails + // (AncientBirthBlock), check() returns Err, and try_decode_shielded_tx returns None, + // so the outer tx is included as a plain tx with no inner tx extracted. + // Anchoring to B-1 (the parent) means birth = B-1, which IS in BlockHash at block + // B's state, so implicit() succeeds and the signature verifies correctly. + const header = await polkadotJs.rpc.chain.getHeader(); + const blockNumber = header.number.toNumber() - 1; + const blockHash = header.parentHash; + const era = polkadotJs.createType("ExtrinsicEra", { current: blockNumber, period: 8 }); + + // Submit the wrapper directly to the pool (not via createBlock) so the proposer + // scans the pool naturally and runs shielded-tx detection. + const signedWrapper = await polkadotJs.tx.mevShield + .submitEncrypted(u8aToHex(ciphertext)) + .signAsync(alice, { nonce: aliceNonce, era, blockHash }); + await polkadotJs.rpc.author.submitExtrinsic(signedWrapper.toHex()); + + // Seal a block — the proposer detects the shielded tx in the pool, decrypts the + // inner execute_orders, and includes both in the same block. + await context.createBlock([]); + + // Assert the order is Fulfilled + const id = orderId(polkadotJs, signedOrder.order); + expect(await getOrderStatus(polkadotJs, id)).toBe("Fulfilled"); + }, + }); + + it({ + id: "T02", + title: "LimitBuy with a designated relayer is executed when the relayer submits via MEVShield", + test: async () => { + const relayer = generateKeyringPair("sr25519"); + await devForceSetBalance(polkadotJs, context, relayer.address, tao(100)); + + const pendingKeyRaw = await polkadotJs.query.mevShield.pendingKey(); + if ((pendingKeyRaw as any).isNone) + throw new Error("MEVShield PendingKey not available — create more blocks first"); + const pendingKeyBytes = (pendingKeyRaw as any).unwrap().toU8a(true); + + const signedOrder = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: aliceHotKey.address, + netuid, + orderType: "LimitBuy", + amount: tao(100), + limitPrice: FAR_FUTURE, + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + relayer: [relayer.address], + chainId, + }); + + // The relayer submits the encrypted execute_orders tx on Alice's behalf. + // relayerNonce+0 = outer submit_encrypted, relayerNonce+1 = inner execute_orders. + const relayerNonce = ( + (await polkadotJs.query.system.account(relayer.address)) as any + ).nonce.toNumber() as number; + + const innerTx = await polkadotJs.tx.limitOrders + .executeOrders([signedOrder], false) + .signAsync(relayer, { nonce: relayerNonce + 1 }); + const innerTxBytes = innerTx.toU8a(); + + const ciphertext = await encryptTransaction(innerTxBytes, pendingKeyBytes); + + const header = await polkadotJs.rpc.chain.getHeader(); + const blockNumber = header.number.toNumber() - 1; + const blockHash = header.parentHash; + const era = polkadotJs.createType("ExtrinsicEra", { current: blockNumber, period: 8 }); + + const signedWrapper = await polkadotJs.tx.mevShield + .submitEncrypted(u8aToHex(ciphertext)) + .signAsync(relayer, { nonce: relayerNonce, era, blockHash }); + await polkadotJs.rpc.author.submitExtrinsic(signedWrapper.toHex()); + + await context.createBlock([]); + + const id = orderId(polkadotJs, signedOrder.order); + expect(await getOrderStatus(polkadotJs, id)).toBe("Fulfilled"); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/limit-orders/test-pallet-status.ts b/ts-tests/suites/dev/subtensor/limit-orders/test-pallet-status.ts new file mode 100644 index 0000000000..1864704177 --- /dev/null +++ b/ts-tests/suites/dev/subtensor/limit-orders/test-pallet-status.ts @@ -0,0 +1,107 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { KeyringPair } from "@moonwall/util"; +import type { ApiPromise } from "@polkadot/api"; +import { tao } from "../../../../utils/balance.js"; +import { devForceSetBalance } from "../../../../utils/dev-helpers.js"; +import { buildSignedOrder, FAR_FUTURE, filterEvents, registerLimitOrderTypes } from "../../../../utils/limit-orders.js"; + +describeSuite({ + id: "DEV_SUB_LIMIT_ORDERS_STATUS", + title: "set_pallet_status", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + registerLimitOrderTypes(polkadotJs); + await devForceSetBalance(polkadotJs, context, alice.address, tao(1_000)); + }); + + it({ + id: "T01", + title: "root can disable the pallet", + test: async () => { + await context.createBlock([ + await polkadotJs.tx.sudo.sudo(polkadotJs.tx.limitOrders.setPalletStatus(false)).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + const statusEvent = filterEvents(events, "LimitOrdersPalletStatusChanged"); + expect(statusEvent.length).toBe(1); + expect(statusEvent[0].event.data[0].isTrue).toBe(false); + }, + }); + + it({ + id: "T02", + title: "execute_orders is blocked when pallet is disabled", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: alice.address, + netuid: 1, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: BigInt(2_000_000_000), + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const { + result: [attempt], + } = await context.createBlock([ + await polkadotJs.tx.limitOrders.executeOrders([signed], false).signAsync(alice), + ]); + + expect(attempt.successful).toEqual(false); + expect(attempt.error.name).toEqual("LimitOrdersDisabled"); + }, + }); + + it({ + id: "T03", + title: "execute_batched_orders is blocked when pallet is disabled", + test: async () => { + const signed = buildSignedOrder(polkadotJs, { + signer: alice, + hotkey: alice.address, + netuid: 1, + orderType: "LimitBuy", + amount: tao(1), + limitPrice: BigInt(2_000_000_000), + expiry: FAR_FUTURE, + feeRate: 0, + feeRecipient: alice.address, + }); + + const { + result: [attempt], + } = await context.createBlock([ + await polkadotJs.tx.limitOrders.executeBatchedOrders(1, [signed]).signAsync(alice), + ]); + + expect(attempt.successful).toEqual(false); + expect(attempt.error.name).toEqual("LimitOrdersDisabled"); + }, + }); + + it({ + id: "T04", + title: "root can re-enable the pallet", + test: async () => { + await context.createBlock([ + await polkadotJs.tx.sudo.sudo(polkadotJs.tx.limitOrders.setPalletStatus(true)).signAsync(alice), + ]); + + const events = await polkadotJs.query.system.events(); + const statusEvent = filterEvents(events, "LimitOrdersPalletStatusChanged"); + expect(statusEvent.length).toBe(1); + expect(statusEvent[0].event.data[0].isTrue).toBe(true); + }, + }); + }, +}); diff --git a/ts-tests/suites/dev/subtensor/staking/test-transfer-stake-rate-limit.ts b/ts-tests/suites/dev/subtensor/staking/test-transfer-stake-rate-limit.ts new file mode 100644 index 0000000000..c8bc53419e --- /dev/null +++ b/ts-tests/suites/dev/subtensor/staking/test-transfer-stake-rate-limit.ts @@ -0,0 +1,253 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { generateKeyringPair } from "../../../../utils/account"; + +const TAO = 1_000_000_000n; // 10^9 RAO per TAO +const tao = (value: number): bigint => TAO * BigInt(value); + +async function devForceSetBalance( + polkadotJs: ApiPromise, + context: any, + address: string, + amount: bigint +): Promise { + await context.createBlock([ + await polkadotJs.tx.sudo + .sudo(polkadotJs.tx.balances.forceSetBalance(address, amount)) + .signAsync(context.keyring.alice), + ]); +} + +async function devSudoSetLockReductionInterval( + polkadotJs: ApiPromise, + context: any, + alice: KeyringPair, + interval: number +): Promise { + await context.createBlock([await polkadotJs.tx.adminUtils.sudoSetLockReductionInterval(interval).signAsync(alice)]); +} + +async function devRegisterSubnet( + polkadotJs: ApiPromise, + context: any, + alice: KeyringPair, + hotkey: KeyringPair +): Promise { + await context.createBlock([await polkadotJs.tx.subtensorModule.registerNetwork(hotkey.address).signAsync(alice)]); + const events = (await polkadotJs.query.system.events()) as any; + const netuid = (events as any[]).filter((e: any) => e.event.method === "NetworkAdded")[0].event.data[0].toNumber(); + return netuid; +} + +async function devEnableSubtoken( + polkadotJs: ApiPromise, + context: any, + alice: KeyringPair, + netuid: number +): Promise { + await context.createBlock([ + await polkadotJs.tx.sudo.sudo(polkadotJs.tx.adminUtils.sudoSetSubtokenEnabled(netuid, true)).signAsync(alice), + ]); +} + +async function devAssociateHotKey( + polkadotJs: ApiPromise, + context: any, + coldkey: KeyringPair, + hotkey: string +): Promise { + await context.createBlock([await polkadotJs.tx.subtensorModule.tryAssociateHotkey(hotkey).signAsync(coldkey)]); +} + +async function devGetAlphaStake( + polkadotJs: ApiPromise, + hotkey: string, + coldkey: string, + netuid: number +): Promise { + const value = (await polkadotJs.query.subtensorModule.alphaV2(hotkey, coldkey, netuid)) as any; + const mantissa = value.mantissa; + const exponent = value.exponent; + if (exponent >= 0n) { + return BigInt(mantissa) * BigInt(10) ** BigInt(exponent); + } + return BigInt(mantissa) / BigInt(10) ** BigInt(-exponent); +} + +describeSuite({ + id: "DEV_SUB_STAKING_TRANSFER_RATE_LIMIT", + title: "staking — same-block add_stake / transfer_stake (no per-block rate limiter)", + foundationMethods: "dev", + testCases: ({ it, context }) => { + let polkadotJs: ApiPromise; + let alice: KeyringPair; + let aliceHotKey: KeyringPair; + let destinationColdkey: KeyringPair; + let netuid: number; + + beforeAll(async () => { + polkadotJs = context.polkadotJs(); + alice = context.keyring.alice; + aliceHotKey = generateKeyringPair("sr25519"); + destinationColdkey = generateKeyringPair("sr25519"); + + await devForceSetBalance(polkadotJs, context, alice.address, tao(10_000)); + // ensure destination coldkey can receive transferred stake + await devForceSetBalance(polkadotJs, context, destinationColdkey.address, tao(10_000)); + await devSudoSetLockReductionInterval(polkadotJs, context, alice, 1); + + await context.createBlock([ + await polkadotJs.tx.sudo.sudo(polkadotJs.tx.adminUtils.sudoSetNetworkRateLimit(0)).signAsync(alice), + ]); + + netuid = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + + await devEnableSubtoken(polkadotJs, context, alice, netuid); + await devAssociateHotKey(polkadotJs, context, alice, aliceHotKey.address); + }); + + it({ + id: "T01", + title: "add_stake + same-subnet transfer_stake in one block both succeed", + test: async () => { + // Both extrinsics are signed by alice, so use explicit incrementing + // nonces to land them in the same block in submission order. + const aliceNonce = ((await polkadotJs.query.system.account(alice.address)) as any).nonce.toNumber(); + + // Stake a large amount so the same-block transfer has plenty of alpha + // to move and clears the DefaultMinStake floor. + const addTx = await polkadotJs.tx.subtensorModule + .addStake(aliceHotKey.address, netuid, tao(100)) + .signAsync(alice, { nonce: aliceNonce }); + + const transferAmount = 1_000_000_000n; + const transferTx = await polkadotJs.tx.subtensorModule + .transferStake(destinationColdkey.address, aliceHotKey.address, netuid, netuid, transferAmount) + .signAsync(alice, { nonce: aliceNonce + 1 }); + + const { result } = await context.createBlock([addTx, transferTx]); + const [addAttempt, transferAttempt] = result; + + expect(addAttempt.successful).toEqual(true); + expect(transferAttempt.successful).toEqual(true); + }, + }); + + it({ + id: "T02", + title: "add_stake then transfer_stake across SEPARATE blocks both succeed", + test: async () => { + // add in its own block + const { + result: [addAttempt2], + } = await context.createBlock([ + await polkadotJs.tx.subtensorModule + .addStake(aliceHotKey.address, netuid, tao(100)) + .signAsync(alice), + ]); + expect(addAttempt2.successful).toEqual(true); + + const alphaStaked = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const transferAmount = alphaStaked / 2n; + expect(transferAmount > 0n).toEqual(true); + + // transfer in the NEXT block — same triple, succeeds + const { + result: [transferAttempt2], + } = await context.createBlock([ + await polkadotJs.tx.subtensorModule + .transferStake(destinationColdkey.address, aliceHotKey.address, netuid, netuid, transferAmount) + .signAsync(alice), + ]); + expect(transferAttempt2.successful).toEqual(true); + }, + }); + + it({ + id: "T03", + title: "two add_stake on the IDENTICAL (coldkey, hotkey, netuid) in the SAME block both succeed", + test: async () => { + const aliceNonce = ((await polkadotJs.query.system.account(alice.address)) as any).nonce.toNumber(); + + const addTx1 = await polkadotJs.tx.subtensorModule + .addStake(aliceHotKey.address, netuid, tao(10)) + .signAsync(alice, { nonce: aliceNonce }); + + const addTx2 = await polkadotJs.tx.subtensorModule + .addStake(aliceHotKey.address, netuid, tao(10)) + .signAsync(alice, { nonce: aliceNonce + 1 }); + + const { result } = await context.createBlock([addTx1, addTx2]); + const [addAttempt1, addAttempt2] = result; + + expect(addAttempt1.successful).toEqual(true); + expect(addAttempt2.successful).toEqual(true); + }, + }); + + it({ + id: "T04", + title: "remove_stake then transfer_stake on the IDENTICAL (coldkey, hotkey, netuid) in the SAME block both succeed", + test: async () => { + const { + result: [seedAdd], + } = await context.createBlock([ + await polkadotJs.tx.subtensorModule + .addStake(aliceHotKey.address, netuid, tao(100)) + .signAsync(alice), + ]); + expect(seedAdd.successful).toEqual(true); + + // Size both legs as a real fraction of available alpha so neither trips the + // DefaultMinStake floor, and their sum stays below the available balance. + const alphaStaked = await devGetAlphaStake(polkadotJs, aliceHotKey.address, alice.address, netuid); + const legAmount = alphaStaked / 4n; + expect(legAmount > 0n).toEqual(true); + + const aliceNonce = ((await polkadotJs.query.system.account(alice.address)) as any).nonce.toNumber(); + + const removeTx = await polkadotJs.tx.subtensorModule + .removeStake(aliceHotKey.address, netuid, legAmount) + .signAsync(alice, { nonce: aliceNonce }); + + const transferTx = await polkadotJs.tx.subtensorModule + .transferStake(destinationColdkey.address, aliceHotKey.address, netuid, netuid, legAmount) + .signAsync(alice, { nonce: aliceNonce + 1 }); + + const { result } = await context.createBlock([removeTx, transferTx]); + const [removeAttempt, transferAttempt] = result; + + expect(removeAttempt.successful).toEqual(true); + expect(transferAttempt.successful).toEqual(true); + }, + }); + + it({ + id: "T05", + title: "add_stake + CROSS-subnet transfer_stake in one block is no longer rate-limited (limiter removed) — it now falls through to the normal amount check", + test: async () => { + const netuid2 = await devRegisterSubnet(polkadotJs, context, alice, aliceHotKey); + await devEnableSubtoken(polkadotJs, context, alice, netuid2); + + const aliceNonce = ((await polkadotJs.query.system.account(alice.address)) as any).nonce.toNumber(); + + const addTx = await polkadotJs.tx.subtensorModule + .addStake(aliceHotKey.address, netuid, tao(100)) + .signAsync(alice, { nonce: aliceNonce }); + + const transferTx = await polkadotJs.tx.subtensorModule + .transferStake(destinationColdkey.address, aliceHotKey.address, netuid, netuid2, 1000n) + .signAsync(alice, { nonce: aliceNonce + 1 }); + + const { result } = await context.createBlock([addTx, transferTx]); + const [addAttempt, transferAttempt] = result; + + expect(addAttempt.successful).toEqual(true); + expect(transferAttempt.successful).toEqual(false); + expect(transferAttempt.error.name).not.toEqual("StakingOperationRateLimitExceeded"); + expect(transferAttempt.error.name).toEqual("AmountTooLow"); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_evm/00-evm-substrate-transfer.test.ts b/ts-tests/suites/zombienet_evm/00-evm-substrate-transfer.test.ts new file mode 100644 index 0000000000..93e245d276 --- /dev/null +++ b/ts-tests/suites/zombienet_evm/00-evm-substrate-transfer.test.ts @@ -0,0 +1,500 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { MultiAddress, subtensor } from "@polkadot-api/descriptors"; +import type { KeyringPair } from "@polkadot/keyring/types"; +import { ethers } from "ethers"; +import type { TypedApi } from "polkadot-api"; +import { Binary } from "polkadot-api"; +import { + bigintToRao, + convertH160ToSS58, + convertPublicKeyToSs58, + createEthersWallet, + disableWhiteListCheck, + ethAddressToH160, + forceSetBalance, + generateKeyringPair, + getBalance, + getEthBalance, + GWEI, + IBALANCETRANSFER_ADDRESS, + IBalanceTransferABI, + MAX_TX_FEE, + raoToEth, + sendTransaction, + ss58ToEthAddress, + ss58ToH160, + tao, + waitForFinalizedBlocks, + waitForTransactionWithRetry, + WITHDRAW_CONTRACT_ABI, + WITHDRAW_CONTRACT_BYTECODE, +} from "../../utils"; + +async function estimateTransactionCost(provider: ethers.Provider, tx: ethers.TransactionRequest): Promise { + const feeData = await provider.getFeeData(); + const estimatedGas = await provider.estimateGas(tx); + const gasPrice = feeData.gasPrice ?? feeData.maxFeePerGas; + if (gasPrice == null) { + return estimatedGas; + } + return estimatedGas * gasPrice; +} + +function expectWithinTxFee(actual: bigint, expected: bigint): void { + const diff = actual > expected ? actual - expected : expected - actual; + expect(diff).toBeLessThan(MAX_TX_FEE); +} + +async function transferAndGetFee( + wallet: ethers.Wallet, + wallet2: ethers.Wallet, + provider: ethers.Provider, + maxFeePerGas: bigint, + maxPriorityFeePerGas: bigint +): Promise { + const ethBalanceBefore = await getEthBalance(provider, wallet.address); + const tx = { + to: wallet2.address, + value: raoToEth(tao(1)).toString(), + maxPriorityFeePerGas: maxPriorityFeePerGas.toString(), + maxFeePerGas: maxFeePerGas.toString(), + gasLimit: 21000, + }; + + const txResponse = await wallet.sendTransaction(tx); + const receipt = await txResponse.wait(); + expect(receipt?.status).toEqual(1); + + const ethBalanceAfter = await getEthBalance(provider, wallet.address); + return ethBalanceBefore - ethBalanceAfter - raoToEth(tao(1)); +} + +describeSuite({ + id: "evm-substrate-transfer-basic", + title: "Basic EVM-Substrate Transfer Tests", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi; + let ethWallet: ethers.Wallet; + let ethWallet2: ethers.Wallet; + let signer: KeyringPair; + let provider: ethers.JsonRpcProvider; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + + provider = context.ethers("EVM").provider as ethers.JsonRpcProvider; + ethWallet = createEthersWallet(provider); + ethWallet2 = createEthersWallet(provider); + + signer = generateKeyringPair("sr25519"); + await forceSetBalance(api, convertPublicKeyToSs58(signer.publicKey)); + await forceSetBalance(api, convertH160ToSS58(ethWallet.address)); + await forceSetBalance(api, convertH160ToSS58(ethWallet2.address)); + await disableWhiteListCheck(api, true); + await waitForFinalizedBlocks(api, 1); + }, 300000); + + it({ + id: "T01", + title: "Can transfer token from EVM to EVM", + test: async () => { + const senderBalanceBefore = await getEthBalance(provider, ethWallet.address); + const receiverBalanceBefore = await getEthBalance(provider, ethWallet2.address); + + const transferAmount = raoToEth(tao(1)); + const tx: ethers.TransactionRequest = { + to: ethWallet2.address, + value: transferAmount, + }; + + const txFee = await estimateTransactionCost(provider, tx); + + const txResponse = await ethWallet.sendTransaction(tx); + const receipt = await txResponse.wait(); + expect(receipt).toBeDefined(); + expect(receipt!.status).toEqual(1); + + const senderBalanceAfter = await getEthBalance(provider, ethWallet.address); + const receiverBalanceAfter = await getEthBalance(provider, ethWallet2.address); + + expect(senderBalanceAfter).toEqual(senderBalanceBefore - transferAmount - txFee); + expect(receiverBalanceAfter).toEqual(receiverBalanceBefore + transferAmount); + }, + }); + + it({ + id: "T02", + title: "Can transfer token from Substrate to EVM", + test: async () => { + const ss58Address = convertH160ToSS58(ethWallet.address); + const receiverBalance = await getEthBalance(provider, ethWallet.address); + const transferBalance = tao(1); + + const tx = api.tx.Balances.transfer_keep_alive({ + value: transferBalance, + dest: MultiAddress.Id(ss58Address), + }); + await waitForTransactionWithRetry(api, tx, signer, "substrate_to_evm"); + + const receiverBalanceAfter = await getEthBalance(provider, ethWallet.address); + expect(receiverBalanceAfter).toEqual(receiverBalance + raoToEth(transferBalance)); + }, + }); + + it({ + id: "T03", + title: "Can transfer token from EVM to Substrate", + test: async () => { + const contract = new ethers.Contract(IBALANCETRANSFER_ADDRESS, IBalanceTransferABI, ethWallet); + const signerSs58 = convertPublicKeyToSs58(signer.publicKey); + + const senderBalance = await getEthBalance(provider, ethWallet.address); + const receiverBalance = await getBalance(api, signerSs58); + const transferBalance = raoToEth(tao(1)); + + const tx = await contract.transfer(signer.publicKey, { value: transferBalance.toString() }); + const receipt = await tx.wait(); + expect(receipt?.status).toEqual(1); + + await waitForFinalizedBlocks(api, 2); + + const senderBalanceAfter = await getEthBalance(provider, ethWallet.address); + const receiverBalanceAfter = await getBalance(api, signerSs58); + + expectWithinTxFee(senderBalanceAfter, senderBalance - transferBalance); + expect(receiverBalance).toEqual(receiverBalanceAfter - tao(1)); + }, + }); + + it({ + id: "T04", + title: "Transfer from EVM to substrate using evm::withdraw", + test: async () => { + const ss58Address = convertPublicKeyToSs58(signer.publicKey); + const senderBalance = await getBalance(api, ss58Address); + const ethAddress = ss58ToH160(ss58Address); + + const ethTransfer = { + to: ss58ToEthAddress(ss58Address), + value: raoToEth(tao(2)).toString(), + }; + const fundReceipt = await (await ethWallet.sendTransaction(ethTransfer)).wait(); + expect(fundReceipt?.status).toEqual(1); + + const tx = api.tx.EVM.withdraw({ address: ethAddress, value: tao(1) }); + const txFee = (await tx.getPaymentInfo(ss58Address)).partial_fee; + + await waitForTransactionWithRetry(api, tx, signer, "evm_withdraw", 5); + + const senderBalanceAfterWithdraw = await getBalance(api, ss58Address); + expect(senderBalance).toEqual(senderBalanceAfterWithdraw - tao(1) + txFee); + }, + }); + + it({ + id: "T05", + title: "Transfer from EVM to substrate using evm::call", + test: async () => { + const ss58Address = convertPublicKeyToSs58(signer.publicKey); + const ethAddress = ss58ToH160(ss58Address); + + const ethTransfer = { + to: ss58ToEthAddress(ss58Address), + value: raoToEth(tao(2)).toString(), + }; + const fundReceipt = await (await ethWallet.sendTransaction(ethTransfer)).wait(); + expect(fundReceipt?.status).toEqual(1); + + const source = ethAddress; + const target = ethAddressToH160(ethWallet.address); + const receiverBalance = await getEthBalance(provider, ethWallet.address); + + const tx = api.tx.EVM.call({ + source, + target, + value: [raoToEth(tao(1)), tao(0), tao(0), tao(0)], + gas_limit: BigInt(1000000), + max_fee_per_gas: [BigInt(10e9), BigInt(0), BigInt(0), BigInt(0)], + max_priority_fee_per_gas: undefined, + // PAPI encodes this field with the Binary codec despite the Uint8Array annotation. + input: Binary.fromText("") as unknown as Uint8Array, + nonce: undefined, + access_list: [], + authorization_list: [], + }); + + await waitForTransactionWithRetry(api, tx, signer, "evm_call", 5); + + const receiverBalanceAfterCall = await getEthBalance(provider, ethWallet.address); + expect(receiverBalanceAfterCall).toEqual(receiverBalance + raoToEth(tao(1))); + }, + }); + + it({ + id: "T06", + title: "Forward value in smart contract", + test: async () => { + const contractFactory = new ethers.ContractFactory( + WITHDRAW_CONTRACT_ABI, + WITHDRAW_CONTRACT_BYTECODE, + ethWallet + ); + const contract = await contractFactory.deploy(); + await contract.waitForDeployment(); + + const contractAddress = contract.target.toString(); + const code = await provider.getCode(contractAddress); + expect(code).toBeDefined(); + expect(code.length).toBeGreaterThan(100); + + const ethTransfer = { + to: contractAddress, + value: raoToEth(tao(2)).toString(), + }; + const fundReceipt = await (await ethWallet.sendTransaction(ethTransfer)).wait(); + expect(fundReceipt?.status).toEqual(1); + + const contractBalance = await getEthBalance(provider, contractAddress); + const callerBalance = await getEthBalance(provider, ethWallet.address); + + const contractForCall = new ethers.Contract(contractAddress, WITHDRAW_CONTRACT_ABI, ethWallet); + const withdrawTx = await contractForCall.withdraw(raoToEth(tao(1)).toString()); + const withdrawReceipt = await withdrawTx.wait(); + expect(withdrawReceipt?.status).toEqual(1); + + const contractBalanceAfterWithdraw = await getEthBalance(provider, contractAddress); + const callerBalanceAfterWithdraw = await getEthBalance(provider, ethWallet.address); + + expectWithinTxFee(callerBalanceAfterWithdraw, callerBalance + raoToEth(tao(1))); + expect(contractBalance).toEqual(contractBalanceAfterWithdraw + raoToEth(tao(1))); + }, + }); + + it({ + id: "T07", + title: "Transfer full balance", + test: async () => { + const ethBalance = await getEthBalance(provider, ethWallet.address); + const receiverBalance = await getEthBalance(provider, ethWallet2.address); + const txPrice = await estimateTransactionCost(provider, { + to: ethWallet2.address, + value: ethBalance.toString(), + }); + const finalTx = { + to: ethWallet2.address, + value: (ethBalance - txPrice).toString(), + }; + + let rejected = false; + try { + const txResponse = await ethWallet.sendTransaction(finalTx); + await txResponse.wait(); + } catch (error) { + rejected = true; + if (error instanceof Error) { + expect( + (error as { code?: string }).code === "INSUFFICIENT_FUNDS" || + error.message.includes("insufficient funds") + ).toBe(true); + } + } + expect(rejected).toBe(true); + + const receiverBalanceAfterTransfer = await getEthBalance(provider, ethWallet2.address); + expect(receiverBalanceAfterTransfer).toEqual(receiverBalance); + }, + }); + + it({ + id: "T08", + title: "Transfer more than owned balance should fail", + test: async () => { + const ethBalance = await getEthBalance(provider, ethWallet.address); + const receiverBalance = await getEthBalance(provider, ethWallet2.address); + const tx = { + to: ethWallet2.address, + value: (ethBalance + raoToEth(tao(1))).toString(), + }; + + let rejected = false; + try { + const txResponse = await ethWallet.sendTransaction(tx); + await txResponse.wait(); + } catch (error) { + rejected = true; + if (error instanceof Error) { + expect( + (error as { code?: string }).code === "INSUFFICIENT_FUNDS" || + error.message.includes("insufficient funds") + ).toBe(true); + } + } + expect(rejected).toBe(true); + + const receiverBalanceAfterTransfer = await getEthBalance(provider, ethWallet2.address); + expect(receiverBalanceAfterTransfer).toEqual(receiverBalance); + }, + }); + + it({ + id: "T09", + title: "Transfer more than u64::max in substrate equivalent should receive error response", + test: async () => { + const receiverBalance = await getEthBalance(provider, ethWallet2.address); + const oversize = raoToEth(BigInt(2) ** BigInt(64)); + + let ethRejected = false; + try { + const txResponse = await ethWallet.sendTransaction({ + to: ethWallet2.address, + value: oversize.toString(), + }); + await txResponse.wait(); + } catch (error) { + ethRejected = true; + if (error instanceof Error) { + expect( + (error as { code?: string }).code === "INSUFFICIENT_FUNDS" || + error.message.includes("insufficient funds") + ).toBe(true); + } + } + expect(ethRejected).toBe(true); + + const contract = new ethers.Contract(IBALANCETRANSFER_ADDRESS, IBalanceTransferABI, ethWallet); + let precompileRejected = false; + try { + const tx = await contract.transfer(signer.publicKey, { value: oversize.toString() }); + await tx.wait(); + } catch (error) { + precompileRejected = true; + if (error instanceof Error) { + expect(error.message.includes("revert") || error.message.includes("CALL_EXCEPTION")).toBe(true); + } + } + expect(precompileRejected).toBe(true); + + let balanceTxRejected = false; + try { + const dest = convertH160ToSS58(ethWallet2.address); + const tx = api.tx.Balances.transfer_keep_alive({ + value: bigintToRao(BigInt(2) ** BigInt(64)), + dest: MultiAddress.Id(dest), + }); + const result = await sendTransaction(tx, signer); + balanceTxRejected = !result.success; + } catch { + balanceTxRejected = true; + } + expect(balanceTxRejected).toBe(true); + + let withdrawRejected = false; + try { + const dest = ethAddressToH160(ethWallet2.address); + const tx = api.tx.EVM.withdraw({ + value: bigintToRao(BigInt(2) ** BigInt(64)), + address: dest, + }); + const result = await sendTransaction(tx, signer); + withdrawRejected = !result.success; + } catch { + withdrawRejected = true; + } + expect(withdrawRejected).toBe(true); + + let evmCallRejected = false; + try { + const source = ethAddressToH160(ethWallet.address); + const target = ethAddressToH160(ethWallet2.address); + const tx = api.tx.EVM.call({ + source, + target, + value: [raoToEth(tao(1)), tao(0), tao(0), tao(1)], + gas_limit: BigInt(1000000), + max_fee_per_gas: [BigInt(10e9), BigInt(0), BigInt(0), BigInt(0)], + max_priority_fee_per_gas: undefined, + input: Binary.fromText("") as unknown as Uint8Array, + nonce: undefined, + access_list: [], + authorization_list: [], + }); + const result = await sendTransaction(tx, signer); + evmCallRejected = !result.success; + } catch { + evmCallRejected = true; + } + expect(evmCallRejected).toBe(true); + + const receiverBalanceAfter = await getEthBalance(provider, ethWallet2.address); + expect(receiverBalanceAfter).toEqual(receiverBalance); + }, + }); + + it({ + id: "T10", + title: "Gas price should be 10 GWei", + test: async () => { + const feeData = await provider.getFeeData(); + expect(feeData.gasPrice).toEqual(BigInt(10000000000)); + }, + }); + + it({ + id: "T11", + title: "max_fee_per_gas and max_priority_fee_per_gas affect transaction fee properly", + test: async () => { + const testCases: [number, number, bigint][] = [ + [10, 0, BigInt(21000 * 10) * BigInt(1e9)], + [10, 10, BigInt(21000 * 10) * BigInt(1e9)], + [11, 0, BigInt(21000 * 10) * BigInt(1e9)], + ]; + + for (const [maxFeeGwei, maxPriorityGwei, expectedFee] of testCases) { + const actualFee = await transferAndGetFee( + ethWallet, + ethWallet2, + provider, + GWEI * BigInt(maxFeeGwei), + GWEI * BigInt(maxPriorityGwei) + ); + expect(actualFee).toEqual(expectedFee); + } + }, + }); + + it({ + id: "T12", + title: "Low max_fee_per_gas gets transaction rejected", + test: async () => { + let rejected = false; + try { + await transferAndGetFee(ethWallet, ethWallet2, provider, GWEI * BigInt(9), BigInt(0)); + } catch (error) { + rejected = true; + if (error instanceof Error) { + expect(error.message.includes("gas price less than block base fee")).toBe(true); + } + } + expect(rejected).toBe(true); + }, + }); + + it({ + id: "T13", + title: "max_fee_per_gas lower than max_priority_fee_per_gas gets transaction rejected", + test: async () => { + let rejected = false; + try { + await transferAndGetFee(ethWallet, ethWallet2, provider, GWEI * BigInt(10), GWEI * BigInt(11)); + } catch (error) { + rejected = true; + if (error instanceof Error) { + expect(error.message.includes("priorityFee cannot be more than maxFee")).toBe(true); + } + } + expect(rejected).toBe(true); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_evm/01-contract-deploy-call.test.ts b/ts-tests/suites/zombienet_evm/01-contract-deploy-call.test.ts new file mode 100644 index 0000000000..f8976bc674 --- /dev/null +++ b/ts-tests/suites/zombienet_evm/01-contract-deploy-call.test.ts @@ -0,0 +1,523 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { MultiAddress, subtensor } from "@polkadot-api/descriptors"; +import type { KeyringPair } from "@polkadot/keyring/types"; +import { u8aToHex } from "@polkadot/util"; +import { decodeAddress } from "@polkadot/util-crypto"; +import { ethers } from "ethers"; +import type { TypedApi } from "polkadot-api"; +import { + addNewSubnetwork, + ALPHA_POOL_CONTRACT_ABI, + ALPHA_POOL_CONTRACT_BYTECODE, + BRIDGE_TOKEN_CONTRACT_ABI, + BRIDGE_TOKEN_CONTRACT_BYTECODE, + burnedRegister, + convertH160ToPublicKey, + convertH160ToSS58, + convertPublicKeyToSs58, + createEthersWallet, + disableWhiteListCheck, + forceSetBalance, + generateKeyringPair, + getBalance, + getProxies, + getStake, + IPROXY_ADDRESS, + IProxyABI, + ISTAKING_V2_ADDRESS, + IStakingV2ABI, + raoToEth, + STAKE_WRAP_ABI, + STAKE_WRAP_BYTECODE, + startCall, + sudoSetLockReductionInterval, + tao, + waitForFinalizedBlocks, +} from "../../utils"; + +const DEPLOYED_BYTECODE_PREFIX = "0x60806040523480156"; + +async function expectDeployedContract(provider: ethers.Provider, contractAddress: string): Promise { + const code = await provider.getCode(contractAddress); + expect(code).toBeDefined(); + expect(code.length).toBeGreaterThan(100); + expect(code.includes(DEPLOYED_BYTECODE_PREFIX)).toBe(true); +} + +export async function getTransferCallCode( + api: TypedApi, + receiver: KeyringPair, + transferAmount: number +): Promise { + const unsignedTx = api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(convertPublicKeyToSs58(receiver.publicKey)), + value: BigInt(transferAmount), + }); + const encodedCallDataBytes = await unsignedTx.getEncodedData(); + return [...encodedCallDataBytes.asBytes()]; +} + +describeSuite({ + id: "contract-deploy-call", + title: "Contract deploy and precompile call tests", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi; + let provider: ethers.JsonRpcProvider; + let ethWallet: ethers.Wallet; + let stakeWallet: ethers.Wallet; + let proxyWallet1: ethers.Wallet; + let proxyWallet2: ethers.Wallet; + let proxyWallet3: ethers.Wallet; + let proxyWallet4: ethers.Wallet; + let pureProxyReceiver: KeyringPair; + let delegateProxyReceiver: KeyringPair; + let hotkey: KeyringPair; + let coldkey: KeyringPair; + let netuid: number; + let subnetReady = false; + let proxyWalletsReady = false; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + provider = context.ethers("EVM").provider as ethers.JsonRpcProvider; + ethWallet = createEthersWallet(provider); + await forceSetBalance(api, convertH160ToSS58(ethWallet.address)); + await disableWhiteListCheck(api, true); + await waitForFinalizedBlocks(api, 1); + }, 300000); + + async function ensureSubnetReady(): Promise { + if (subnetReady) { + return; + } + + hotkey = generateKeyringPair("sr25519"); + coldkey = generateKeyringPair("sr25519"); + + await sudoSetLockReductionInterval(api, 1); + await forceSetBalance(api, convertPublicKeyToSs58(hotkey.publicKey)); + await forceSetBalance(api, convertPublicKeyToSs58(coldkey.publicKey)); + + netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + await burnedRegister(api, netuid, convertH160ToSS58(ethWallet.address), coldkey); + await waitForFinalizedBlocks(api, 1); + subnetReady = true; + } + + async function ensureProxyWalletsReady(): Promise { + if (proxyWalletsReady) { + return; + } + + stakeWallet = createEthersWallet(provider); + proxyWallet1 = createEthersWallet(provider); + proxyWallet2 = createEthersWallet(provider); + proxyWallet3 = createEthersWallet(provider); + proxyWallet4 = createEthersWallet(provider); + pureProxyReceiver = generateKeyringPair("sr25519"); + delegateProxyReceiver = generateKeyringPair("sr25519"); + + for (const wallet of [stakeWallet, proxyWallet1, proxyWallet2, proxyWallet3, proxyWallet4]) { + await forceSetBalance(api, convertH160ToSS58(wallet.address)); + } + await waitForFinalizedBlocks(api, 1); + proxyWalletsReady = true; + } + + async function deployAndFundStakeWrap(wallet: ethers.Wallet): Promise { + const contractFactory = new ethers.ContractFactory(STAKE_WRAP_ABI, STAKE_WRAP_BYTECODE, wallet); + const contract = await contractFactory.deploy(); + await contract.waitForDeployment(); + + const txResponse = await wallet.sendTransaction({ + to: contract.target.toString(), + value: raoToEth(tao(10000)), + }); + await txResponse.wait(); + await waitForFinalizedBlocks(api, 1); + + return new ethers.Contract(contract.target.toString(), STAKE_WRAP_ABI, wallet); + } + + async function waitForPureProxyCount(delegateSs58: string, expectedCount: number): Promise { + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + const proxies = await getProxies(api, delegateSs58); + if (proxies.length === expectedCount) { + return proxies; + } + await waitForFinalizedBlocks(api, 1); + } + const proxies = await getProxies(api, delegateSs58); + expect(proxies.length).toEqual(expectedCount); + return proxies; + } + + async function waitForProxyDelegates(realSs58: string, expectedCount: number): Promise { + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + const proxies = await api.query.Proxy.Proxies.getValue(realSs58); + const delegates = proxies[0].map((proxy) => proxy.delegate); + if (delegates.length === expectedCount) { + return delegates; + } + await waitForFinalizedBlocks(api, 1); + } + const proxies = await api.query.Proxy.Proxies.getValue(realSs58); + const delegates = proxies[0].map((proxy) => proxy.delegate); + expect(delegates.length).toEqual(expectedCount); + return delegates; + } + + async function waitForBalanceIncrease( + ss58Address: string, + balanceBefore: bigint, + increase: bigint + ): Promise { + const expected = balanceBefore + increase; + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + const balance = await getBalance(api, ss58Address); + if (balance === expected) { + return balance; + } + await waitForFinalizedBlocks(api, 1); + } + const balance = await getBalance(api, ss58Address); + expect(balance).toEqual(expected); + return balance; + } + + it({ + id: "T01", + title: "Can deploy bridge token smart contract", + test: async () => { + const contractFactory = new ethers.ContractFactory( + BRIDGE_TOKEN_CONTRACT_ABI, + BRIDGE_TOKEN_CONTRACT_BYTECODE, + ethWallet + ); + const contract = await contractFactory.deploy("name", "symbol", ethWallet.address); + await contract.waitForDeployment(); + + expect(contract.target).toBeDefined(); + await expectDeployedContract(provider, contract.target.toString()); + }, + }); + + it({ + id: "T02", + title: "Can deploy bridge token contract with gas limit", + test: async () => { + const contractFactory = new ethers.ContractFactory( + BRIDGE_TOKEN_CONTRACT_ABI, + BRIDGE_TOKEN_CONTRACT_BYTECODE, + ethWallet + ); + const contract = await contractFactory.deploy("name", "symbol", ethWallet.address, { + gasLimit: 12_345_678, + }); + await contract.waitForDeployment(); + + expect(contract.target).toBeDefined(); + await expectDeployedContract(provider, contract.target.toString()); + }, + }); + + it({ + id: "T03", + title: "Can add stake V2", + test: async () => { + await ensureSubnetReady(); + const hotkeySs58 = convertPublicKeyToSs58(hotkey.publicKey); + const walletSs58 = convertH160ToSS58(ethWallet.address); + const stakeBalance = tao(20); + + const stakeBefore = await getStake(api, hotkeySs58, walletSs58, netuid); + const stakingPrecompile = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, ethWallet); + const tx = await stakingPrecompile.addStake(hotkey.publicKey, stakeBalance.toString(), netuid); + const receipt = await tx.wait(); + expect(receipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 2); + + const stakeFromContract = BigInt( + await stakingPrecompile.getStake( + hotkey.publicKey, + convertH160ToPublicKey(ethWallet.address), + netuid + ) + ); + const stakeAfter = await getStake(api, hotkeySs58, walletSs58, netuid); + + expect(stakeFromContract).toBeGreaterThan(stakeBefore); + expect(stakeAfter).toBeGreaterThan(stakeBefore); + }, + }); + + it({ + id: "T04", + title: "Can deploy alpha pool smart contract", + test: async () => { + await ensureSubnetReady(); + const hotkeySs58 = convertPublicKeyToSs58(hotkey.publicKey); + const walletSs58 = convertH160ToSS58(ethWallet.address); + const stakingPrecompile = new ethers.Contract(ISTAKING_V2_ADDRESS, IStakingV2ABI, ethWallet); + + const stakeBeforeDeposit = await getStake(api, hotkeySs58, walletSs58, netuid); + + const contractFactory = new ethers.ContractFactory( + ALPHA_POOL_CONTRACT_ABI, + ALPHA_POOL_CONTRACT_BYTECODE, + ethWallet + ); + const contract = await contractFactory.deploy(hotkey.publicKey); + await contract.waitForDeployment(); + expect(contract.target).toBeDefined(); + + const contractAddress = contract.target.toString(); + const contractPublicKey = convertH160ToPublicKey(contractAddress); + await forceSetBalance(api, convertPublicKeyToSs58(contractPublicKey)); + await expectDeployedContract(provider, contractAddress); + + const contractForCall = new ethers.Contract(contractAddress, ALPHA_POOL_CONTRACT_ABI, ethWallet); + const setContractColdkeyTx = await contractForCall.setContractColdkey(contractPublicKey); + const setColdkeyReceipt = await setContractColdkeyTx.wait(); + expect(setColdkeyReceipt?.status).toEqual(1); + + expect(await contractForCall.contract_coldkey()).toEqual(u8aToHex(contractPublicKey)); + expect(await contractForCall.contract_hotkey()).toEqual(u8aToHex(hotkey.publicKey)); + + const alphaInPool = await contractForCall.getContractStake(netuid); + expect(alphaInPool).toEqual(BigInt(0)); + + const depositAlphaTx = await contractForCall.depositAlpha(netuid, tao(10).toString(), hotkey.publicKey); + const depositReceipt = await depositAlphaTx.wait(); + expect(depositReceipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 2); + + const stakeAfterDeposit = await getStake(api, hotkeySs58, walletSs58, netuid); + expect(stakeAfterDeposit).toBeLessThan(stakeBeforeDeposit); + + const contractStake = await getStake(api, hotkeySs58, convertH160ToSS58(contractAddress), netuid); + expect(contractStake).toBeGreaterThan(BigInt(0)); + + const alphaBalanceOnContract = await contractForCall.alphaBalance(ethWallet.address, netuid); + expect(tao(10) - alphaBalanceOnContract).toBeLessThan(BigInt(1000)); + + const stakeFromContract = BigInt( + await stakingPrecompile.getStake(hotkey.publicKey, contractPublicKey, netuid) + ); + expect(stakeFromContract).toEqual(await contractForCall.getContractStake(netuid)); + }, + }); + + it({ + id: "T05", + title: "Staker add and remove stake", + test: async () => { + await ensureSubnetReady(); + await ensureProxyWalletsReady(); + + const deployedContract = await deployAndFundStakeWrap(stakeWallet); + + const stakeTx = await deployedContract.stake(hotkey.publicKey, netuid, tao(2)); + const stakeReceipt = await stakeTx.wait(); + expect(stakeReceipt?.status).toEqual(1); + + const removeTx = await deployedContract.removeStake(hotkey.publicKey, netuid, tao(1)); + const removeReceipt = await removeTx.wait(); + expect(removeReceipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 1); + }, + }); + + it({ + id: "T06", + title: "Staker add stake limit", + test: async () => { + await ensureSubnetReady(); + await ensureProxyWalletsReady(); + + const deployedContract = await deployAndFundStakeWrap(stakeWallet); + + const tx = await deployedContract.stakeLimit(hotkey.publicKey, netuid, tao(2000), tao(1000), true); + const receipt = await tx.wait(); + expect(receipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 1); + }, + }); + + it({ + id: "T07", + title: "Call createPureProxy, then use proxy to call transfer", + test: async () => { + await ensureProxyWalletsReady(); + + const proxies = await getProxies(api, convertH160ToSS58(proxyWallet1.address)); + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, proxyWallet1); + + const type = 0; + const delay = 0; + const index = 0; + const tx = await contract.createPureProxy(type, delay, index); + const response = await tx.wait(); + expect(response?.status).toEqual(1); + + const proxiesAfterAdd = await waitForPureProxyCount( + convertH160ToSS58(proxyWallet1.address), + proxies.length + 1 + ); + + const proxy = proxiesAfterAdd[proxiesAfterAdd.length - 1]; + await forceSetBalance(api, proxy); + + const receiverSs58 = convertPublicKeyToSs58(pureProxyReceiver.publicKey); + const balance = await getBalance(api, receiverSs58); + const amount = 1_000_000_000; + const callCode = await getTransferCallCode(api, pureProxyReceiver, amount); + + const tx2 = await contract.proxyCall(decodeAddress(proxy), [type], callCode); + const response2 = await tx2.wait(); + expect(response2?.status).toEqual(1); + + await waitForBalanceIncrease(receiverSs58, balance, BigInt(amount)); + }, + }); + + it({ + id: "T08", + title: "Call createPureProxy, add multiple proxies", + test: async () => { + await ensureProxyWalletsReady(); + + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, proxyWallet1); + const type = 0; + const delay = 0; + const index = 0; + const delegateSs58 = convertH160ToSS58(proxyWallet1.address); + const proxies = await getProxies(api, delegateSs58); + const length = proxies.length; + + for (let i = 0; i < 5; i++) { + const tx = await contract.createPureProxy(type, delay, index); + await tx.wait(); + await waitForPureProxyCount(delegateSs58, length + i + 1); + } + }, + }); + + it({ + id: "T09", + title: "Call createPureProxy, edge cases, call via wrong proxy", + test: async () => { + await ensureProxyWalletsReady(); + + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, proxyWallet2); + const amount = 1_000_000_000; + const wrongReceiver = generateKeyringPair("sr25519"); + const callCode = await getTransferCallCode(api, wrongReceiver, amount); + const type = 0; + + await expect( + contract.proxyCall(wrongReceiver.publicKey, [type], callCode).then((proxyTx) => proxyTx.wait()) + ).rejects.toBeDefined(); + }, + }); + + it({ + id: "T10", + title: "Call createProxy, then use proxy to call transfer", + test: async () => { + await ensureProxyWalletsReady(); + + const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(proxyWallet2.address)); + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, proxyWallet2); + + const proxiesFromContract = await contract.getProxies(convertH160ToPublicKey(proxyWallet2.address)); + expect(proxiesFromContract.length).toEqual(proxies[0].length); + + const type = 0; + const delay = 0; + + const tx = await contract.addProxy(convertH160ToPublicKey(proxyWallet3.address), type, delay); + await tx.wait(); + + const proxiesList = await waitForProxyDelegates( + convertH160ToSS58(proxyWallet2.address), + proxies[0].length + 1 + ); + + const proxiesFromContractAfterAdd = await contract.getProxies( + convertH160ToPublicKey(proxyWallet2.address) + ); + expect(proxiesFromContractAfterAdd.length).toEqual(proxiesList.length); + + for (let index = 0; index < proxiesFromContractAfterAdd.length; index++) { + const proxyInfo = proxiesFromContractAfterAdd[index]; + const proxySs58 = convertPublicKeyToSs58(proxyInfo[0]); + expect(proxiesList.includes(proxySs58)).toBe(true); + if (index === proxiesFromContractAfterAdd.length - 1) { + expect(Number(proxyInfo[1])).toEqual(type); + expect(Number(proxyInfo[2])).toEqual(delay); + } + } + + expect(proxiesList.length).toEqual(proxies[0].length + 1); + const proxy = proxiesList[proxiesList.length - 1]; + expect(proxy).toEqual(convertH160ToSS58(proxyWallet3.address)); + + const receiverSs58 = convertPublicKeyToSs58(delegateProxyReceiver.publicKey); + const balance = await getBalance(api, receiverSs58); + const amount = 1_000_000_000; + + const contract2 = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, proxyWallet3); + const callCode = await getTransferCallCode(api, delegateProxyReceiver, amount); + const tx2 = await contract2.proxyCall(convertH160ToPublicKey(proxyWallet2.address), [type], callCode); + await tx2.wait(); + + await waitForBalanceIncrease(receiverSs58, balance, BigInt(amount)); + }, + }); + + it({ + id: "T11", + title: "Call addProxy many times, then check getProxies is correct", + test: async () => { + await ensureProxyWalletsReady(); + + const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(proxyWallet4.address)); + const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, proxyWallet4); + expect(proxies[0].length).toEqual(0); + + const proxiesFromContract = await contract.getProxies(convertH160ToPublicKey(proxyWallet4.address)); + expect(proxiesFromContract.length).toEqual(proxies[0].length); + + const type = 1; + const delay = 2; + + for (let i = 0; i < 5; i++) { + const delegateWallet = createEthersWallet(provider); + const addTx = await contract.addProxy(convertH160ToPublicKey(delegateWallet.address), type, delay); + await addTx.wait(); + } + + const proxiesList = await waitForProxyDelegates(convertH160ToSS58(proxyWallet4.address), 5); + + const proxiesFromContractAfterAdd = await contract.getProxies( + convertH160ToPublicKey(proxyWallet4.address) + ); + expect(proxiesFromContractAfterAdd.length).toEqual(proxiesList.length); + + for (let index = 0; index < proxiesFromContractAfterAdd.length; index++) { + const proxyInfo = proxiesFromContractAfterAdd[index]; + const proxySs58 = convertPublicKeyToSs58(proxyInfo[0]); + expect(proxiesList.includes(proxySs58)).toBe(true); + expect(Number(proxyInfo[1])).toEqual(type); + expect(Number(proxyInfo[2])).toEqual(delay); + } + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_evm/02-precompile-gas.test.ts b/ts-tests/suites/zombienet_evm/02-precompile-gas.test.ts new file mode 100644 index 0000000000..d53c42a2f2 --- /dev/null +++ b/ts-tests/suites/zombienet_evm/02-precompile-gas.test.ts @@ -0,0 +1,97 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { subtensor } from "@polkadot-api/descriptors"; +import { ethers } from "ethers"; +import type { TypedApi } from "polkadot-api"; +import { + convertH160ToSS58, + createEthersWallet, + disableWhiteListCheck, + forceSetBalance, + getBalance, + PRECOMPILE_GAS_CONTRACT_ABI, + PRECOMPILE_GAS_CONTRACT_BYTECODE, + waitForFinalizedBlocks, +} from "../../utils"; + +const MIN_PRECOMPILE_GAS = BigInt(6000); +const MAX_PRECOMPILE_GAS = BigInt(10000); +const ITERATION_COUNTS = [1, 11, 101] as const; + +async function assertPrecompileGasScaling( + api: TypedApi, + wallet: ethers.Wallet, + call: (iterations: number) => Promise +): Promise { + let oneIterationGas = BigInt(0); + + for (const iterations of ITERATION_COUNTS) { + const balanceBefore = await getBalance(api, convertH160ToSS58(wallet.address)); + const tx = await call(iterations); + const receipt = await tx.wait(); + await waitForFinalizedBlocks(api, 2); + + const balanceAfter = await getBalance(api, convertH160ToSS58(wallet.address)); + expect(balanceAfter).toBeLessThan(balanceBefore); + + const gasUsed = receipt!.gasUsed; + if (iterations === 1) { + oneIterationGas = gasUsed; + continue; + } + + expect(gasUsed >= oneIterationGas).toBe(true); + + const precompileUsedGas = gasUsed - oneIterationGas; + const minExpected = MIN_PRECOMPILE_GAS * BigInt(iterations - 1); + const maxExpected = MAX_PRECOMPILE_GAS * BigInt(iterations - 1); + + expect(precompileUsedGas >= minExpected).toBe(true); + expect(precompileUsedGas <= maxExpected).toBe(true); + } +} + +describeSuite({ + id: "precompile-gas", + title: "SR25519 and ED25519 precompile gas tests", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi; + let ethWallet: ethers.Wallet; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + const provider = context.ethers("EVM").provider as ethers.JsonRpcProvider; + ethWallet = createEthersWallet(provider); + + await forceSetBalance(api, convertH160ToSS58(ethWallet.address)); + await disableWhiteListCheck(api, true); + await waitForFinalizedBlocks(api, 1); + }, 300000); + + it({ + id: "T01", + title: "Can deploy and call precompile gas contract", + test: async () => { + const fee = await api.query.BaseFee.BaseFeePerGas.getValue(); + expect(fee[0]).toBeGreaterThan(1_000_000_000); + + const contractFactory = new ethers.ContractFactory( + PRECOMPILE_GAS_CONTRACT_ABI, + PRECOMPILE_GAS_CONTRACT_BYTECODE, + ethWallet + ); + const contractDeploy = await contractFactory.deploy(); + await contractDeploy.waitForDeployment(); + await waitForFinalizedBlocks(api, 1); + + const contractAddress = await contractDeploy.getAddress(); + expect(contractAddress).toBeDefined(); + + const contract = new ethers.Contract(contractAddress, PRECOMPILE_GAS_CONTRACT_ABI, ethWallet); + + await assertPrecompileGasScaling(api, ethWallet, (iterations) => contract.callED25519(iterations)); + await assertPrecompileGasScaling(api, ethWallet, (iterations) => contract.callSR25519(iterations)); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_evm/03-wasm-contract.test.ts b/ts-tests/suites/zombienet_evm/03-wasm-contract.test.ts new file mode 100644 index 0000000000..e7e567f87d --- /dev/null +++ b/ts-tests/suites/zombienet_evm/03-wasm-contract.test.ts @@ -0,0 +1,1217 @@ +import { beforeAll, beforeEach, describeSuite, expect } from "@moonwall/cli"; +import { contracts, MultiAddress, subtensor } from "@polkadot-api/descriptors"; +import { getInkClient, InkClient } from "@polkadot-api/ink-contracts"; +import type { KeyringPair } from "@polkadot/keyring/types"; +import fs from "node:fs"; +import { Binary, type TypedApi } from "polkadot-api"; +import { + addNewSubnetwork, + BITTENSOR_WASM_PATH, + burnedRegister, + convertPublicKeyToSs58, + forceSetBalance, + generateKeyringPair, + getBalance, + instantiateWasmContract, + sendWasmContractExtrinsic, + sendWasmContractExtrinsicAllowFailure, + setTargetRegistrationsPerInterval, + startCall, + sudoSetAdminFreezeWindow, + sudoSetLockReductionInterval, + tao, + waitForFinalizedBlocks, + waitForTransactionWithRetry, +} from "../../utils"; + +const bittensorBytecode = fs.readFileSync(BITTENSOR_WASM_PATH); + +async function fundAccount( + api: TypedApi, + faucet: KeyringPair, + address: string, + amount: bigint = tao(10_000) +): Promise { + const tx = api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(address), + value: amount, + }); + await waitForTransactionWithRetry(api, tx, faucet, "fund_account", 5); +} + +describeSuite({ + id: "wasm-contract", + title: "Wasm ink contract tests", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi; + let faucet: KeyringPair; + let hotkey: KeyringPair; + let coldkey: KeyringPair; + let hotkey2: KeyringPair; + let coldkey2: KeyringPair; + let netuid = 0; + let contractAddress = ""; + let inkClient: InkClient; + + async function addStakeViaContract(addStakeToContract: boolean) { + if (contractAddress === "") { + return; + } + + const amount = tao(100); + let message; + let dest; + if (addStakeToContract) { + message = inkClient.message("add_stake"); + dest = contractAddress; + } else { + message = inkClient.message("caller_add_stake"); + dest = convertPublicKeyToSs58(coldkey.publicKey); + } + + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + amount: amount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stake = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + dest, + netuid + ) + )?.stake; + + expect(stake).toBeDefined(); + expect(stake > BigInt(0)).toBeTruthy(); + } + + async function getContractStake(): Promise { + const stake = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + ) + )?.stake; + + expect(stake).toBeDefined(); + return stake as bigint; + } + + async function getContractStakeOnRoot(): Promise { + const stake = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + 0 + ) + )?.stake; + + expect(stake).toBeDefined(); + return stake as bigint; + } + + async function initSecondColdAndHotkey() { + hotkey2 = generateKeyringPair("sr25519"); + coldkey2 = generateKeyringPair("sr25519"); + await fundAccount(api, faucet, convertPublicKeyToSs58(coldkey2.publicKey)); + await fundAccount(api, faucet, convertPublicKeyToSs58(hotkey2.publicKey)); + await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey2.publicKey), coldkey2); + } + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + await waitForFinalizedBlocks(api, 2); + await sudoSetLockReductionInterval(api, 1); + await sudoSetAdminFreezeWindow(api, 0); + + inkClient = getInkClient(contracts.bittensor); + faucet = generateKeyringPair("sr25519"); + await forceSetBalance(api, convertPublicKeyToSs58(faucet.publicKey), tao(1e9)); + + hotkey = generateKeyringPair("sr25519"); + coldkey = generateKeyringPair("sr25519"); + await fundAccount(api, faucet, convertPublicKeyToSs58(coldkey.publicKey)); + await fundAccount(api, faucet, convertPublicKeyToSs58(hotkey.publicKey)); + + netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid + 1, coldkey); + await setTargetRegistrationsPerInterval(api, netuid); + await waitForFinalizedBlocks(api, 1); + }, 900000); + + beforeEach(async () => { + hotkey = generateKeyringPair("sr25519"); + coldkey = generateKeyringPair("sr25519"); + await fundAccount(api, faucet, convertPublicKeyToSs58(coldkey.publicKey)); + await fundAccount(api, faucet, convertPublicKeyToSs58(hotkey.publicKey)); + await burnedRegister(api, netuid, convertPublicKeyToSs58(hotkey.publicKey), coldkey); + }, 300000); + + it({ + id: "T01", + title: "Can instantiate contract", + test: async () => { + const constructor = inkClient.constructor("new"); + const data = constructor.encode(); + contractAddress = await instantiateWasmContract(api, coldkey, bittensorBytecode, data); + + const transfer = api.tx.Balances.transfer_keep_alive({ + dest: MultiAddress.Id(contractAddress), + value: tao(2000), + }); + await waitForTransactionWithRetry(api, transfer, coldkey, "transfer_to_contract", 5); + await waitForFinalizedBlocks(api, 1); + }, + }); + + it({ + id: "T02", + title: "Can query stake info from contract", + test: async () => { + const queryMessage = inkClient.message("get_stake_info_for_hotkey_coldkey_netuid"); + + const data = queryMessage.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + coldkey: Binary.fromBytes(coldkey.publicKey), + netuid: netuid, + }); + + const response = await api.apis.ContractsApi.call( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + BigInt(0), + undefined, + undefined, + Binary.fromBytes(data.asBytes()) + ); + + expect(response.result.success).toBeTruthy(); + const result = queryMessage.decode(response.result.value).value.value; + + if ( + typeof result === "object" && + "hotkey" in result && + "coldkey" in result && + "netuid" in result && + "stake" in result && + "locked" in result && + "emission" in result && + "tao_emission" in result && + "drain" in result && + "is_registered" in result + ) { + expect(result.hotkey).toEqual(convertPublicKeyToSs58(hotkey.publicKey)); + expect(result.coldkey).toEqual(convertPublicKeyToSs58(coldkey.publicKey)); + expect(result.netuid).toEqual(netuid); + expect(result.is_registered).toEqual(true); + } else { + throw new Error("result is not an object"); + } + }, + }); + + it({ + id: "T03", + title: "Can add stake to contract", + test: async () => { + await addStakeViaContract(true); + }, + }); + + it({ + id: "T04", + title: "Can remove stake to contract", + test: async () => { + await addStakeViaContract(true); + const stake = await getContractStake(); + + let amount = stake / BigInt(2); + const message = inkClient.message("remove_stake"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + amount: amount, + }); + + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfterAddStake = await getContractStake(); + + expect(stakeAfterAddStake < stake).toBeTruthy(); + }, + }); + + it({ + id: "T05", + title: "Can unstake all from contract", + test: async () => { + await addStakeViaContract(true); + // Get stake before unstake_all + const stakeBefore = await getContractStake(); + + expect(stakeBefore > BigInt(0)).toBeTruthy(); + + // Call unstake_all + const unstakeMessage = inkClient.message("unstake_all"); + const unstakeData = unstakeMessage.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, unstakeData); + + // Verify stake is now zero + const stakeAfter = await getContractStake(); + + expect(stakeAfter).toEqual(BigInt(0)); + }, + }); + + it({ + id: "T06", + title: "Can unstake all alpha from contract", + test: async () => { + await addStakeViaContract(true); + // Get stake before unstake_all_alpha + const stakeBefore = await getContractStake(); + + expect(stakeBefore > BigInt(0)).toBeTruthy(); + + // Call unstake_all_alpha + const message = inkClient.message("unstake_all_alpha"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + // Verify stake is now zero + const stakeAfter = await getContractStake(); + + expect(stakeAfter).toEqual(BigInt(0)); + }, + }); + + it({ + id: "T07", + title: "Can move stake between hotkeys", + test: async () => { + await addStakeViaContract(true); + await initSecondColdAndHotkey(); + // Get initial stakes + const originStakeBefore = await getContractStake(); + + const destStakeBefore = + ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + contractAddress, + netuid + ) + )?.stake || BigInt(0); + + expect(originStakeBefore > BigInt(0)).toBeTruthy(); + + // Move stake + const moveAmount = originStakeBefore / BigInt(2); + const message = inkClient.message("move_stake"); + const data = message.encode({ + origin_hotkey: Binary.fromBytes(hotkey.publicKey), + destination_hotkey: Binary.fromBytes(hotkey2.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: moveAmount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + // Verify stakes changed + const originStakeAfter = await getContractStake(); + + const destStakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + contractAddress, + netuid + ) + )?.stake; + + expect(destStakeAfter).toBeDefined(); + expect(originStakeAfter < originStakeBefore).toBeTruthy(); + expect(destStakeAfter > destStakeBefore).toBeTruthy(); + }, + }); + + it({ + id: "T08", + title: "Can transfer stake between coldkeys", + test: async () => { + await addStakeViaContract(true); + await initSecondColdAndHotkey(); + // Get initial stake + const stakeBeforeOrigin = await getContractStake(); + + const stakeBeforeDest = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid + ) + )?.stake; + + expect(stakeBeforeOrigin > BigInt(0)).toBeTruthy(); + expect(stakeBeforeDest).toBeDefined(); + + // Transfer stake + const transferAmount = stakeBeforeOrigin / BigInt(2); + const message = inkClient.message("transfer_stake"); + const data = message.encode({ + destination_coldkey: Binary.fromBytes(coldkey2.publicKey), + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: transferAmount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + // Verify stake transferred + const stakeAfterOrigin = await getContractStake(); + + const stakeAfterDest = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid + ) + )?.stake; + + expect(stakeAfterDest).toBeDefined(); + expect(stakeAfterOrigin < stakeBeforeOrigin).toBeTruthy(); + expect(stakeAfterDest > stakeBeforeDest!).toBeTruthy(); + }, + }); + + it({ + id: "T09", + title: "Can swap stake between networks", + test: async () => { + await addStakeViaContract(true); + // Get initial stakes + const stakeBefore = await getContractStake(); + + const stakeBefore2 = + ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1 + ) + )?.stake || BigInt(0); + + expect(stakeBefore > BigInt(0)).toBeTruthy(); + + // Swap stake + const swapAmount = stakeBefore / BigInt(2); + const message = inkClient.message("swap_stake"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: swapAmount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + // Verify stakes swapped + const stakeAfter = await getContractStake(); + + const stakeAfter2 = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1 + ) + )?.stake; + + expect(stakeAfter2).toBeDefined(); + expect(stakeAfter < stakeBefore).toBeTruthy(); + expect(stakeAfter2 > stakeBefore2).toBeTruthy(); + }, + }); + + it({ + id: "T10", + title: "Can add stake with limit", + test: async () => { + const stakeBefore = await getContractStake(); + + const message = inkClient.message("add_stake_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + amount: tao(200), + limit_price: tao(100), + allow_partial: false, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + // Verify stake was added + const stakeAfter = await getContractStake(); + + expect(stakeAfter > stakeBefore).toBeTruthy(); + }, + }); + + it({ + id: "T11", + title: "Can remove stake with limit", + test: async () => { + await addStakeViaContract(true); + const stakeBefore = await getContractStake(); + + expect(stakeBefore > BigInt(0)).toBeTruthy(); + + const message = inkClient.message("remove_stake_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStake(); + + expect(stakeAfter < stakeBefore).toBeTruthy(); + }, + }); + + it({ + id: "T12", + title: "Can swap stake with limit", + test: async () => { + await addStakeViaContract(true); + + const stakeBefore = await getContractStake(); + + const stakeBefore2 = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1 + ) + )?.stake; + + expect(stakeBefore > BigInt(0)).toBeTruthy(); + expect(stakeBefore2).toBeDefined(); + + const message = inkClient.message("swap_stake_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStake(); + + const stakeAfter2 = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + netuid + 1 + ) + )?.stake; + + expect(stakeAfter2).toBeDefined(); + expect(stakeAfter < stakeBefore).toBeTruthy(); + expect(stakeAfter2 > stakeBefore2).toBeTruthy(); + }, + }); + + it({ + id: "T13", + title: "Can remove stake full limit", + test: async () => { + await addStakeViaContract(true); + const stakeBefore = await getContractStake(); + + expect(stakeBefore > BigInt(0)).toBeTruthy(); + + const message = inkClient.message("remove_stake_full_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: netuid, + limit_price: BigInt(0), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStake(); + + expect(stakeAfter < stakeBefore).toBeTruthy(); + }, + }); + + it({ + id: "T14", + title: "Can set coldkey auto stake hotkey", + test: async () => { + const message = inkClient.message("set_coldkey_auto_stake_hotkey"); + const data = message.encode({ + netuid: netuid, + hotkey: Binary.fromBytes(hotkey.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + let autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( + contractAddress, + netuid + ); + + expect(autoStakeHotkey).toBeDefined(); + expect(autoStakeHotkey).toEqual(convertPublicKeyToSs58(hotkey.publicKey)); + }, + }); + + it({ + id: "T15", + title: "Can add and remove proxy", + test: async () => { + const message = inkClient.message("add_proxy"); + const data = message.encode({ + delegate: Binary.fromBytes(hotkey.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + let proxies = await api.query.Proxy.Proxies.getValue(contractAddress); + expect(proxies).toBeDefined(); + expect(proxies.length > 0 && proxies[0].length > 0).toBeTruthy(); + expect(proxies[0][0].delegate).toEqual(convertPublicKeyToSs58(hotkey.publicKey)); + + const removeMessage = inkClient.message("remove_proxy"); + const removeData = removeMessage.encode({ + delegate: Binary.fromBytes(hotkey.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData); + + let proxiesAfterRemove = await api.query.Proxy.Proxies.getValue(contractAddress); + expect(proxiesAfterRemove).toBeDefined(); + expect(proxiesAfterRemove[0].length).toEqual(0); + }, + }); + + it({ + id: "T16", + title: "Can get alpha price", + test: async () => { + const message = inkClient.message("get_alpha_price"); + const data = message.encode({ + netuid: netuid, + }); + + const response = await api.apis.ContractsApi.call( + convertPublicKeyToSs58(hotkey.publicKey), + contractAddress, + BigInt(0), + undefined, + undefined, + Binary.fromBytes(data.asBytes()) + ); + + expect(response.result.success).toBeTruthy(); + const result = message.decode(response.result.value).value.value; + + expect(result).toBeDefined(); + }, + }); + + it({ + id: "T17", + title: "Can recycle alpha from contract stake", + test: async () => { + await addStakeViaContract(true); + await waitForFinalizedBlocks(api, 2); + const stakeBefore = await getContractStake(); + const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid); + + const message = inkClient.message("recycle_alpha"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: stakeBefore / BigInt(2), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStake(); + const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid); + + expect(stakeAfter < stakeBefore).toBeTruthy(); + expect(alphaOutAfter < alphaOutBefore).toBeTruthy(); + }, + }); + + it({ + id: "T18", + title: "Can burn alpha from contract stake", + test: async () => { + await addStakeViaContract(true); + await waitForFinalizedBlocks(api, 2); + const stakeBefore = await getContractStake(); + const alphaBurnedBefore = await api.query.AlphaAssets.AlphaBurned.getValue(netuid); + + const message = inkClient.message("burn_alpha"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: stakeBefore / BigInt(2), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStake(); + const alphaBurnedAfter = await api.query.AlphaAssets.AlphaBurned.getValue(netuid); + + expect(stakeAfter < stakeBefore).toBeTruthy(); + expect(alphaBurnedBefore < alphaBurnedAfter).toBeTruthy(); + }, + }); + + it({ + id: "T19", + title: "Can add stake and recycle resulting alpha", + test: async () => { + const stakeBefore = await getContractStake(); + + const message = inkClient.message("add_stake_recycle"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: tao(100), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStake(); + + expect(stakeAfter).toEqual(stakeBefore); + }, + }); + + it({ + id: "T20", + title: "Can add stake and burn resulting alpha", + test: async () => { + const stakeBefore = await getContractStake(); + const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid); + + const message = inkClient.message("add_stake_burn"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: tao(100), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStake(); + const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid); + + expect(stakeAfter).toEqual(stakeBefore); + expect(alphaOutAfter > alphaOutBefore).toBeTruthy(); + }, + }); + + it({ + id: "T21", + title: "Can caller add stake (fn 20)", + test: async () => { + await addStakeViaContract(false); + }, + }); + + it({ + id: "T22", + title: "Can caller remove stake (fn 21)", + test: async () => { + await addStakeViaContract(false); + const stake = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stake).toBeDefined(); + const amount = stake / BigInt(2); + const message = inkClient.message("caller_remove_stake"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeAfter !== undefined && stakeAfter < stake!).toBeTruthy(); + }, + }); + + it({ + id: "T23", + title: "Can caller unstake_all (fn 22)", + test: async () => { + await addStakeViaContract(false); + const stakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeBefore !== undefined && stakeBefore > BigInt(0)).toBeTruthy(); + const message = inkClient.message("caller_unstake_all"); + const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeAfter).toBeDefined(); + expect(stakeAfter < stakeBefore!).toBeTruthy(); + }, + }); + + it({ + id: "T24", + title: "Can caller unstake_all_alpha (fn 23)", + test: async () => { + await addStakeViaContract(false); + const stakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeBefore !== undefined && stakeBefore > BigInt(0)).toBeTruthy(); + const message = inkClient.message("caller_unstake_all_alpha"); + const data = message.encode({ hotkey: Binary.fromBytes(hotkey.publicKey) }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeAfter).toBeDefined(); + expect(stakeAfter < stakeBefore!).toBeTruthy(); + }, + }); + + it({ + id: "T25", + title: "Can caller move_stake (fn 24)", + test: async () => { + await addStakeViaContract(false); + await initSecondColdAndHotkey(); + const originStakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const destStakeBefore = + ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake || BigInt(0); + expect(originStakeBefore !== undefined && originStakeBefore > BigInt(0)).toBeTruthy(); + const moveAmount = originStakeBefore / BigInt(2); + const message = inkClient.message("caller_move_stake"); + const data = message.encode({ + origin_hotkey: Binary.fromBytes(hotkey.publicKey), + destination_hotkey: Binary.fromBytes(hotkey2.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: moveAmount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const originStakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const destStakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey2.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(originStakeAfter !== undefined && destStakeAfter !== undefined).toBeTruthy(); + expect(originStakeAfter < originStakeBefore!).toBeTruthy(); + expect(destStakeAfter > destStakeBefore).toBeTruthy(); + }, + }); + + it({ + id: "T26", + title: "Can caller transfer_stake (fn 25)", + test: async () => { + await addStakeViaContract(false); + await initSecondColdAndHotkey(); + const stakeBeforeOrigin = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const stakeBeforeDest = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid + ) + )?.stake; + expect(stakeBeforeOrigin !== undefined && stakeBeforeOrigin > BigInt(0)).toBeTruthy(); + expect(stakeBeforeDest).toBeDefined(); + const transferAmount = stakeBeforeOrigin / BigInt(2); + const message = inkClient.message("caller_transfer_stake"); + const data = message.encode({ + destination_coldkey: Binary.fromBytes(coldkey2.publicKey), + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid, + amount: transferAmount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfterOrigin = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const stakeAfterDest = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey2.publicKey), + netuid + ) + )?.stake; + expect(stakeAfterOrigin !== undefined && stakeAfterDest !== undefined).toBeTruthy(); + expect(stakeAfterOrigin < stakeBeforeOrigin!).toBeTruthy(); + expect(stakeAfterDest > stakeBeforeDest!).toBeTruthy(); + }, + }); + + it({ + id: "T27", + title: "Can caller swap_stake (fn 26)", + test: async () => { + await addStakeViaContract(false); + await initSecondColdAndHotkey(); + const stakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const stakeBefore2 = + ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1 + ) + )?.stake || BigInt(0); + expect(stakeBefore !== undefined && stakeBefore > BigInt(0)).toBeTruthy(); + const swapAmount = stakeBefore / BigInt(2); + const message = inkClient.message("caller_swap_stake"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: swapAmount, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const stakeAfter2 = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1 + ) + )?.stake; + expect(stakeAfter !== undefined && stakeAfter2 !== undefined).toBeTruthy(); + expect(stakeAfter < stakeBefore).toBeTruthy(); + expect(stakeAfter2 > stakeBefore2).toBeTruthy(); + }, + }); + + it({ + id: "T28", + title: "Can caller add_stake_limit (fn 27)", + test: async () => { + const stakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeBefore).toBeDefined(); + const message = inkClient.message("caller_add_stake_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: tao(200), + limit_price: tao(100), + allow_partial: false, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeAfter !== undefined && stakeAfter > stakeBefore!).toBeTruthy(); + }, + }); + + it({ + id: "T29", + title: "Can caller remove_stake_limit (fn 28)", + test: async () => { + await addStakeViaContract(false); + const stakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeBefore !== undefined && stakeBefore > BigInt(0)).toBeTruthy(); + const message = inkClient.message("caller_remove_stake_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeAfter !== undefined && stakeAfter < stakeBefore!).toBeTruthy(); + }, + }); + + it({ + id: "T30", + title: "Can caller swap_stake_limit (fn 29)", + test: async () => { + await addStakeViaContract(false); + const stakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const stakeBefore2 = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1 + ) + )?.stake; + expect(stakeBefore !== undefined && stakeBefore > BigInt(0)).toBeTruthy(); + expect(stakeBefore2).toBeDefined(); + const message = inkClient.message("caller_swap_stake_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + origin_netuid: netuid, + destination_netuid: netuid + 1, + amount: stakeBefore / BigInt(2), + limit_price: tao(1), + allow_partial: false, + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + const stakeAfter2 = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + 1 + ) + )?.stake; + expect(stakeAfter !== undefined && stakeAfter2 !== undefined).toBeTruthy(); + expect(stakeAfter < stakeBefore).toBeTruthy(); + expect(stakeAfter2 > stakeBefore2!).toBeTruthy(); + }, + }); + + it({ + id: "T31", + title: "Can caller remove_stake_full_limit (fn 30)", + test: async () => { + await addStakeViaContract(false); + const stakeBefore = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeBefore !== undefined && stakeBefore > BigInt(0)).toBeTruthy(); + const message = inkClient.message("caller_remove_stake_full_limit"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid, + limit_price: BigInt(0), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const stakeAfter = ( + await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid( + convertPublicKeyToSs58(hotkey.publicKey), + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ) + )?.stake; + expect(stakeAfter !== undefined && stakeAfter < stakeBefore!).toBeTruthy(); + }, + }); + + it({ + id: "T32", + title: "Can caller set_coldkey_auto_stake_hotkey (fn 31)", + test: async () => { + await addStakeViaContract(false); + await initSecondColdAndHotkey(); + const message = inkClient.message("caller_set_coldkey_auto_stake_hotkey"); + const data = message.encode({ + netuid, + hotkey: Binary.fromBytes(hotkey2.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, data); + const autoStakeHotkey = await api.query.SubtensorModule.AutoStakeDestination.getValue( + convertPublicKeyToSs58(coldkey.publicKey), + netuid + ); + expect(autoStakeHotkey).toEqual(convertPublicKeyToSs58(hotkey2.publicKey)); + }, + }); + + it({ + id: "T33", + title: "Can caller add_proxy and remove_proxy (fn 32-33)", + test: async () => { + const addMessage = inkClient.message("caller_add_proxy"); + const addData = addMessage.encode({ + delegate: Binary.fromBytes(hotkey2.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, addData); + let proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)); + expect(proxies !== undefined && proxies[0].length > 0).toBeTruthy(); + expect(proxies[0][0].delegate).toEqual(convertPublicKeyToSs58(hotkey2.publicKey)); + + const removeMessage = inkClient.message("caller_remove_proxy"); + const removeData = removeMessage.encode({ + delegate: Binary.fromBytes(hotkey2.publicKey), + }); + await sendWasmContractExtrinsic(api, coldkey, contractAddress, removeData); + proxies = await api.query.Proxy.Proxies.getValue(convertPublicKeyToSs58(coldkey.publicKey)); + expect(proxies !== undefined && proxies[0].length).toEqual(0); + }, + }); + + it({ + id: "T34", + title: "Check add_stake_recycle is atomic operation", + test: async () => { + const stakeBefore = await getContractStakeOnRoot(); + const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)); + + // recycle alpha on root subnet is not allowed, the extrinsic should be failed. + const message = inkClient.message("add_stake_recycle"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: 0, + amount: tao(100), + }); + await sendWasmContractExtrinsicAllowFailure(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStakeOnRoot(); + const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)); + + expect(balanceBefore - balanceAfter < 10_000_000).toBeTruthy(); + expect(stakeAfter).toEqual(stakeBefore); + }, + }); + + it({ + id: "T35", + title: "Check add_stake_burn is atomic operation", + test: async () => { + const stakeBefore = await getContractStakeOnRoot(); + const balanceBefore = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)); + const alphaOutBefore = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid); + + const message = inkClient.message("add_stake_burn"); + const data = message.encode({ + hotkey: Binary.fromBytes(hotkey.publicKey), + netuid: 0, + amount: tao(100), + }); + await sendWasmContractExtrinsicAllowFailure(api, coldkey, contractAddress, data); + + const stakeAfter = await getContractStakeOnRoot(); + const alphaOutAfter = await api.query.SubtensorModule.SubnetAlphaOut.getValue(netuid); + const balanceAfter = await getBalance(api, convertPublicKeyToSs58(coldkey.publicKey)); + + expect(balanceBefore - balanceAfter < 10_000_000).toBeTruthy(); + expect(stakeAfter).toEqual(stakeBefore); + expect(alphaOutAfter > alphaOutBefore).toBeTruthy(); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_evm/04-edge-cases.test.ts b/ts-tests/suites/zombienet_evm/04-edge-cases.test.ts new file mode 100644 index 0000000000..a3cbcf4c85 --- /dev/null +++ b/ts-tests/suites/zombienet_evm/04-edge-cases.test.ts @@ -0,0 +1,152 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { KeyringPair } from "@polkadot/keyring/types"; +import { ethers } from "ethers"; +import type { TypedApi } from "polkadot-api"; +import { + convertH160ToSS58, + convertPublicKeyToSs58, + createEthersWallet, + disableWhiteListCheck, + forceSetBalance, + forceSetChainID, + generateKeyringPair, + getEthChainId, + IBALANCETRANSFER_ADDRESS, + IBalanceTransferABI, + raoToEth, + sendTransaction, + tao, + waitForFinalizedBlocks, +} from "../../utils"; + +const INIT_CHAIN_ID = 42; + +describeSuite({ + id: "edge-cases", + title: "EVM edge case tests", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi; + let provider: ethers.JsonRpcProvider; + let ethWallet: ethers.Wallet; + let ethWallet2: ethers.Wallet; + let transferTarget: KeyringPair; + let nonSudoSigner: KeyringPair; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + provider = context.ethers("EVM").provider as ethers.JsonRpcProvider; + + ethWallet = createEthersWallet(provider); + ethWallet2 = createEthersWallet(provider); + transferTarget = generateKeyringPair("sr25519"); + nonSudoSigner = generateKeyringPair("sr25519"); + + await forceSetBalance(api, convertH160ToSS58(ethWallet.address)); + await forceSetBalance(api, convertPublicKeyToSs58(nonSudoSigner.publicKey)); + await disableWhiteListCheck(api, true); + await waitForFinalizedBlocks(api, 1); + }, 300000); + + async function getChainId(): Promise { + return getEthChainId(provider); + } + + it({ + id: "T01", + title: "EVM chain id update is ok", + test: async () => { + let chainId = await getChainId(); + expect(chainId).toEqual(BigInt(INIT_CHAIN_ID)); + + const newChainId = BigInt(100); + await forceSetChainID(api, newChainId); + await waitForFinalizedBlocks(api, 1); + + chainId = await getChainId(); + expect(chainId).toEqual(newChainId); + + await forceSetChainID(api, BigInt(INIT_CHAIN_ID)); + await waitForFinalizedBlocks(api, 1); + + chainId = await getChainId(); + expect(chainId).toEqual(BigInt(INIT_CHAIN_ID)); + }, + }); + + it({ + id: "T02", + title: "EVM chain id is the same, only sudo can change it", + test: async () => { + let chainId = await getChainId(); + expect(chainId).toEqual(BigInt(INIT_CHAIN_ID)); + + const tx = api.tx.AdminUtils.sudo_set_evm_chain_id({ chain_id: BigInt(100) }); + const result = await sendTransaction(tx, nonSudoSigner); + expect(result.success).toBe(false); + + chainId = await getChainId(); + expect(chainId).toEqual(BigInt(INIT_CHAIN_ID)); + }, + }); + + it({ + id: "T03", + title: "Can replace simple transfer transaction", + test: async () => { + const transferBalance = raoToEth(tao(1)); + const gasPrice = BigInt(10e9); + const gasLimit = BigInt(1_000_000); + const nonce = await provider.getTransactionCount(ethWallet.address); + + for (let i = 1; i < 10; i++) { + try { + await ethWallet.sendTransaction({ + to: ethWallet2.address, + value: transferBalance, + nonce, + gasPrice: gasPrice * BigInt(i), + gasLimit: gasLimit * BigInt(i), + }); + } catch { + // Previous transaction may have been mined with the same nonce. + } + await new Promise((resolve) => setTimeout(resolve, 10)); + } + + await forceSetBalance(api, convertH160ToSS58(ethWallet.address)); + await waitForFinalizedBlocks(api, 1); + }, + }); + + it({ + id: "T04", + title: "Can replace precompile call transaction", + test: async () => { + const contract = new ethers.Contract(IBALANCETRANSFER_ADDRESS, IBalanceTransferABI, ethWallet); + const transferBalance = raoToEth(tao(1)); + const gasPrice = BigInt(10e9); + const gasLimit = BigInt(1_000_000); + const nonce = await provider.getTransactionCount(ethWallet.address); + + for (let i = 1; i < 10; i++) { + try { + await contract.transfer(transferTarget.publicKey, { + value: transferBalance, + nonce, + gasPrice: gasPrice * BigInt(i), + gasLimit: gasLimit * BigInt(i), + }); + } catch { + // Previous transaction may have been mined with the same nonce. + } + await new Promise((resolve) => setTimeout(resolve, 10)); + } + + await forceSetBalance(api, convertH160ToSS58(ethWallet.address)); + await waitForFinalizedBlocks(api, 1); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_evm/05-direct-call-precompile.test.ts b/ts-tests/suites/zombienet_evm/05-direct-call-precompile.test.ts new file mode 100644 index 0000000000..ce7b2f5255 --- /dev/null +++ b/ts-tests/suites/zombienet_evm/05-direct-call-precompile.test.ts @@ -0,0 +1,542 @@ +import { beforeAll, describeSuite, expect } from "@moonwall/cli"; +import { subtensor } from "@polkadot-api/descriptors"; +import type { KeyringPair } from "@polkadot/keyring/types"; +import { ethers } from "ethers"; +import { Binary, type TypedApi } from "polkadot-api"; +import { + addNewSubnetwork, + convertH160ToPublicKey, + convertH160ToSS58, + convertPublicKeyToSs58, + createEthersWallet, + disableWhiteListCheck, + forceSetBalance, + generateKeyringPair, + getBalance, + getStake, + IPROXY_ADDRESS, + IProxyABI, + PRECOMPILE_WRAPPER_ABI, + PRECOMPILE_WRAPPER_BYTECODE, + raoToEth, + startCall, + sudoSetLockReductionInterval, + tao, + waitForFinalizedBlocks, + waitForTransactionWithRetry, +} from "../../utils"; + +describeSuite({ + id: "direct-call-precompile", + title: "PrecompileWrapper direct call tests", + foundationMethods: "zombie", + testCases: ({ it, context }) => { + let api: TypedApi; + let provider: ethers.JsonRpcProvider; + let ethWallet: ethers.Wallet; + let ethWallet2: ethers.Wallet; + let hotkey: KeyringPair; + let coldkey: KeyringPair; + let wrapperContract: ethers.Contract; + let wrapperAddress: string; + let netuid: number; + let subnetReady = false; + + beforeAll(async () => { + api = context.papi("Node").getTypedApi(subtensor); + provider = context.ethers("EVM").provider as ethers.JsonRpcProvider; + + ethWallet = createEthersWallet(provider); + ethWallet2 = createEthersWallet(provider); + + await forceSetBalance(api, convertH160ToSS58(ethWallet.address)); + await forceSetBalance(api, convertH160ToSS58(ethWallet2.address)); + await disableWhiteListCheck(api, true); + + hotkey = generateKeyringPair("sr25519"); + coldkey = generateKeyringPair("sr25519"); + + await sudoSetLockReductionInterval(api, 1); + await forceSetBalance(api, convertPublicKeyToSs58(hotkey.publicKey)); + await forceSetBalance(api, convertPublicKeyToSs58(coldkey.publicKey)); + + netuid = await addNewSubnetwork(api, hotkey, coldkey); + await startCall(api, netuid, coldkey); + + const factory = new ethers.ContractFactory(PRECOMPILE_WRAPPER_ABI, PRECOMPILE_WRAPPER_BYTECODE, ethWallet); + const deployed = await factory.deploy(); + await deployed.waitForDeployment(); + wrapperAddress = await deployed.getAddress(); + await forceSetBalance(api, convertH160ToSS58(wrapperAddress)); + await waitForFinalizedBlocks(api, 1); + + wrapperContract = new ethers.Contract(wrapperAddress, PRECOMPILE_WRAPPER_ABI, ethWallet); + subnetReady = true; + }, 600000); + + async function ensureSubnetAndWrapperReady(): Promise { + expect(subnetReady).toBe(true); + } + + async function getCrowdloanEndBlock(): Promise { + const currentBlock = await api.query.System.Number.getValue(); + const minDuration = await api.constants.Crowdloan.MinimumBlockDuration(); + return currentBlock + minDuration + 100; + } + + async function waitForCrowdloanId(expected: number): Promise { + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + if (nextId === expected) { + return; + } + await waitForFinalizedBlocks(api, 1); + } + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + expect(nextId).toEqual(expected); + } + + async function waitForBalanceAtLeast(ss58Address: string, minimum: bigint): Promise { + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + const balance = await getBalance(api, ss58Address); + if (balance >= minimum) { + return balance; + } + await waitForFinalizedBlocks(api, 1); + } + const balance = await getBalance(api, ss58Address); + expect(balance).toBeGreaterThanOrEqual(minimum); + return balance; + } + + async function waitForTotalNetworks(expected: number): Promise { + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + const total = await api.query.SubtensorModule.TotalNetworks.getValue(); + if (total === expected) { + return; + } + await waitForFinalizedBlocks(api, 1); + } + const total = await api.query.SubtensorModule.TotalNetworks.getValue(); + expect(total).toEqual(expected); + } + + async function waitForProxyCount(realSs58: string, expected: number): Promise { + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + const proxies = await api.query.Proxy.Proxies.getValue(realSs58); + if (proxies[0].length === expected) { + return; + } + await waitForFinalizedBlocks(api, 1); + } + const proxies = await api.query.Proxy.Proxies.getValue(realSs58); + expect(proxies[0].length).toEqual(expected); + } + + it({ + id: "T01", + title: "Should transfer balance via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const recipient = generateKeyringPair("sr25519"); + const transferAmount = raoToEth(tao(1)); + + const transferTx = await wrapperContract.transfer(recipient.publicKey, { + value: transferAmount, + }); + const receipt = await transferTx.wait(); + expect(receipt?.status).toEqual(1); + + await waitForBalanceAtLeast(convertPublicKeyToSs58(recipient.publicKey), tao(1)); + }, + }); + + it({ + id: "T02", + title: "Should get UID count via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + const uidCount = await wrapperContract.getUidCount(netuid); + expect(uidCount).toBeDefined(); + }, + }); + + it({ + id: "T03", + title: "Should get serving rate limit via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + const rateLimit = await wrapperContract.getServingRateLimit(netuid); + expect(rateLimit).toBeDefined(); + }, + }); + + it({ + id: "T04", + title: "Should get network registered block via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const onchainValue = await api.query.SubtensorModule.NetworkRegisteredAt.getValue(netuid); + const valueViaWrapper = Number(await wrapperContract.getNetworkRegistrationBlock(netuid)); + + expect(valueViaWrapper).toBeGreaterThan(0); + expect(valueViaWrapper).toEqual(Number(onchainValue)); + }, + }); + + it({ + id: "T05", + title: "Should register network with details via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const newHotkey = generateKeyringPair("sr25519"); + await forceSetBalance(api, convertPublicKeyToSs58(newHotkey.publicKey)); + + const totalNetworksBefore = await api.query.SubtensorModule.TotalNetworks.getValue(); + const registerTx = await wrapperContract.registerNetworkWithDetails( + newHotkey.publicKey, + "Test Subnet", + "https://github.com/test/repo", + "test@example.com", + "https://test.example.com", + "test#1234", + "Test description", + "Additional info", + { value: raoToEth(tao(100)) } + ); + const receipt = await registerTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForTotalNetworks(totalNetworksBefore + 1); + }, + }); + + it({ + id: "T06", + title: "Should register neuron via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const newHotkey = generateKeyringPair("sr25519"); + const newColdkey = generateKeyringPair("sr25519"); + await forceSetBalance(api, convertPublicKeyToSs58(newHotkey.publicKey)); + await forceSetBalance(api, convertPublicKeyToSs58(newColdkey.publicKey)); + + const burnAmount = tao(100); + const registerTx = await wrapperContract.burnedRegister(netuid, newHotkey.publicKey, { + value: raoToEth(burnAmount), + }); + const receipt = await registerTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 2); + + const uid = await api.query.SubtensorModule.Uids.getValue( + netuid, + convertPublicKeyToSs58(newHotkey.publicKey) + ); + expect(uid).toBeDefined(); + }, + }); + + it({ + id: "T07", + title: "Should get total coldkey stake via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + const stake = await wrapperContract.getTotalColdkeyStake(coldkey.publicKey); + expect(stake).toBeDefined(); + }, + }); + + it({ + id: "T08", + title: "Should get total hotkey stake via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + const stake = await wrapperContract.getTotalHotkeyStake(hotkey.publicKey); + expect(stake).toBeDefined(); + }, + }); + + it({ + id: "T09", + title: "Should add stake via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const stakeAmount = tao(2); + const wrapperSs58 = convertH160ToSS58(wrapperAddress); + const hotkeySs58 = convertPublicKeyToSs58(hotkey.publicKey); + const stakeBefore = await getStake(api, hotkeySs58, wrapperSs58, netuid); + + const addStakeTx = await wrapperContract.addStake(hotkey.publicKey, stakeAmount, netuid, { + value: raoToEth(stakeAmount), + }); + const receipt = await addStakeTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 2); + + const stakeAfter = await getStake(api, hotkeySs58, wrapperSs58, netuid); + expect(stakeAfter).toBeGreaterThan(stakeBefore); + }, + }); + + it({ + id: "T10", + title: "Should remove stake via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const removeAmount = tao(1); + const wrapperSs58 = convertH160ToSS58(wrapperAddress); + const hotkeySs58 = convertPublicKeyToSs58(hotkey.publicKey); + const stakeBefore = await getStake(api, hotkeySs58, wrapperSs58, netuid); + + const removeStakeTx = await wrapperContract.removeStake( + hotkey.publicKey, + removeAmount.toString(), + netuid + ); + const receipt = await removeStakeTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 2); + + const stakeAfter = await getStake(api, hotkeySs58, wrapperSs58, netuid); + expect(stakeAfter).toBeLessThan(stakeBefore); + }, + }); + + it({ + id: "T11", + title: "Should lookup UID via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + const lookup = await wrapperContract.uidLookup(netuid, ethWallet.address, 10); + expect(Array.isArray(lookup)).toBe(true); + }, + }); + + it({ + id: "T12", + title: "Should get alpha price via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + const price = await wrapperContract.getAlphaPrice(netuid); + expect(price).toBeDefined(); + }, + }); + + it({ + id: "T13", + title: "Should get crowdloan via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + const end = await getCrowdloanEndBlock(); + const deposit = BigInt(15_000_000_000); + const minContribution = BigInt(1_000_000_000); + const cap = BigInt(100_000_000_000); + + const tx = api.tx.Crowdloan.create({ + deposit, + min_contribution: minContribution, + cap, + end, + target_address: undefined, + call: api.tx.System.remark({ remark: Binary.fromText("test") }).decodedCall, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "crowdloan_create", 5); + await waitForFinalizedBlocks(api, 1); + + const crowdloan = await wrapperContract.getCrowdloan(nextId); + expect(crowdloan).toBeDefined(); + }, + }); + + it({ + id: "T14", + title: "Should get contribution via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const nextId = await api.query.Crowdloan.NextCrowdloanId.getValue(); + expect(nextId).toBeGreaterThan(0); + const contribution = await wrapperContract.getContribution(nextId - 1, coldkey.publicKey); + expect(contribution).toBeDefined(); + }, + }); + + it({ + id: "T15", + title: "Should create crowdloan via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const deposit = BigInt(20_000_000_000); + const minContribution = BigInt(2_000_000_000); + const cap = BigInt(200_000_000_000); + const end = await getCrowdloanEndBlock(); + const nextIdBefore = await api.query.Crowdloan.NextCrowdloanId.getValue(); + + const createTx = await wrapperContract.createCrowdloan( + deposit, + minContribution, + cap, + end, + ethWallet2.address, + { value: raoToEth(deposit) } + ); + const receipt = await createTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForCrowdloanId(nextIdBefore + 1); + }, + }); + + it({ + id: "T16", + title: "Should get contributor share via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const crowdloanDeposit = BigInt(100_000_000_000); + const networkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue(); + const crowdloanCap = networkLastLockCost * BigInt(2); + const crowdloanEnd = await getCrowdloanEndBlock(); + const leasingEmissionsShare = 15; + const leasingEndBlock = crowdloanEnd + 200; + + const tx = api.tx.Crowdloan.create({ + deposit: crowdloanDeposit, + min_contribution: BigInt(1_000_000_000), + cap: crowdloanCap, + end: crowdloanEnd, + target_address: undefined, + call: api.tx.SubtensorModule.register_leased_network({ + emissions_share: leasingEmissionsShare, + end_block: leasingEndBlock, + }).decodedCall, + }); + await waitForTransactionWithRetry(api, tx, coldkey, "lease_crowdloan_create", 5); + await waitForFinalizedBlocks(api, 1); + + const nextLeaseId = await api.query.SubtensorModule.NextSubnetLeaseId.getValue(); + const share = await wrapperContract.getContributorShare(nextLeaseId, coldkey.publicKey); + expect(share).toBeDefined(); + }, + }); + + it({ + id: "T17", + title: "Should create lease crowdloan via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const crowdloanDeposit = BigInt(100_000_000_000); + const crowdloanMinContribution = BigInt(1_000_000_000); + const networkLastLockCost = await api.query.SubtensorModule.NetworkLastLockCost.getValue(); + const crowdloanCap = networkLastLockCost * BigInt(2); + const crowdloanEnd = await getCrowdloanEndBlock(); + const leasingEmissionsShare = 15; + const leasingEndBlock = crowdloanEnd + 200; + const nextCrowdloanIdBefore = await api.query.Crowdloan.NextCrowdloanId.getValue(); + + const createTx = await wrapperContract.createLeaseCrowdloan( + crowdloanDeposit, + crowdloanMinContribution, + crowdloanCap, + crowdloanEnd, + leasingEmissionsShare, + true, + leasingEndBlock, + { value: raoToEth(crowdloanDeposit) } + ); + const receipt = await createTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForCrowdloanId(nextCrowdloanIdBefore + 1); + }, + }); + + it({ + id: "T18", + title: "Should get proxies via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const accountKey = convertH160ToPublicKey(ethWallet.address); + const proxies = await wrapperContract.getProxies(accountKey); + expect(proxies).toBeDefined(); + expect(Array.isArray(proxies)).toBe(true); + }, + }); + + it({ + id: "T19", + title: "Should add proxy via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const delegate = generateKeyringPair("sr25519"); + await forceSetBalance(api, convertPublicKeyToSs58(delegate.publicKey)); + + const wrapperSs58 = convertH160ToSS58(wrapperAddress); + const proxiesBefore = await api.query.Proxy.Proxies.getValue(wrapperSs58); + + const addProxyTx = await wrapperContract.addProxy(delegate.publicKey, 0, 0); + const receipt = await addProxyTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForProxyCount(wrapperSs58, proxiesBefore[0].length + 1); + }, + }); + + it({ + id: "T20", + title: "Should proxy call via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const proxyType = 0; + const delay = 0; + const proxyContract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, ethWallet); + const addProxyTx = await proxyContract.addProxy( + convertH160ToPublicKey(wrapperAddress), + proxyType, + delay + ); + const receipt = await addProxyTx.wait(); + expect(receipt?.status).toEqual(1); + await waitForFinalizedBlocks(api, 1); + + const remarkCall = api.tx.System.remark({ remark: Binary.fromText("") }); + const callData = await remarkCall.getEncodedData(); + const data = callData.asBytes(); + + const proxyCallTx = await wrapperContract.proxyCall( + convertH160ToPublicKey(ethWallet.address), + [proxyType], + [...data] + ); + const proxyReceipt = await proxyCallTx.wait(); + expect(proxyReceipt?.status).toEqual(1); + }, + }); + + it({ + id: "T21", + title: "Should map address via wrapper", + test: async () => { + await ensureSubnetAndWrapperReady(); + + const mapped = await wrapperContract.addressMapping(ethWallet.address); + expect(mapped).toBeDefined(); + expect(mapped).not.toEqual("0x0000000000000000000000000000000000000000000000000000000000000000"); + }, + }); + }, +}); diff --git a/ts-tests/suites/zombienet_shield/01-scaling.test.ts b/ts-tests/suites/zombienet_shield/01-scaling.test.ts index d6072158f7..a83d5a3e2b 100644 --- a/ts-tests/suites/zombienet_shield/01-scaling.test.ts +++ b/ts-tests/suites/zombienet_shield/01-scaling.test.ts @@ -1,7 +1,10 @@ -import { expect, beforeAll } from "vitest"; -import type { PolkadotClient, TypedApi } from "polkadot-api"; -import { hexToU8a } from "@polkadot/util"; import { describeSuite } from "@moonwall/cli"; +import type { KeyringPair } from "@moonwall/util"; +import { MultiAddress, subtensor } from "@polkadot-api/descriptors"; +import { Keyring } from "@polkadot/keyring"; +import { hexToU8a } from "@polkadot/util"; +import type { PolkadotClient, TypedApi } from "polkadot-api"; +import { beforeAll, expect } from "vitest"; import { checkRuntime, getAccountNonce, @@ -11,9 +14,6 @@ import { 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", diff --git a/ts-tests/suites/zombienet_staking/02.04-claim-root-hotkey-swap.test.ts b/ts-tests/suites/zombienet_staking/02.04-claim-root-hotkey-swap.test.ts index 0124bae671..3578061b6f 100644 --- a/ts-tests/suites/zombienet_staking/02.04-claim-root-hotkey-swap.test.ts +++ b/ts-tests/suites/zombienet_staking/02.04-claim-root-hotkey-swap.test.ts @@ -68,7 +68,7 @@ async function setupTwoSubnetsWithClaimable( log(`Created netuid2: ${netuid2}`); for (const netuid of [netuid1, netuid2]) { - await sudoSetTempo(api, netuid, 1); + await sudoSetTempo(api, netuid, 5); await sudoSetEmaPriceHalvingPeriod(api, netuid, 1); await sudoSetRootClaimThreshold(api, netuid, 0n); } @@ -91,14 +91,15 @@ async function setupTwoSubnetsWithClaimable( await addStake(api, owner1Coldkey, owner1Hotkey.address, netuid1, tao(50)); await addStake(api, owner2Coldkey, owner2Hotkey.address, netuid2, tao(50)); - log("Waiting 30 blocks for RootClaimable to accumulate on both subnets..."); - await waitForBlocks(api, 30); + const waitBlocks = 90; + log(`Waiting ${waitBlocks} blocks for RootClaimable to accumulate on both subnets...`); + await waitForBlocks(api, waitBlocks); return { oldHotkey, oldHotkeyColdkey, newHotkey, netuid1, netuid2 }; } describeSuite({ - id: "0203_swap_hotkey_root_claimable", + id: "0204_claim-root_hotkey_swap", title: "▶ swap_hotkey RootClaimable per-subnet transfer", foundationMethods: "zombie", testCases: ({ it, context, log }) => { diff --git a/ts-tests/utils/address.ts b/ts-tests/utils/address.ts new file mode 100644 index 0000000000..eb4b1fe905 --- /dev/null +++ b/ts-tests/utils/address.ts @@ -0,0 +1,44 @@ +import { hexToU8a } from "@polkadot/util"; +import { blake2AsU8a, decodeAddress, encodeAddress } from "@polkadot/util-crypto"; +import { Binary } from "polkadot-api"; +import type { Address } from "viem"; + +const SS58_PREFIX = 42; + +export function toViemAddress(address: string): Address { + const addressNoPrefix = address.replace("0x", ""); + return `0x${addressNoPrefix}`; +} + +export function convertH160ToPublicKey(ethAddress: string) { + const prefix = "evm:"; + const prefixBytes = new TextEncoder().encode(prefix); + const addressBytes = hexToU8a(ethAddress.startsWith("0x") ? ethAddress : `0x${ethAddress}`); + const combined = new Uint8Array(prefixBytes.length + addressBytes.length); + combined.set(prefixBytes); + combined.set(addressBytes, prefixBytes.length); + return blake2AsU8a(combined); +} + +export function convertH160ToSS58(ethAddress: string): string { + return encodeAddress(convertH160ToPublicKey(ethAddress), SS58_PREFIX); +} + +export function convertPublicKeyToSs58(publicKey: Uint8Array): string { + return encodeAddress(publicKey, SS58_PREFIX); +} + +export function ss58ToEthAddress(ss58Address: string): string { + const publicKey = decodeAddress(ss58Address); + const ethereumAddressBytes = publicKey.slice(0, 20); + return `0x${Buffer.from(ethereumAddressBytes).toString("hex")}`; +} + +export function ss58ToH160(ss58Address: string): Binary { + const publicKey = decodeAddress(ss58Address); + return new Binary(publicKey.slice(0, 20)); +} + +export function ethAddressToH160(ethAddress: string): Binary { + return new Binary(hexToU8a(ethAddress.startsWith("0x") ? ethAddress : `0x${ethAddress}`)); +} diff --git a/ts-tests/utils/admin_utils.ts b/ts-tests/utils/admin_utils.ts index af1b591321..e6ecb6f953 100644 --- a/ts-tests/utils/admin_utils.ts +++ b/ts-tests/utils/admin_utils.ts @@ -1,7 +1,7 @@ +import type { subtensor } from "@polkadot-api/descriptors"; import { Keyring } from "@polkadot/keyring"; -import { waitForTransactionWithRetry } from "./transactions.js"; import type { TypedApi } from "polkadot-api"; -import type { subtensor } from "@polkadot-api/descriptors"; +import { waitForTransactionWithRetry } from "./transactions.js"; export async function sudoSetStakeThreshold(api: TypedApi, threshold: bigint): Promise { const keyring = new Keyring({ type: "sr25519" }); @@ -10,3 +10,22 @@ export async function sudoSetStakeThreshold(api: TypedApi, thr const tx = api.tx.Sudo.sudo({ call: inner.decodedCall }); await waitForTransactionWithRetry(api, tx, alice, "sudo_set_stake_threshold"); } + +export async function setTargetRegistrationsPerInterval( + api: TypedApi, + netuid: number +): Promise { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalTx = api.tx.AdminUtils.sudo_set_target_registrations_per_interval({ + netuid, + target_registrations_per_interval: 1000, + }); + const tx = api.tx.Sudo.sudo({ call: internalTx.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_target_registrations_per_interval"); + + const target = await api.query.SubtensorModule.TargetRegistrationsPerInterval.getValue(netuid); + if (target !== 1000) { + throw new Error(`Expected TargetRegistrationsPerInterval=1000 for netuid ${netuid}, got ${target}`); + } +} diff --git a/ts-tests/utils/balance.ts b/ts-tests/utils/balance.ts index f6fe83d3b0..ce739e57c2 100644 --- a/ts-tests/utils/balance.ts +++ b/ts-tests/utils/balance.ts @@ -1,14 +1,24 @@ -import { waitForTransactionWithRetry } from "./transactions.js"; -import type { TypedApi } from "polkadot-api"; -import { type subtensor, MultiAddress } from "@polkadot-api/descriptors"; +import type { subtensor } from "@polkadot-api/descriptors"; import { Keyring } from "@polkadot/keyring"; - +import type { TypedApi } from "polkadot-api"; +import { waitForFinalizedBlocks, waitForTransactionWithRetry } from "./transactions.js"; export const TAO = BigInt(1000000000); // 10^9 RAO per TAO +export const GWEI = BigInt(1000000000); +export const MAX_TX_FEE = BigInt(21000000) * GWEI; export function tao(value: number): bigint { return TAO * BigInt(value); } +/** Convert RAO to the EVM native balance unit (1 RAO → 1 gwei on-chain). */ +export function raoToEth(rao: bigint): bigint { + return GWEI * rao; +} + +export function bigintToRao(value: bigint): bigint { + return TAO * value; +} + export async function getBalance(api: TypedApi, ss58Address: string): Promise { const account = await api.query.System.Account.getValue(ss58Address); return account.data.free; @@ -19,6 +29,7 @@ export async function forceSetBalance( ss58Address: string, amount: bigint = tao(1e10) ): Promise { + const { MultiAddress } = await import("@polkadot-api/descriptors"); const keyring = new Keyring({ type: "sr25519" }); const alice = keyring.addFromUri("//Alice"); const internalCall = api.tx.Balances.force_set_balance({ @@ -27,4 +38,5 @@ export async function forceSetBalance( }); const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); await waitForTransactionWithRetry(api, tx, alice, "force_set_balance"); + await waitForFinalizedBlocks(api, 1); } diff --git a/ts-tests/utils/dev-helpers.ts b/ts-tests/utils/dev-helpers.ts new file mode 100644 index 0000000000..bc7b07659a --- /dev/null +++ b/ts-tests/utils/dev-helpers.ts @@ -0,0 +1,105 @@ +/** + * Polkadot.js (ApiPromise) compatible helpers for dev tests. + * Uses ApiPromise, not PAPI TypedApi — keep them separate. + */ +import type { ApiPromise } from "@polkadot/api"; +import type { KeyringPair } from "@moonwall/util"; +import { SignedOrder } from "./index.js"; + +export async function devForceSetBalance( + polkadotJs: ApiPromise, + context: any, + address: string, + amount: bigint +): Promise { + await context.createBlock([ + await polkadotJs.tx.sudo + .sudo(polkadotJs.tx.balances.forceSetBalance(address, amount)) + .signAsync(context.keyring.alice), + ]); +} + +export async function devAddStake( + polkadotJs: ApiPromise, + context: any, + coldkey: KeyringPair, + hotkey: string, + netuid: number, + amount: bigint +): Promise { + await context.createBlock([ + await polkadotJs.tx.subtensorModule.addStake(hotkey, netuid, amount).signAsync(coldkey), + ]); +} + +export async function devAssociateHotKey( + polkadotJs: ApiPromise, + context: any, + coldkey: KeyringPair, + hotkey: string +): Promise { + await context.createBlock([await polkadotJs.tx.subtensorModule.tryAssociateHotkey(hotkey).signAsync(coldkey)]); +} + +export async function devGetAlphaStake( + polkadotJs: ApiPromise, + hotkey: string, + coldkey: string, + netuid: number +): Promise { + const value = await polkadotJs.query.subtensorModule.alphaV2(hotkey, coldkey, netuid); + + const mantissa = value.mantissa; + const exponent = value.exponent; + + let result: bigint; + + if (exponent >= 0n) { + result = BigInt(mantissa) * BigInt(10) ** BigInt(exponent); + } else { + result = BigInt(mantissa) / BigInt(10) ** BigInt(-exponent); + } + + return result; +} + +export async function devSudoSetLockReductionInterval( + polkadotJs: ApiPromise, + context: any, + alice: KeyringPair, + interval: number +): Promise { + await context.createBlock([await polkadotJs.tx.adminUtils.sudoSetLockReductionInterval(interval).signAsync(alice)]); +} + +export async function devRegisterSubnet( + polkadotJs: ApiPromise, + context: any, + alice: KeyringPair, + hotkey: KeyringPair +): Promise { + await context.createBlock([await polkadotJs.tx.subtensorModule.registerNetwork(hotkey.address).signAsync(alice)]); + const events = (await polkadotJs.query.system.events()) as any; + const netuid = (events as any[]).filter((e: any) => e.event.method === "NetworkAdded")[0].event.data[0].toNumber(); + return netuid; +} + +export async function devEnableSubtoken( + polkadotJs: ApiPromise, + context: any, + alice: KeyringPair, + netuid: number +): Promise { + await context.createBlock([ + await polkadotJs.tx.sudo.sudo(polkadotJs.tx.adminUtils.sudoSetSubtokenEnabled(netuid, true)).signAsync(alice), + ]); +} +export async function devExecuteOrders( + polkadotJs: ApiPromise, + context: any, + alice: KeyringPair, + orders: SignedOrder[], + shouldFail = false +): Promise { + await context.createBlock([await polkadotJs.tx.limitOrders.executeOrders(orders, shouldFail).signAsync(alice)]); +} diff --git a/ts-tests/utils/evm-config.ts b/ts-tests/utils/evm-config.ts new file mode 100644 index 0000000000..7ea940d572 --- /dev/null +++ b/ts-tests/utils/evm-config.ts @@ -0,0 +1,2502 @@ +export const ISTAKING_ADDRESS = "0x0000000000000000000000000000000000000801"; +export const ISTAKING_V2_ADDRESS = "0x0000000000000000000000000000000000000805"; +export const IPROXY_ADDRESS = "0x000000000000000000000000000000000000080b"; + +export const IStakingABI = [ + { + inputs: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + ], + name: "addProxy", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "addStake", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + ], + name: "removeProxy", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "coldkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "getStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "removeStake", + outputs: [], + stateMutability: "payable", + type: "function", + }, +]; + +export const IStakingV2ABI = [ + { + inputs: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + ], + name: "addProxy", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "addStake", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "getAlphaStakedValidators", + outputs: [ + { + internalType: "uint256[]", + name: "", + type: "uint256[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "coldkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "getStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "getTotalAlphaStaked", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "coldkey", + type: "bytes32", + }, + ], + name: "getTotalColdkeyStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "coldkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "getTotalColdkeyStakeOnSubnet", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + ], + name: "getTotalHotkeyStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getNominatorMinRequiredStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + ], + name: "removeProxy", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "removeStake", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "limit_price", + type: "uint256", + }, + { + internalType: "bool", + name: "allow_partial", + type: "bool", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "addStakeLimit", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "limit_price", + type: "uint256", + }, + { + internalType: "bool", + name: "allow_partial", + type: "bool", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "removeStakeLimit", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "removeStakeFull", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "limit_price", + type: "uint256", + }, + ], + name: "removeStakeFullLimit", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "burnAlpha", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "spenderAddress", + type: "address", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "absoluteAmount", + type: "uint256", + }, + ], + name: "approve", + outputs: [], + stateMutability: "", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "sourceAddress", + type: "address", + }, + { + internalType: "address", + name: "spenderAddress", + type: "address", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "allowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "spenderAddress", + type: "address", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "increaseAmount", + type: "uint256", + }, + ], + name: "increaseAllowance", + outputs: [], + stateMutability: "", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "spenderAddress", + type: "address", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "decreaseAmount", + type: "uint256", + }, + ], + name: "decreaseAllowance", + outputs: [], + stateMutability: "", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "sourceAddress", + type: "address", + }, + { + internalType: "address", + name: "destinationAddress", + type: "address", + }, + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "originNetuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "destinationNetuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "transferStakeFrom", + outputs: [], + stateMutability: "", + type: "function", + }, +]; + +export const IProxyABI = [ + { + inputs: [ + { + internalType: "uint8", + name: "proxy_type", + type: "uint8", + }, + { + internalType: "uint32", + name: "delay", + type: "uint32", + }, + { + internalType: "uint16", + name: "index", + type: "uint16", + }, + ], + name: "createPureProxy", + outputs: [ + { + internalType: "bytes32", + name: "proxy", + type: "bytes32", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "spawner", + type: "bytes32", + }, + { + internalType: "uint8", + name: "proxy_type", + type: "uint8", + }, + { + internalType: "uint16", + name: "index", + type: "uint16", + }, + { + internalType: "uint32", + name: "height", + type: "uint32", + }, + { + internalType: "uint32", + name: "ext_index", + type: "uint32", + }, + ], + name: "killPureProxy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "real", + type: "bytes32", + }, + { + internalType: "uint8[]", + name: "force_proxy_type", + type: "uint8[]", + }, + { + internalType: "uint8[]", + name: "call", + type: "uint8[]", + }, + ], + name: "proxyCall", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "removeProxies", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "pokeDeposit", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + { + internalType: "uint8", + name: "proxy_type", + type: "uint8", + }, + { + internalType: "uint32", + name: "delay", + type: "uint32", + }, + ], + name: "removeProxy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + { + internalType: "uint8", + name: "proxy_type", + type: "uint8", + }, + { + internalType: "uint32", + name: "delay", + type: "uint32", + }, + ], + name: "addProxy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "account", + type: "bytes32", + }, + ], + name: "getProxies", + outputs: [ + { + components: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + { + internalType: "uint256", + name: "proxy_type", + type: "uint256", + }, + { + internalType: "uint256", + name: "delay", + type: "uint256", + }, + ], + internalType: "struct IProxy.ProxyInfo[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "view", + type: "function", + }, +]; + +export const IBALANCETRANSFER_ADDRESS = "0x0000000000000000000000000000000000000800"; + +export const IBalanceTransferABI = [ + { + inputs: [ + { + internalType: "bytes32", + name: "data", + type: "bytes32", + }, + ], + name: "transfer", + outputs: [], + stateMutability: "payable", + type: "function", + }, +] as const; + +export const WITHDRAW_CONTRACT_ABI = [ + { + inputs: [], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "withdraw", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + stateMutability: "payable", + type: "receive", + }, +] as const; + +export const WITHDRAW_CONTRACT_BYTECODE = + "6080604052348015600e575f80fd5b506101148061001c5f395ff3fe608060405260043610601e575f3560e01c80632e1a7d4d146028576024565b36602457005b5f80fd5b603e6004803603810190603a919060b8565b6040565b005b3373ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f193505050501580156082573d5f803e3d5ffd5b5050565b5f80fd5b5f819050919050565b609a81608a565b811460a3575f80fd5b50565b5f8135905060b2816093565b92915050565b5f6020828403121560ca5760c96086565b5b5f60d58482850160a6565b9150509291505056fea2646970667358221220f43400858bfe4fcc0bf3c1e2e06d3a9e6ced86454a00bd7e4866b3d4d64e46bb64736f6c634300081a0033"; +export const ALPHA_POOL_CONTRACT_ABI = [ + { + inputs: [ + { + internalType: "bytes32", + name: "_contract_hotkey", + type: "bytes32", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "ISTAKING_V2_ADDRESS", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "alphaBalance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "contract_coldkey", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "contract_hotkey", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "_alphaAmount", + type: "uint256", + }, + { + internalType: "bytes32", + name: "_hotkey", + type: "bytes32", + }, + ], + name: "depositAlpha", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "getContractStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "_contract_coldkey", + type: "bytes32", + }, + ], + name: "setContractColdkey", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "_alphaAmount", + type: "uint256", + }, + { + internalType: "bytes32", + name: "_user_coldkey", + type: "bytes32", + }, + ], + name: "withdrawAlpha", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +export const ALPHA_POOL_CONTRACT_BYTECODE = + "6080604052348015600e575f5ffd5b506040516110d83803806110d88339818101604052810190602e9190606c565b80600181905550506092565b5f5ffd5b5f819050919050565b604e81603e565b81146057575f5ffd5b50565b5f815190506066816047565b92915050565b5f60208284031215607e57607d603a565b5b5f608984828501605a565b91505092915050565b6110398061009f5f395ff3fe608060405234801561000f575f5ffd5b5060043610610086575f3560e01c8063cdcde3e911610059578063cdcde3e914610110578063d67c076114610140578063f0d6bb891461015e578063fdbcdce91461017a57610086565b80632849912d1461008a5780633af975ff146100a657806359948a67146100c4578063bee0bca1146100f4575b5f5ffd5b6100a4600480360381019061009f919061090d565b610198565b005b6100ae610518565b6040516100bb919061096c565b60405180910390f35b6100de60048036038101906100d991906109df565b61051d565b6040516100eb9190610a2c565b60405180910390f35b61010e6004803603810190610109919061090d565b61053d565b005b61012a60048036038101906101259190610a45565b610805565b6040516101379190610a2c565b60405180910390f35b61014861088e565b604051610155919061096c565b60405180910390f35b61017860048036038101906101739190610a70565b610894565b005b61018261089d565b60405161018f9190610aaa565b60405180910390f35b5f5f1b5f54036101dd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101d490610b1d565b60405180910390fd5b5f6101e784610805565b90505f6317ce5f6260e01b5f548487888860405160240161020c959493929190610b3b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516102949190610bde565b5f604051808303818686f4925050503d805f81146102cd576040519150601f19603f3d011682016040523d82523d5f602084013e6102d2565b606091505b5050905080610316576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030d90610c3e565b60405180910390fd5b5f61032087610805565b9050838111610364576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035b90610ccc565b60405180910390fd5b5f84826103719190610d17565b905060015486146104ac57631149f65960e01b866001548a8b8560405160240161039f959493929190610b3b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050935061080573ffffffffffffffffffffffffffffffffffffffff165a856040516104269190610bde565b5f604051808303815f8787f1925050503d805f8114610460576040519150601f19603f3d011682016040523d82523d5f602084013e610465565b606091505b505080935050826104ab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104a290610dba565b60405180910390fd5b5b8060025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8a81526020019081526020015f205f8282546105079190610dd8565b925050819055505050505050505050565b5f5481565b6002602052815f5260405f20602052805f5260405f205f91509150505481565b5f5f1b5f5403610582576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057990610b1d565b60405180910390fd5b8160025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8581526020019081526020015f20541015610611576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060890610e7b565b60405180910390fd5b5f61061b84610805565b90508260025f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8681526020019081526020015f205f8282546106789190610d17565b925050819055505f6317ce5f6260e01b836001548788886040516024016106a3959493929190610b3b565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a8360405161072b9190610bde565b5f604051808303815f8787f1925050503d805f8114610765576040519150601f19603f3d011682016040523d82523d5f602084013e61076a565b606091505b505090505f61077887610805565b90508381106107bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b390610f09565b60405180910390fd5b816107fc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107f390610f71565b60405180910390fd5b50505050505050565b5f61080573ffffffffffffffffffffffffffffffffffffffff1663e3b598fa6001545f54856040518463ffffffff1660e01b815260040161084893929190610f8f565b602060405180830381865afa158015610863573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108879190610fd8565b9050919050565b60015481565b805f8190555050565b61080581565b5f5ffd5b5f819050919050565b6108b9816108a7565b81146108c3575f5ffd5b50565b5f813590506108d4816108b0565b92915050565b5f819050919050565b6108ec816108da565b81146108f6575f5ffd5b50565b5f81359050610907816108e3565b92915050565b5f5f5f60608486031215610924576109236108a3565b5b5f610931868287016108c6565b9350506020610942868287016108c6565b9250506040610953868287016108f9565b9150509250925092565b610966816108da565b82525050565b5f60208201905061097f5f83018461095d565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6109ae82610985565b9050919050565b6109be816109a4565b81146109c8575f5ffd5b50565b5f813590506109d9816109b5565b92915050565b5f5f604083850312156109f5576109f46108a3565b5b5f610a02858286016109cb565b9250506020610a13858286016108c6565b9150509250929050565b610a26816108a7565b82525050565b5f602082019050610a3f5f830184610a1d565b92915050565b5f60208284031215610a5a57610a596108a3565b5b5f610a67848285016108c6565b91505092915050565b5f60208284031215610a8557610a846108a3565b5b5f610a92848285016108f9565b91505092915050565b610aa4816109a4565b82525050565b5f602082019050610abd5f830184610a9b565b92915050565b5f82825260208201905092915050565b7f636f6e747261637420636f6c646b6579206e6f742073657400000000000000005f82015250565b5f610b07601883610ac3565b9150610b1282610ad3565b602082019050919050565b5f6020820190508181035f830152610b3481610afb565b9050919050565b5f60a082019050610b4e5f83018861095d565b610b5b602083018761095d565b610b686040830186610a1d565b610b756060830185610a1d565b610b826080830184610a1d565b9695505050505050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f610bb882610b8c565b610bc28185610b96565b9350610bd2818560208601610ba0565b80840191505092915050565b5f610be98284610bae565b915081905092915050565b7f75736572206465706f73697420616c7068612063616c6c206661696c656400005f82015250565b5f610c28601e83610ac3565b9150610c3382610bf4565b602082019050919050565b5f6020820190508181035f830152610c5581610c1c565b9050919050565b7f636f6e7472616374207374616b652064656372656173656420616674657220645f8201527f65706f7369740000000000000000000000000000000000000000000000000000602082015250565b5f610cb6602683610ac3565b9150610cc182610c5c565b604082019050919050565b5f6020820190508181035f830152610ce381610caa565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610d21826108a7565b9150610d2c836108a7565b9250828203905081811115610d4457610d43610cea565b5b92915050565b7f75736572206465706f7369742c206d6f7665207374616b652063616c6c2066615f8201527f696c656400000000000000000000000000000000000000000000000000000000602082015250565b5f610da4602483610ac3565b9150610daf82610d4a565b604082019050919050565b5f6020820190508181035f830152610dd181610d98565b9050919050565b5f610de2826108a7565b9150610ded836108a7565b9250828201905080821115610e0557610e04610cea565b5b92915050565b7f757365722077697468647261772c20696e73756666696369656e7420616c70685f8201527f612062616c616e63650000000000000000000000000000000000000000000000602082015250565b5f610e65602983610ac3565b9150610e7082610e0b565b604082019050919050565b5f6020820190508181035f830152610e9281610e59565b9050919050565b7f636f6e7472616374207374616b6520696e6372656173656420616674657220775f8201527f6974686472617700000000000000000000000000000000000000000000000000602082015250565b5f610ef3602783610ac3565b9150610efe82610e99565b604082019050919050565b5f6020820190508181035f830152610f2081610ee7565b9050919050565b7f7573657220776974686472617720616c7068612063616c6c206661696c6564005f82015250565b5f610f5b601f83610ac3565b9150610f6682610f27565b602082019050919050565b5f6020820190508181035f830152610f8881610f4f565b9050919050565b5f606082019050610fa25f83018661095d565b610faf602083018561095d565b610fbc6040830184610a1d565b949350505050565b5f81519050610fd2816108b0565b92915050565b5f60208284031215610fed57610fec6108a3565b5b5f610ffa84828501610fc4565b9150509291505056fea2646970667358221220a0d4b2eb5f0c7f74a27f987e803ae1c8465e0da35f09c240ddb6bac757ce422164736f6c634300081e0033"; + +export const BRIDGE_TOKEN_CONTRACT_ABI = [ + { + inputs: [ + { + internalType: "string", + name: "name_", + type: "string", + }, + { + internalType: "string", + name: "symbol_", + type: "string", + }, + { + internalType: "address", + name: "admin", + type: "address", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "AccessControlBadConfirmation", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + { + internalType: "bytes32", + name: "neededRole", + type: "bytes32", + }, + ], + name: "AccessControlUnauthorizedAccount", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "allowance", + type: "uint256", + }, + { + internalType: "uint256", + name: "needed", + type: "uint256", + }, + ], + name: "ERC20InsufficientAllowance", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "sender", + type: "address", + }, + { + internalType: "uint256", + name: "balance", + type: "uint256", + }, + { + internalType: "uint256", + name: "needed", + type: "uint256", + }, + ], + name: "ERC20InsufficientBalance", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "approver", + type: "address", + }, + ], + name: "ERC20InvalidApprover", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "receiver", + type: "address", + }, + ], + name: "ERC20InvalidReceiver", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "ERC20InvalidSender", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + ], + name: "ERC20InvalidSpender", + type: "error", + }, + { + inputs: [], + name: "UnauthorizedHandler", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "spender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "previousAdminRole", + type: "bytes32", + }, + { + indexed: true, + internalType: "bytes32", + name: "newAdminRole", + type: "bytes32", + }, + ], + name: "RoleAdminChanged", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleGranted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + indexed: true, + internalType: "address", + name: "account", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "RoleRevoked", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Transfer", + type: "event", + }, + { + inputs: [], + name: "DEFAULT_ADMIN_ROLE", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address", + }, + { + internalType: "address", + name: "spender", + type: "address", + }, + ], + name: "allowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "burn", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "from", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "burnFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "decimals", + outputs: [ + { + internalType: "uint8", + name: "", + type: "uint8", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + ], + name: "getRoleAdmin", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "grantRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "hasRole", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "isAdmin", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "mint", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "callerConfirmation", + type: "address", + }, + ], + name: "renounceRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "role", + type: "bytes32", + }, + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "revokeRole", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes4", + name: "interfaceId", + type: "bytes4", + }, + ], + name: "supportsInterface", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalSupply", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "from", + type: "address", + }, + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "transferFrom", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, +]; + +export const BRIDGE_TOKEN_CONTRACT_BYTECODE = + "0x60806040523480156200001157600080fd5b5060405162000fac38038062000fac8339810160408190526200003491620001ea565b8282600362000044838262000308565b50600462000053828262000308565b5062000065915060009050826200006f565b50505050620003d4565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff16620001185760008381526005602090815260408083206001600160a01b03861684529091529020805460ff19166001179055620000cf3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016200011c565b5060005b92915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200014a57600080fd5b81516001600160401b038082111562000167576200016762000122565b604051601f8301601f19908116603f0116810190828211818310171562000192576200019262000122565b8160405283815260209250866020858801011115620001b057600080fd5b600091505b83821015620001d45785820183015181830184015290820190620001b5565b6000602085830101528094505050505092915050565b6000806000606084860312156200020057600080fd5b83516001600160401b03808211156200021857600080fd5b620002268783880162000138565b945060208601519150808211156200023d57600080fd5b506200024c8682870162000138565b604086015190935090506001600160a01b03811681146200026c57600080fd5b809150509250925092565b600181811c908216806200028c57607f821691505b602082108103620002ad57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000303576000816000526020600020601f850160051c81016020861015620002de5750805b601f850160051c820191505b81811015620002ff57828155600101620002ea565b5050505b505050565b81516001600160401b0381111562000324576200032462000122565b6200033c8162000335845462000277565b84620002b3565b602080601f8311600181146200037457600084156200035b5750858301515b600019600386901b1c1916600185901b178555620002ff565b600085815260208120601f198616915b82811015620003a55788860151825594840194600190910190840162000384565b5085821015620003c45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610bc880620003e46000396000f3fe608060405234801561001057600080fd5b506004361061012c5760003560e01c806340c10f19116100ad57806395d89b411161007157806395d89b4114610288578063a217fddf14610290578063a9059cbb14610298578063d547741f146102ab578063dd62ed3e146102be57600080fd5b806340c10f191461021357806342966c681461022657806370a082311461023957806379cc67901461026257806391d148541461027557600080fd5b8063248a9ca3116100f4578063248a9ca3146101a657806324d7806c146101c95780632f2ff15d146101dc578063313ce567146101f157806336568abe1461020057600080fd5b806301ffc9a71461013157806306fdde0314610159578063095ea7b31461016e57806318160ddd1461018157806323b872dd14610193575b600080fd5b61014461013f3660046109ab565b6102f7565b60405190151581526020015b60405180910390f35b61016161032e565b60405161015091906109dc565b61014461017c366004610a47565b6103c0565b6002545b604051908152602001610150565b6101446101a1366004610a71565b6103d8565b6101856101b4366004610aad565b60009081526005602052604090206001015490565b6101446101d7366004610ac6565b6103fc565b6101ef6101ea366004610ae1565b610408565b005b60405160128152602001610150565b6101ef61020e366004610ae1565b610433565b6101ef610221366004610a47565b61046b565b6101ef610234366004610aad565b610480565b610185610247366004610ac6565b6001600160a01b031660009081526020819052604090205490565b6101ef610270366004610a47565b61048d565b610144610283366004610ae1565b6104a2565b6101616104cd565b610185600081565b6101446102a6366004610a47565b6104dc565b6101ef6102b9366004610ae1565b6104ea565b6101856102cc366004610b0d565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60006001600160e01b03198216637965db0b60e01b148061032857506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606003805461033d90610b37565b80601f016020809104026020016040519081016040528092919081815260200182805461036990610b37565b80156103b65780601f1061038b576101008083540402835291602001916103b6565b820191906000526020600020905b81548152906001019060200180831161039957829003601f168201915b5050505050905090565b6000336103ce81858561050f565b5060019392505050565b6000336103e685828561051c565b6103f1858585610599565b506001949350505050565b600061032881836104a2565b600082815260056020526040902060010154610423816105f8565b61042d8383610602565b50505050565b6001600160a01b038116331461045c5760405163334bd91960e11b815260040160405180910390fd5b6104668282610696565b505050565b6000610476816105f8565b6104668383610703565b61048a338261073d565b50565b6000610498816105f8565b610466838361073d565b60009182526005602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60606004805461033d90610b37565b6000336103ce818585610599565b600082815260056020526040902060010154610505816105f8565b61042d8383610696565b6104668383836001610773565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461042d578181101561058a57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61042d84848484036000610773565b6001600160a01b0383166105c357604051634b637e8f60e11b815260006004820152602401610581565b6001600160a01b0382166105ed5760405163ec442f0560e01b815260006004820152602401610581565b610466838383610848565b61048a8133610972565b600061060e83836104a2565b61068e5760008381526005602090815260408083206001600160a01b03861684529091529020805460ff191660011790556106463390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610328565b506000610328565b60006106a283836104a2565b1561068e5760008381526005602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610328565b6001600160a01b03821661072d5760405163ec442f0560e01b815260006004820152602401610581565b61073960008383610848565b5050565b6001600160a01b03821661076757604051634b637e8f60e11b815260006004820152602401610581565b61073982600083610848565b6001600160a01b03841661079d5760405163e602df0560e01b815260006004820152602401610581565b6001600160a01b0383166107c757604051634a1406b160e11b815260006004820152602401610581565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561042d57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161083a91815260200190565b60405180910390a350505050565b6001600160a01b0383166108735780600260008282546108689190610b71565b909155506108e59050565b6001600160a01b038316600090815260208190526040902054818110156108c65760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610581565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b03821661090157600280548290039055610920565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161096591815260200190565b60405180910390a3505050565b61097c82826104a2565b6107395760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610581565b6000602082840312156109bd57600080fd5b81356001600160e01b0319811681146109d557600080fd5b9392505050565b60006020808352835180602085015260005b81811015610a0a578581018301518582016040015282016109ee565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610a4257600080fd5b919050565b60008060408385031215610a5a57600080fd5b610a6383610a2b565b946020939093013593505050565b600080600060608486031215610a8657600080fd5b610a8f84610a2b565b9250610a9d60208501610a2b565b9150604084013590509250925092565b600060208284031215610abf57600080fd5b5035919050565b600060208284031215610ad857600080fd5b6109d582610a2b565b60008060408385031215610af457600080fd5b82359150610b0460208401610a2b565b90509250929050565b60008060408385031215610b2057600080fd5b610b2983610a2b565b9150610b0460208401610a2b565b600181811c90821680610b4b57607f821691505b602082108103610b6b57634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561032857634e487b7160e01b600052601160045260246000fdfea2646970667358221220e179fc58c926e64cb6e87416f8ca64c117044e3195b184afe45038857606c15364736f6c63430008160033"; + +export const PRECOMPILE_WRAPPER_ABI = [ + { + inputs: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + { + internalType: "uint8", + name: "proxy_type", + type: "uint8", + }, + { + internalType: "uint32", + name: "delay", + type: "uint32", + }, + ], + name: "addProxy", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "addStake", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "target_address", + type: "address", + }, + ], + name: "addressMapping", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "addressMappingPrecompile", + outputs: [ + { + internalType: "contract IAddressMapping", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "alpha", + outputs: [ + { + internalType: "contract IAlpha", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "balanceTransfer", + outputs: [ + { + internalType: "contract ISubtensorBalanceTransfer", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "netuid", + type: "uint16", + }, + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + ], + name: "burnedRegister", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint64", + name: "deposit", + type: "uint64", + }, + { + internalType: "uint64", + name: "minContribution", + type: "uint64", + }, + { + internalType: "uint64", + name: "cap", + type: "uint64", + }, + { + internalType: "uint32", + name: "end", + type: "uint32", + }, + { + internalType: "address", + name: "targetAddress", + type: "address", + }, + ], + name: "createCrowdloan", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint64", + name: "crowdloanDeposit", + type: "uint64", + }, + { + internalType: "uint64", + name: "crowdloanMinContribution", + type: "uint64", + }, + { + internalType: "uint64", + name: "crowdloanCap", + type: "uint64", + }, + { + internalType: "uint32", + name: "crowdloanEnd", + type: "uint32", + }, + { + internalType: "uint8", + name: "leasingEmissionsShare", + type: "uint8", + }, + { + internalType: "bool", + name: "hasLeasingEndBlock", + type: "bool", + }, + { + internalType: "uint32", + name: "leasingEndBlock", + type: "uint32", + }, + ], + name: "createLeaseCrowdloan", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "crowdloan", + outputs: [ + { + internalType: "contract ICrowdloan", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "netuid", + type: "uint16", + }, + ], + name: "getAlphaPrice", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint32", + name: "crowdloanId", + type: "uint32", + }, + { + internalType: "bytes32", + name: "coldkey", + type: "bytes32", + }, + ], + name: "getContribution", + outputs: [ + { + internalType: "uint64", + name: "", + type: "uint64", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint32", + name: "leaseId", + type: "uint32", + }, + { + internalType: "bytes32", + name: "contributor", + type: "bytes32", + }, + ], + name: "getContributorShare", + outputs: [ + { + internalType: "uint128", + name: "", + type: "uint128", + }, + { + internalType: "uint128", + name: "", + type: "uint128", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint32", + name: "crowdloanId", + type: "uint32", + }, + ], + name: "getCrowdloan", + outputs: [ + { + components: [ + { + internalType: "bytes32", + name: "creator", + type: "bytes32", + }, + { + internalType: "uint64", + name: "deposit", + type: "uint64", + }, + { + internalType: "uint64", + name: "min_contribution", + type: "uint64", + }, + { + internalType: "uint32", + name: "end", + type: "uint32", + }, + { + internalType: "uint64", + name: "cap", + type: "uint64", + }, + { + internalType: "bytes32", + name: "funds_account", + type: "bytes32", + }, + { + internalType: "uint64", + name: "raised", + type: "uint64", + }, + { + internalType: "bool", + name: "has_target_address", + type: "bool", + }, + { + internalType: "bytes32", + name: "target_address", + type: "bytes32", + }, + { + internalType: "bool", + name: "finalized", + type: "bool", + }, + { + internalType: "uint32", + name: "contributors_count", + type: "uint32", + }, + ], + internalType: "struct CrowdloanInfo", + name: "", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "account", + type: "bytes32", + }, + ], + name: "getProxies", + outputs: [ + { + components: [ + { + internalType: "bytes32", + name: "delegate", + type: "bytes32", + }, + { + internalType: "uint256", + name: "proxy_type", + type: "uint256", + }, + { + internalType: "uint256", + name: "delay", + type: "uint256", + }, + ], + internalType: "struct IProxy.ProxyInfo[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "netuid", + type: "uint16", + }, + ], + name: "getServingRateLimit", + outputs: [ + { + internalType: "uint64", + name: "", + type: "uint64", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "netuid", + type: "uint16", + }, + ], + name: "getNetworkRegistrationBlock", + outputs: [ + { + internalType: "uint64", + name: "", + type: "uint64", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "coldkey", + type: "bytes32", + }, + ], + name: "getTotalColdkeyStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + ], + name: "getTotalHotkeyStake", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "netuid", + type: "uint16", + }, + ], + name: "getUidCount", + outputs: [ + { + internalType: "uint16", + name: "", + type: "uint16", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "leasing", + outputs: [ + { + internalType: "contract ILeasing", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "metagraph", + outputs: [ + { + internalType: "contract IMetagraph", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "neuron", + outputs: [ + { + internalType: "contract INeuron", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "proxy", + outputs: [ + { + internalType: "contract IProxy", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "real", + type: "bytes32", + }, + { + internalType: "uint8[]", + name: "force_proxy_type", + type: "uint8[]", + }, + { + internalType: "uint8[]", + name: "call", + type: "uint8[]", + }, + ], + name: "proxyCall", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "string", + name: "subnetName", + type: "string", + }, + { + internalType: "string", + name: "githubRepo", + type: "string", + }, + { + internalType: "string", + name: "subnetContact", + type: "string", + }, + { + internalType: "string", + name: "subnetUrl", + type: "string", + }, + { + internalType: "string", + name: "discord", + type: "string", + }, + { + internalType: "string", + name: "description", + type: "string", + }, + { + internalType: "string", + name: "additional", + type: "string", + }, + ], + name: "registerNetworkWithDetails", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + ], + name: "removeStake", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "staking", + outputs: [ + { + internalType: "contract IStaking", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "subnet", + outputs: [ + { + internalType: "contract ISubnet", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "data", + type: "bytes32", + }, + ], + name: "transfer", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint16", + name: "netuid", + type: "uint16", + }, + { + internalType: "address", + name: "evm_address", + type: "address", + }, + { + internalType: "uint16", + name: "limit", + type: "uint16", + }, + ], + name: "uidLookup", + outputs: [ + { + components: [ + { + internalType: "uint16", + name: "uid", + type: "uint16", + }, + { + internalType: "uint64", + name: "block_associated", + type: "uint64", + }, + ], + internalType: "struct LookupItem[]", + name: "", + type: "tuple[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "uidLookupPrecompile", + outputs: [ + { + internalType: "contract IUidLookup", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, +]; + +export const PRECOMPILE_WRAPPER_BYTECODE = + "6080604052348015600e575f5ffd5b506119368061001c5f395ff3fe6080604052600436106101da575f3560e01c80638bba466c116100fd578063b1f789ef11610092578063d75e3e0d11610062578063d75e3e0d14610547578063db1d0fd51461055c578063ec55688914610571578063fc6679fb14610586575f5ffd5b8063b1f789ef146104de578063bfe252a21461050a578063caf2ebf21461051f578063cd6f4eb114610534575f5ffd5b8063a2176276116100cd578063a217627614610482578063ac3166bf14610497578063afed65f9146104ac578063b0c751b0146104bf575f5ffd5b80638bba466c146103ec57806394e3ac6f14610418578063998538c4146104445780639f246f6f14610463575f5ffd5b80634cf088d91161017357806369e38bc31161014357806369e38bc31461038857806371214e27146103a75780637444dadc146103ba5780637d691e30146103d9575f5ffd5b80634cf088d9146103145780635b53ddde146103295780635b7210c51461033e5780635e25f3f814610375575f5ffd5b80631fc9b141116101ae5780631fc9b141146102825780633175bd98146102955780634054ecca146102d45780634c378a96146102e7575f5ffd5b80620ae759146101de5780630494cd9a146101ff5780630cadeda5146102315780631f19357214610250575b5f5ffd5b3480156101e9575f5ffd5b506101fd6101f8366004610e85565b61059b565b005b34801561020a575f5ffd5b5061021e610219366004610f06565b6105f4565b6040519081526020015b60405180910390f35b34801561023c575f5ffd5b506101fd61024b366004610f33565b610665565b34801561025b575f5ffd5b5061026f61026a366004610f7f565b6106a0565b60405161ffff9091168152602001610228565b6101fd610290366004610f9a565b610705565b3480156102a0575f5ffd5b506102b46102af366004610fc3565b610739565b604080516001600160801b03938416815292909116602083015201610228565b6101fd6102e2366004610fed565b6107b3565b3480156102f2575f5ffd5b506102fc61080481565b6040516001600160a01b039091168152602001610228565b34801561031f575f5ffd5b506102fc61080581565b348015610334575f5ffd5b506102fc61080a81565b348015610349575f5ffd5b5061035d610358366004610fc3565b6107f7565b6040516001600160401b039091168152602001610228565b6101fd610383366004611074565b61086c565b348015610393575f5ffd5b5061021e6103a2366004610f7f565b6108d6565b6101fd6103b53660046111c2565b610901565b3480156103c5575f5ffd5b5061035d6103d4366004610f7f565b610989565b6101fd6103e7366004610f9a565b6109ef565b3480156103f7575f5ffd5b5061040b61040636600461122b565b610a23565b6040516102289190611246565b348015610423575f5ffd5b50610437610432366004611334565b610add565b604051610228919061134b565b34801561044f575f5ffd5b5061021e61045e366004611334565b610b42565b34801561046e575f5ffd5b5061021e61047d366004611334565b610b6a565b34801561048d575f5ffd5b506102fc61080681565b3480156104a2575f5ffd5b506102fc61080c81565b6101fd6104ba3660046113b6565b610b92565b3480156104ca575f5ffd5b5061035d6104d9366004610f7f565b610c26565b3480156104e9575f5ffd5b506104fd6104f8366004611445565b610c51565b6040516102289190611480565b348015610515575f5ffd5b506102fc61080981565b34801561052a575f5ffd5b506102fc61080381565b6101fd610542366004611334565b610cd8565b348015610552575f5ffd5b506102fc61080081565b348015610567575f5ffd5b506102fc61080881565b34801561057c575f5ffd5b506102fc61080b81565b348015610591575f5ffd5b506102fc61080281565b604051620ae75960e01b815261080b90620ae759906105c29086908690869060040161150d565b5f604051808303815f87803b1580156105d9575f5ffd5b505af11580156105eb573d5f5f3e3d5ffd5b50505050505050565b60405163024a66cd60e11b81526001600160a01b03821660048201525f9061080c90630494cd9a906024015b602060405180830381865afa15801561063b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061065f9190611541565b92915050565b604051630cadeda560e01b81526004810184905260ff8316602482015263ffffffff8216604482015261080b90630cadeda5906064016105c2565b604051630f8c9ab960e11b815261ffff821660048201525f9061080290631f19357290602401602060405180830381865afa1580156106e1573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061065f9190611558565b604051631fc9b14160e01b815260048101849052602481018390526044810182905261080590631fc9b141906064016105c2565b60405163062eb7b360e31b815263ffffffff83166004820152602481018290525f90819061080a90633175bd98906044016040805180830381865afa158015610784573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107a89190611589565b915091509250929050565b60405163202a766560e11b815261ffff831660048201526024810182905261080490634054ecca9034906044015f604051808303818588803b1580156105d9575f5ffd5b604051635b7210c560e01b815263ffffffff83166004820152602481018290525f9061080990635b7210c590604401602060405180830381865afa158015610841573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061086591906115c5565b9392505050565b604051631cf98c6b60e01b815261080390631cf98c6b9061089f908b908b908b908b908b908b908b908b9060040161160e565b5f604051808303815f87803b1580156108b6575f5ffd5b505af11580156108c8573d5f5f3e3d5ffd5b505050505050505050505050565b6040516369e38bc360e01b815261ffff821660048201525f90610808906369e38bc390602401610620565b60405163127e1adb60e01b81526001600160401b03808716600483015280861660248301528416604482015263ffffffff831660648201526001600160a01b03821660848201526108099063127e1adb9060a4015f604051808303815f87803b15801561096c575f5ffd5b505af115801561097e573d5f5f3e3d5ffd5b505050505050505050565b604051631d1136b760e21b815261ffff821660048201525f9061080390637444dadc906024015b602060405180830381865afa1580156109cb573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061065f91906115c5565b6040516307d691e360e41b815260048101849052602481018390526044810182905261080590637d691e30906064016105c2565b60408051610160810182525f80825260208201819052818301819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101008201819052610120820181905261014082015290516322ee919b60e21b815263ffffffff8316600482015261080990638bba466c9060240161016060405180830381865afa158015610ab9573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061065f91906116c3565b6040516394e3ac6f60e01b81526004810182905260609061080b906394e3ac6f906024015f60405180830381865afa158015610b1b573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261065f919081019061178a565b6040516326614e3160e21b8152600481018290525f906108059063998538c490602401610620565b604051639f246f6f60e01b8152600481018290525f9061080590639f246f6f90602401610620565b60405163afed65f960e01b81526001600160401b03808916600483015280881660248301528616604482015263ffffffff808616606483015260ff8516608483015283151560a4830152821660c482015261080a9063afed65f99060e4015f604051808303815f87803b158015610c07575f5ffd5b505af1158015610c19573d5f5f3e3d5ffd5b5050505050505050505050565b604051630b0c751b60e41b815261ffff821660048201525f906108039063b0c751b0906024016109b0565b60405163b1f789ef60e01b815261ffff80851660048301526001600160a01b0384166024830152821660448201526060906108069063b1f789ef906064015f60405180830381865afa158015610ca9573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610cd0919081019061183f565b949350505050565b60405163cd6f4eb160e01b8152600481018290526108009063cd6f4eb19034906024015f604051808303818588803b158015610d12575f5ffd5b505af1158015610d24573d5f5f3e3d5ffd5b505050505050565b634e487b7160e01b5f52604160045260245ffd5b60405161016081016001600160401b0381118282101715610d6357610d63610d2c565b60405290565b604051606081016001600160401b0381118282101715610d6357610d63610d2c565b604080519081016001600160401b0381118282101715610d6357610d63610d2c565b604051601f8201601f191681016001600160401b0381118282101715610dd557610dd5610d2c565b604052919050565b5f6001600160401b03821115610df557610df5610d2c565b5060051b60200190565b803560ff81168114610e0f575f5ffd5b919050565b5f82601f830112610e23575f5ffd5b8135610e36610e3182610ddd565b610dad565b8082825260208201915060208360051b860101925085831115610e57575f5ffd5b602085015b83811015610e7b57610e6d81610dff565b835260209283019201610e5c565b5095945050505050565b5f5f5f60608486031215610e97575f5ffd5b8335925060208401356001600160401b03811115610eb3575f5ffd5b610ebf86828701610e14565b92505060408401356001600160401b03811115610eda575f5ffd5b610ee686828701610e14565b9150509250925092565b80356001600160a01b0381168114610e0f575f5ffd5b5f60208284031215610f16575f5ffd5b61086582610ef0565b63ffffffff81168114610f30575f5ffd5b50565b5f5f5f60608486031215610f45575f5ffd5b83359250610f5560208501610dff565b91506040840135610f6581610f1f565b809150509250925092565b61ffff81168114610f30575f5ffd5b5f60208284031215610f8f575f5ffd5b813561086581610f70565b5f5f5f60608486031215610fac575f5ffd5b505081359360208301359350604090920135919050565b5f5f60408385031215610fd4575f5ffd5b8235610fdf81610f1f565b946020939093013593505050565b5f5f60408385031215610ffe575f5ffd5b8235610fdf81610f70565b5f82601f830112611018575f5ffd5b81356001600160401b0381111561103157611031610d2c565b611044601f8201601f1916602001610dad565b818152846020838601011115611058575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f5f5f5f5f5f5f5f610100898b03121561108c575f5ffd5b8835975060208901356001600160401b038111156110a8575f5ffd5b6110b48b828c01611009565b97505060408901356001600160401b038111156110cf575f5ffd5b6110db8b828c01611009565b96505060608901356001600160401b038111156110f6575f5ffd5b6111028b828c01611009565b95505060808901356001600160401b0381111561111d575f5ffd5b6111298b828c01611009565b94505060a08901356001600160401b03811115611144575f5ffd5b6111508b828c01611009565b93505060c08901356001600160401b0381111561116b575f5ffd5b6111778b828c01611009565b92505060e08901356001600160401b03811115611192575f5ffd5b61119e8b828c01611009565b9150509295985092959890939650565b6001600160401b0381168114610f30575f5ffd5b5f5f5f5f5f60a086880312156111d6575f5ffd5b85356111e1816111ae565b945060208601356111f1816111ae565b93506040860135611201816111ae565b9250606086013561121181610f1f565b915061121f60808701610ef0565b90509295509295909350565b5f6020828403121561123b575f5ffd5b813561086581610f1f565b8151815260208083015161016083019161126a908401826001600160401b03169052565b50604083015161128560408401826001600160401b03169052565b50606083015161129d606084018263ffffffff169052565b5060808301516112b860808401826001600160401b03169052565b5060a083015160a083015260c08301516112dd60c08401826001600160401b03169052565b5060e08301516112f160e084018215159052565b5061010083015161010083015261012083015161131361012084018215159052565b5061014083015161132d61014084018263ffffffff169052565b5092915050565b5f60208284031215611344575f5ffd5b5035919050565b602080825282518282018190525f918401906040840190835b8181101561139e57835180518452602081015160208501526040810151604085015250606083019250602084019350600181019050611364565b509095945050505050565b8015158114610f30575f5ffd5b5f5f5f5f5f5f5f60e0888a0312156113cc575f5ffd5b87356113d7816111ae565b965060208801356113e7816111ae565b955060408801356113f7816111ae565b9450606088013561140781610f1f565b935061141560808901610dff565b925060a0880135611425816113a9565b915060c088013561143581610f1f565b8091505092959891949750929550565b5f5f5f60608486031215611457575f5ffd5b833561146281610f70565b925061147060208501610ef0565b91506040840135610f6581610f70565b602080825282518282018190525f918401906040840190835b8181101561139e578351805161ffff1684526020908101516001600160401b03168185015290930192604090920191600101611499565b5f8151808452602084019350602083015f5b8281101561150357815160ff168652602095860195909101906001016114e2565b5093949350505050565b838152606060208201525f61152560608301856114d0565b828103604084015261153781856114d0565b9695505050505050565b5f60208284031215611551575f5ffd5b5051919050565b5f60208284031215611568575f5ffd5b815161086581610f70565b80516001600160801b0381168114610e0f575f5ffd5b5f5f6040838503121561159a575f5ffd5b6115a383611573565b91506115b160208401611573565b90509250929050565b8051610e0f816111ae565b5f602082840312156115d5575f5ffd5b8151610865816111ae565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b88815261010060208201525f61162861010083018a6115e0565b828103604084015261163a818a6115e0565b9050828103606084015261164e81896115e0565b9050828103608084015261166281886115e0565b905082810360a084015261167681876115e0565b905082810360c084015261168a81866115e0565b905082810360e084015261169e81856115e0565b9b9a5050505050505050505050565b8051610e0f81610f1f565b8051610e0f816113a9565b5f6101608284031280156116d5575f5ffd5b506116de610d40565b825181526116ee602084016115ba565b60208201526116ff604084016115ba565b6040820152611710606084016116ad565b6060820152611721608084016115ba565b608082015260a0838101519082015261173c60c084016115ba565b60c082015261174d60e084016116b8565b60e0820152610100838101519082015261176a61012084016116b8565b61012082015261177d61014084016116ad565b6101408201529392505050565b5f6020828403121561179a575f5ffd5b81516001600160401b038111156117af575f5ffd5b8201601f810184136117bf575f5ffd5b80516117cd610e3182610ddd565b808282526020820191506020606084028501019250868311156117ee575f5ffd5b6020840193505b82841015611537576060848803121561180c575f5ffd5b611814610d69565b84518152602080860151818301526040808701519083015290835260609094019391909101906117f5565b5f6020828403121561184f575f5ffd5b81516001600160401b03811115611864575f5ffd5b8201601f81018413611874575f5ffd5b8051611882610e3182610ddd565b8082825260208201915060208360061b8501019250868311156118a3575f5ffd5b6020840193505b8284101561153757604084880312156118c1575f5ffd5b6118c9610d8b565b84516118d481610f70565b815260208501516118e4816111ae565b80602083015250808352506020820191506040840193506118aa56fea264697066735822122026460b0cf8f5e17c58e4083c1b1155431c8d2cb9962cd9d5f6105ce473df73ee64736f6c63430008230033"; + +export const STAKE_WRAP_ABI = [ + { + inputs: [], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "removeStake", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "stake", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "hotkey", + type: "bytes32", + }, + { + internalType: "uint256", + name: "netuid", + type: "uint256", + }, + { + internalType: "uint256", + name: "limitPrice", + type: "uint256", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "bool", + name: "allowPartial", + type: "bool", + }, + ], + name: "stakeLimit", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + stateMutability: "payable", + type: "receive", + }, +]; + +export const STAKE_WRAP_BYTECODE = + "6080604052348015600e575f5ffd5b50335f5f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610ad08061005b5f395ff3fe608060405260043610610042575f3560e01c80632daedd521461004d5780637d691e30146100755780638da5cb5b1461009d57806390b9d534146100c757610049565b3661004957005b5f5ffd5b348015610058575f5ffd5b50610073600480360381019061006e91906106bd565b6100ef565b005b348015610080575f5ffd5b5061009b600480360381019061009691906106bd565b6102ad565b005b3480156100a8575f5ffd5b506100b161046b565b6040516100be919061074c565b60405180910390f35b3480156100d2575f5ffd5b506100ed60048036038101906100e8919061079a565b61048f565b005b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461017d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161017490610891565b60405180910390fd5b5f631fc9b14160e01b84838560405160240161019b939291906108cd565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516102239190610954565b5f604051808303815f8787f1925050503d805f811461025d576040519150601f19603f3d011682016040523d82523d5f602084013e610262565b606091505b50509050806102a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161029d906109b4565b60405180910390fd5b5050505050565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461033b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161033290610891565b60405180910390fd5b5f637d691e3060e01b848385604051602401610359939291906108cd565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516103e19190610954565b5f604051808303815f8787f1925050503d805f811461041b576040519150601f19603f3d011682016040523d82523d5f602084013e610420565b606091505b5050905080610464576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045b906109b4565b60405180910390fd5b5050505050565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f5f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461051d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161051490610891565b60405180910390fd5b5f635beb6b7460e01b868486858960405160240161053f9594939291906109e1565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090505f61080573ffffffffffffffffffffffffffffffffffffffff165a836040516105c79190610954565b5f604051808303815f8787f1925050503d805f8114610601576040519150601f19603f3d011682016040523d82523d5f602084013e610606565b606091505b505090508061064a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064190610a7c565b60405180910390fd5b50505050505050565b5f5ffd5b5f819050919050565b61066981610657565b8114610673575f5ffd5b50565b5f8135905061068481610660565b92915050565b5f819050919050565b61069c8161068a565b81146106a6575f5ffd5b50565b5f813590506106b781610693565b92915050565b5f5f5f606084860312156106d4576106d3610653565b5b5f6106e186828701610676565b93505060206106f2868287016106a9565b9250506040610703868287016106a9565b9150509250925092565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6107368261070d565b9050919050565b6107468161072c565b82525050565b5f60208201905061075f5f83018461073d565b92915050565b5f8115159050919050565b61077981610765565b8114610783575f5ffd5b50565b5f8135905061079481610770565b92915050565b5f5f5f5f5f60a086880312156107b3576107b2610653565b5b5f6107c088828901610676565b95505060206107d1888289016106a9565b94505060406107e2888289016106a9565b93505060606107f3888289016106a9565b925050608061080488828901610786565b9150509295509295909350565b5f82825260208201905092915050565b7f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f5f8201527f6e00000000000000000000000000000000000000000000000000000000000000602082015250565b5f61087b602183610811565b915061088682610821565b604082019050919050565b5f6020820190508181035f8301526108a88161086f565b9050919050565b6108b881610657565b82525050565b6108c78161068a565b82525050565b5f6060820190506108e05f8301866108af565b6108ed60208301856108be565b6108fa60408301846108be565b949350505050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f61092e82610902565b610938818561090c565b9350610948818560208601610916565b80840191505092915050565b5f61095f8284610924565b915081905092915050565b7f6164645374616b652063616c6c206661696c65640000000000000000000000005f82015250565b5f61099e601483610811565b91506109a98261096a565b602082019050919050565b5f6020820190508181035f8301526109cb81610992565b9050919050565b6109db81610765565b82525050565b5f60a0820190506109f45f8301886108af565b610a0160208301876108be565b610a0e60408301866108be565b610a1b60608301856109d2565b610a2860808301846108be565b9695505050505050565b7f6164645374616b654c696d69742063616c6c206661696c6564000000000000005f82015250565b5f610a66601983610811565b9150610a7182610a32565b602082019050919050565b5f6020820190508181035f830152610a9381610a5a565b905091905056fea2646970667358221220f8ad692d7919fb10f08e5311c64a0aa705a4e665689967633c2ddade5398076664736f6c634300081e0033"; + +export const PRECOMPILE_GAS_CONTRACT_ABI = [ + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "string", + name: "message", + type: "string", + }, + ], + name: "Log", + type: "event", + }, + { + inputs: [ + { + internalType: "uint64", + name: "iterations", + type: "uint64", + }, + ], + name: "callED25519", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint64", + name: "iterations", + type: "uint64", + }, + ], + name: "callSR25519", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +export const PRECOMPILE_GAS_CONTRACT_BYTECODE = + "60806040527f1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef5f1b5f555f5f1b6001555f5f1b6002555f5f1b6003553480156045575f5ffd5b5061048b806100535f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806356554a5714610038578063bd9cac2b14610054575b5f5ffd5b610052600480360381019061004d919061028f565b610070565b005b61006e6004803603810190610069919061028f565b61015f565b005b5f5f90505b8167ffffffffffffffff168167ffffffffffffffff1610156101265761040373ffffffffffffffffffffffffffffffffffffffff1663869adcb95f546001546002546003546040518563ffffffff1660e01b81526004016100d994939291906102d2565b602060405180830381865afa1580156100f4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610118919061034a565b508080600101915050610075565b507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab604051610154906103cf565b60405180910390a150565b5f5f90505b8167ffffffffffffffff168167ffffffffffffffff1610156102155761040273ffffffffffffffffffffffffffffffffffffffff1663869adcb95f546001546002546003546040518563ffffffff1660e01b81526004016101c894939291906102d2565b602060405180830381865afa1580156101e3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610207919061034a565b508080600101915050610164565b507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab60405161024390610437565b60405180910390a150565b5f5ffd5b5f67ffffffffffffffff82169050919050565b61026e81610252565b8114610278575f5ffd5b50565b5f8135905061028981610265565b92915050565b5f602082840312156102a4576102a361024e565b5b5f6102b18482850161027b565b91505092915050565b5f819050919050565b6102cc816102ba565b82525050565b5f6080820190506102e55f8301876102c3565b6102f260208301866102c3565b6102ff60408301856102c3565b61030c60608301846102c3565b95945050505050565b5f8115159050919050565b61032981610315565b8114610333575f5ffd5b50565b5f8151905061034481610320565b92915050565b5f6020828403121561035f5761035e61024e565b5b5f61036c84828501610336565b91505092915050565b5f82825260208201905092915050565b7f63616c6c535232353531390000000000000000000000000000000000000000005f82015250565b5f6103b9600b83610375565b91506103c482610385565b602082019050919050565b5f6020820190508181035f8301526103e6816103ad565b9050919050565b7f63616c6c454432353531390000000000000000000000000000000000000000005f82015250565b5f610421600b83610375565b915061042c826103ed565b602082019050919050565b5f6020820190508181035f83015261044e81610415565b905091905056fea26469706673582212202addcdae9c59ee78157cddedc3148678edf455132bdfc62347f85e7c660b4d2164736f6c634300081e0033"; diff --git a/ts-tests/utils/evm.ts b/ts-tests/utils/evm.ts new file mode 100644 index 0000000000..c4f35c1fba --- /dev/null +++ b/ts-tests/utils/evm.ts @@ -0,0 +1,44 @@ +import { subtensor } from "@polkadot-api/descriptors"; +import { Keyring } from "@polkadot/keyring"; +import { ethers } from "ethers"; +import type { TypedApi } from "polkadot-api"; +import { waitForTransactionWithRetry } from "./transactions.js"; + +export async function disableWhiteListCheck(api: TypedApi, disabled: boolean): Promise { + const value = await api.query.EVM.DisableWhitelistCheck.getValue(); + if (value === disabled) { + return; + } + + const alice = new Keyring({ type: "sr25519" }).addFromUri("//Alice"); + const internalCall = api.tx.EVM.disable_whitelist({ disabled }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "disable_whitelist", 5); +} + +export function createEthersWallet(provider: ethers.JsonRpcProvider): ethers.Wallet { + const account = ethers.Wallet.createRandom(); + return new ethers.Wallet(account.privateKey, provider); +} + +export async function getEthBalance(provider: ethers.Provider, address: string): Promise { + return provider.getBalance(address); +} + +/** Read chain ID via RPC without ethers' cached-network checks. */ +export async function getEthChainId(provider: ethers.JsonRpcProvider): Promise { + const chainId = await provider.send("eth_chainId", []); + return BigInt(chainId); +} + +export async function forceSetChainID(api: TypedApi, chainId: bigint): Promise { + const value = await api.query.EVMChainId.ChainId.getValue(); + if (value === chainId) { + return; + } + + const alice = new Keyring({ type: "sr25519" }).addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_evm_chain_id({ chain_id: chainId }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_evm_chain_id", 5); +} diff --git a/ts-tests/utils/index.ts b/ts-tests/utils/index.ts index b3aa36d528..8cbfbf9ce4 100644 --- a/ts-tests/utils/index.ts +++ b/ts-tests/utils/index.ts @@ -1,7 +1,13 @@ -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"; +export * from "./address.ts"; +export * from "./admin_utils.ts"; +export * from "./balance.js"; export * from "./coldkey_swap.ts"; +export * from "./evm-config.ts"; +export * from "./evm.ts"; +export * from "./shield_helpers.ts"; +export * from "./staking.js"; +export * from "./subnet.js"; +export * from "./subtensor.ts"; +export * from "./transactions.js"; +export * from "./wasm-contract.ts"; diff --git a/ts-tests/utils/limit-orders.ts b/ts-tests/utils/limit-orders.ts new file mode 100644 index 0000000000..0ffbe177e0 --- /dev/null +++ b/ts-tests/utils/limit-orders.ts @@ -0,0 +1,267 @@ +import type { KeyringPair } from "@moonwall/util"; +import type { TypedApi } from "polkadot-api"; +import type { subtensor } from "@polkadot-api/descriptors"; +import { Keyring } from "@polkadot/keyring"; +import { u8aToHex } from "@polkadot/util"; +import { blake2AsHex } from "@polkadot/util-crypto"; +import { waitForTransactionWithRetry } from "./transactions.js"; +import { MultiAddress } from "@polkadot-api/descriptors"; + +// ── Types ───────────────────────────────────────────────────────────────────── + +export type OrderType = "LimitBuy" | "TakeProfit" | "StopLoss"; + +export interface OrderParams { + signer: KeyringPair; + hotkey: string; + netuid: number; + orderType: OrderType; + amount: bigint; + limitPrice: bigint; + expiry: bigint; + feeRate: number; // Perbill (parts per billion), e.g. 10_000_000 = 1% + feeRecipient: string; + chainId?: bigint; // defaults to 42n (the dev node's EVM chain ID) + relayer?: string[] | null; // Optional: if set, only these accounts may relay the order + maxSlippage?: number | null; // Optional: Perbill (ppb). When set, effective swap limit = limit_price ± limit_price * maxSlippage / 1e9 + partialFillsEnabled?: boolean; // Optional: if true, order can be partially filled (requires relayer) +} + +export interface Order { + signer: string; + hotkey: string; + netuid: number; + order_type: OrderType; + amount: bigint; + limit_price: bigint; + expiry: bigint; + fee_rate: number; + fee_recipient: string; + relayer: string[] | null; + max_slippage: number | null; + chain_id: bigint; + partial_fills_enabled: boolean; +} + +export interface VersionedOrder { + V1: Order; +} + +export interface SignedOrder { + order: VersionedOrder; + signature: { Sr25519: `0x${string}` } | { Ed25519: `0x${string}` } | { Ecdsa: `0x${string}` }; + partial_fill: number | null; +} + +// ── Constants ───────────────────────────────────────────────────────────────── + +export const PERBILL_ONE_PERCENT = 10_000_000; +export const FAR_FUTURE = BigInt("18446744073709551615"); // u64::MAX +export const EXPIRED = BigInt(1); // 1ms — always in the past + +// ── Order building & signing ────────────────────────────────────────────────── + +/** + * Build a SignedOrder ready for submission to execute_orders / + * execute_batched_orders. The Order struct is SCALE-encoded via the + * polkadot.js registry and then signed with the signer's sr25519 key. + */ +export function buildSignedOrder(api: any, params: OrderParams): SignedOrder { + const inner: Order = { + signer: params.signer.address, + hotkey: params.hotkey, + netuid: params.netuid, + order_type: params.orderType, + amount: params.amount, + limit_price: params.limitPrice, + expiry: params.expiry, + fee_rate: params.feeRate, + fee_recipient: params.feeRecipient, + relayer: params.relayer ?? null, + max_slippage: params.maxSlippage ?? null, + chain_id: params.chainId ?? 42n, + partial_fills_enabled: params.partialFillsEnabled ?? false, + }; + + const versionedOrder: VersionedOrder = { V1: inner }; + + // SCALE-encode the VersionedOrder so the signature covers the version tag. + const encoded = api.registry.createType("LimitVersionedOrder", versionedOrder); + const sig = params.signer.sign(encoded.toU8a()); + + return { + order: versionedOrder, + signature: { Sr25519: u8aToHex(sig) as `0x${string}` }, + partial_fill: null, + }; +} + +/** + * Compute the on-chain OrderId (blake2_256 of SCALE-encoded VersionedOrder). + * Mirrors `Pallet::derive_order_id` in Rust. + */ +export function orderId(api: any, order: VersionedOrder): `0x${string}` { + const encoded = api.registry.createType("LimitVersionedOrder", order); + return blake2AsHex(encoded.toU8a(), 256) as `0x${string}`; +} + +// ── Registry ────────────────────────────────────────────────────────────────── + +/** + * Register the custom SCALE types used by pallet-limit-orders with the + * polkadot.js ApiPromise registry. Call this once after obtaining the api. + */ +export function registerLimitOrderTypes(api: any): void { + api.registry.register({ + LimitOrderType: { + _enum: ["LimitBuy", "TakeProfit", "StopLoss"], + }, + LimitOrder: { + signer: "AccountId", + hotkey: "AccountId", + netuid: "u16", + order_type: "LimitOrderType", + amount: "u64", + limit_price: "u64", + expiry: "u64", + fee_rate: "u32", // Perbill + fee_recipient: "AccountId", + relayer: "Option>", + max_slippage: "Option", + chain_id: "u64", + partial_fills_enabled: "bool", + }, + LimitVersionedOrder: { + _enum: { + V1: "LimitOrder", + }, + }, + LimitSignedOrder: { + order: "LimitVersionedOrder", + signature: "MultiSignature", + partial_fill: "Option", + }, + LimitOrderStatus: { + _enum: { + Fulfilled: null, + PartiallyFilled: "u64", + Cancelled: null, + }, + }, + }); +} + +// ── Chain helpers ───────────────────────────────────────────────────────────── + +/** Read current SubnetTAO and SubnetAlphaIn to derive spot price (TAO per alpha). */ +export async function getAlphaPrice(api: TypedApi, netuid: number): Promise { + const taoReserve = await api.query.SubtensorModule.SubnetTAO.getValue(netuid); + const alphaIn = await api.query.SubtensorModule.SubnetAlphaIn.getValue(netuid); + if (alphaIn === 0n) return 0n; + return taoReserve / alphaIn; // integer approximation +} + +/** Enable the subtoken for a subnet (required for swaps to work). */ +export async function enableSubtoken(api: TypedApi, netuid: number): Promise { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const internalCall = api.tx.AdminUtils.sudo_set_subtoken_enabled({ + netuid, + subtoken_enabled: true, + }); + const tx = api.tx.Sudo.sudo({ call: internalCall.decodedCall }); + await waitForTransactionWithRetry(api, tx, alice, "sudo_set_subtoken_enabled"); +} + +/** Sudo-enable or disable the limit-orders pallet. */ +export async function setPalletStatus(api: TypedApi, enabled: boolean): Promise { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const tx = api.tx.Sudo.sudo({ + call: api.tx.LimitOrders.set_pallet_status({ enabled }).decodedCall, + }); + await waitForTransactionWithRetry(api, tx, alice, "set_pallet_status"); +} + +/** Read the on-chain OrderStatus for a given order id (hex). */ +export async function getOrderStatus( + polkadotJs: any, + id: `0x${string}` +): Promise<"Fulfilled" | "PartiallyFilled" | "Cancelled" | undefined> { + const result = await polkadotJs.query.limitOrders.orders(id); + if (result.isNone) return undefined; + return result.unwrap().type as "Fulfilled" | "PartiallyFilled" | "Cancelled"; +} + +/** Read the on-chain OrderStatus and return the PartiallyFilled amount, or null. */ +export async function getPartiallyFilledAmount(polkadotJs: any, id: `0x${string}`): Promise { + const result = await polkadotJs.query.limitOrders.orders(id); + if (result.isNone) return null; + const status = result.unwrap(); + if (status.type !== "PartiallyFilled") return null; + return BigInt(status.asPartiallyFilled.toString()); +} + +/** Filter system events by method name. */ +export function filterEvents(events: any, method: string): any[] { + return (events as any[]).filter((e: any) => e.event.method === method); +} + +/** Read the EVM chain ID from pallet_evm_chain_id storage. */ +export async function fetchChainId(api: any): Promise { + const result = await api.query.evmChainId.chainId(); + return BigInt(result.toString()); +} + +/** + * Compute the expected `net_amount` field of `GroupExecutionSummary` for a + * mixed buy/sell batch, mirroring the pallet's netting logic. + * + * The runtime API returns `floor(price_actual * 1e9)` as a u64, so our + * bigint replication differs from the on-chain U96F32 result by at most a + * few RAO — use `toBeCloseTo` or a small tolerance window when asserting. + * + * @param polkadotJs polkadot-js ApiPromise + * @param netuid subnet id + * @param buySideTao total net TAO from buy orders (after fees, in RAO) + * @param sellSideAlpha total net alpha from sell orders (in RAO) + * @param side which side dominates ("Buy" | "Sell") + */ +export async function computeNetAmount( + polkadotJs: any, + netuid: number, + buySideTao: bigint, + sellSideAlpha: bigint, + side: "Buy" | "Sell" +): Promise { + // price_scaled = floor(price_actual * 1e9) [RAO per alpha * 1e9 / 1e9 = dimensionless] + const priceRaw = await polkadotJs.call.swapRuntimeApi.currentAlphaPrice(netuid); + const price = BigInt(priceRaw.toString()); + const SCALE = 1_000_000_000n; + + if (side === "Buy") { + // net_amount (TAO) = buy_tao - alpha_to_tao(sell_alpha, price) + // alpha_to_tao ≈ floor(price * sell_alpha / 1e9) + const sellTaoEquiv = (price * sellSideAlpha) / SCALE; + return buySideTao - sellTaoEquiv; + } else { + // net_amount (alpha) = sell_alpha - tao_to_alpha(buy_tao, price) + // tao_to_alpha ≈ floor(buy_tao * 1e9 / price) + const buyAlphaEquiv = (buySideTao * SCALE) / price; + return sellSideAlpha - buyAlphaEquiv; + } +} + +export async function executeBatchedOrders( + api: TypedApi, + netuid: number, + orders: SignedOrder[] +): Promise { + const keyring = new Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + const tx = api.tx.LimitOrders.execute_batched_orders({ + netuid, + orders, + }); + await waitForTransactionWithRetry(api, tx, alice, "execute_batched_orders"); +} diff --git a/ts-tests/utils/shield_helpers.ts b/ts-tests/utils/shield_helpers.ts index 24656b1208..a437e6591c 100644 --- a/ts-tests/utils/shield_helpers.ts +++ b/ts-tests/utils/shield_helpers.ts @@ -1,13 +1,26 @@ import type { KeyringPair } from "@moonwall/util"; +import { xchacha20poly1305 } from "@noble/ciphers/chacha.js"; +import type { subtensor } from "@polkadot-api/descriptors"; +import { hexToU8a } from "@polkadot/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"; + +const keyToBytes = (key: unknown): Uint8Array => { + if (key instanceof Uint8Array) { + return key; + } + if (typeof key === "object" && key !== null && "asBytes" in key) { + return (key as Binary).asBytes(); + } + if (typeof key === "string") { + return hexToU8a(key); + } + throw new Error(`Unexpected MEV shield key type: ${typeof key}`); +}; export const getNextKey = async (api: TypedApi): Promise => { // Query at "best" (not default "finalized") because keys rotate every block @@ -16,8 +29,7 @@ export const getNextKey = async (api: TypedApi): Promise) => { @@ -41,8 +53,7 @@ export const checkRuntime = async (api: TypedApi) => { export const getCurrentKey = async (api: TypedApi): Promise => { 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); + return keyToBytes(key); }; export const encryptTransaction = async (plaintext: Uint8Array, publicKey: Uint8Array): Promise => { diff --git a/ts-tests/utils/staking.ts b/ts-tests/utils/staking.ts index 4efdc19802..79c432b17b 100644 --- a/ts-tests/utils/staking.ts +++ b/ts-tests/utils/staking.ts @@ -1,8 +1,8 @@ -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"; +import type { TypedApi } from "polkadot-api"; +import { waitForTransactionWithRetry } from "./transactions.js"; export async function addStake( api: TypedApi, @@ -437,3 +437,13 @@ export async function getTotalHotkeyAlpha( ): Promise { return await api.query.SubtensorModule.TotalHotkeyAlpha.getValue(hotkey, netuid); } + +export async function getStakeInfoForHotkeyColdkeyNetuid( + api: TypedApi, + hotkey: string, + coldkey: string, + netuid: number +): Promise { + return (await api.apis.StakeInfoRuntimeApi.get_stake_info_for_hotkey_coldkey_netuid(hotkey, coldkey, netuid)) + ?.stake; +} diff --git a/ts-tests/utils/subnet.ts b/ts-tests/utils/subnet.ts index b45f4a7934..5f9d9e8a33 100644 --- a/ts-tests/utils/subnet.ts +++ b/ts-tests/utils/subnet.ts @@ -1,9 +1,9 @@ -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 { Keyring } from "@polkadot/keyring"; import type { TypedApi } from "polkadot-api"; +import { log } from "./logger.js"; +import { waitForTransactionWithRetry } from "./transactions.js"; export async function addNewSubnetwork( api: TypedApi, @@ -51,8 +51,14 @@ export async function startCall(api: TypedApi, netuid: number, 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); + const deadline = Date.now() + 120_000; while (currentBlock - registerBlock <= duration) { + if (Date.now() > deadline) { + throw new Error( + `startCall timed out for netuid ${netuid} (registerBlock=${registerBlock}, currentBlock=${currentBlock}, duration=${duration})` + ); + } await new Promise((resolve) => setTimeout(resolve, 2000)); currentBlock = await api.query.System.Number.getValue(); } diff --git a/ts-tests/utils/subtensor.ts b/ts-tests/utils/subtensor.ts new file mode 100644 index 0000000000..f6090a67ca --- /dev/null +++ b/ts-tests/utils/subtensor.ts @@ -0,0 +1,18 @@ +import { subtensor } from "@polkadot-api/descriptors"; +import type { TypedApi } from "polkadot-api"; + +export async function getProxies(api: TypedApi, address: string): Promise { + const entries = await api.query.Proxy.Proxies.getEntries(); + const result: string[] = []; + for (const entry of entries) { + const proxyAddress = entry.keyArgs[0]; + const values = entry.value; + const proxies = values[0]; + for (const proxy of proxies) { + if (proxy.delegate === address) { + result.push(proxyAddress); + } + } + } + return result; +} diff --git a/ts-tests/utils/transactions.ts b/ts-tests/utils/transactions.ts index f64c772f79..53b9185828 100644 --- a/ts-tests/utils/transactions.ts +++ b/ts-tests/utils/transactions.ts @@ -1,10 +1,10 @@ -import { log } from "./logger.js"; import type { KeyringPair } from "@moonwall/util"; +import type { subtensor } from "@polkadot-api/descriptors"; 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"; +import { log } from "./logger.js"; +import { waitForBlocks } from "./staking.ts"; export async function waitForTransactionWithRetry( api: TypedApi, diff --git a/ts-tests/utils/wasm-contract.ts b/ts-tests/utils/wasm-contract.ts new file mode 100644 index 0000000000..b4f3f6650f --- /dev/null +++ b/ts-tests/utils/wasm-contract.ts @@ -0,0 +1,82 @@ +import { MultiAddress, subtensor } from "@polkadot-api/descriptors"; +import type { KeyringPair } from "@polkadot/keyring/types"; +import type { TypedApi } from "polkadot-api"; +import { Binary } from "polkadot-api"; +import { convertPublicKeyToSs58 } from "./address.ts"; +import { getBalance } from "./balance.ts"; +import { sendTransaction, waitForFinalizedBlocks, waitForTransactionWithRetry } from "./transactions.ts"; + +export const BITTENSOR_WASM_PATH = "./ink/bittensor.wasm"; + +export async function sendWasmContractExtrinsic( + api: TypedApi, + coldkey: KeyringPair, + contractAddress: string, + data: { asBytes(): Uint8Array } +): Promise { + const tx = api.tx.Contracts.call({ + value: BigInt(0), + dest: MultiAddress.Id(contractAddress), + data: Binary.fromBytes(data.asBytes()), + gas_limit: { + ref_time: BigInt(10_000_000_000), + proof_size: BigInt(10_000_000), + }, + storage_deposit_limit: BigInt(1_000_000_000), + }); + await waitForTransactionWithRetry(api, tx, coldkey, "contracts_call", 1); + await waitForFinalizedBlocks(api, 1); +} + +/** Submit a contract call without failing when the contract reverts (expected for atomic-failure tests). */ +export async function sendWasmContractExtrinsicAllowFailure( + api: TypedApi, + coldkey: KeyringPair, + contractAddress: string, + data: { asBytes(): Uint8Array } +): Promise { + const tx = api.tx.Contracts.call({ + value: BigInt(0), + dest: MultiAddress.Id(contractAddress), + data: Binary.fromBytes(data.asBytes()), + gas_limit: { + ref_time: BigInt(10_000_000_000), + proof_size: BigInt(10_000_000), + }, + storage_deposit_limit: BigInt(1_000_000_000), + }); + await sendTransaction(tx, coldkey); +} + +export async function instantiateWasmContract( + api: TypedApi, + coldkey: KeyringPair, + wasmBytecode: Uint8Array, + constructorData: { asBytes(): Uint8Array } +): Promise { + const tx = api.tx.Contracts.instantiate_with_code({ + code: Binary.fromBytes(wasmBytecode), + storage_deposit_limit: BigInt(10_000_000), + value: BigInt(0), + gas_limit: { + ref_time: BigInt(1_000_000_000), + proof_size: BigInt(1_000_000), + }, + data: Binary.fromBytes(constructorData.asBytes()), + salt: Binary.fromHex("0x"), + }); + + const result = await sendTransaction(tx, coldkey); + if (!result.success) { + throw new Error(`instantiate_with_code failed: ${result.errorMessage ?? "unknown error"}`); + } + + const instantiatedEvents = await api.event.Contracts.Instantiated.filter(result.events); + if (instantiatedEvents.length === 0) { + throw new Error("No Contracts.Instantiated events found after instantiate_with_code"); + } + + return instantiatedEvents[0].contract; +} + +export { convertPublicKeyToSs58, getBalance };